Package: libmms Version: 0.2-7 Severity: wishlist Tags: patch The patch will add support for byte offset seeking and time offset seeking, and some other APIs as follows, (Note: This patch is licensed under LGPL)
int mms_request_time_seek (mms_io_t *io, mms_t *instance, double time_sec); int mms_time_seek (mms_io_t *io, mms_t *instance, double time_sec); int mms_request_packet_seek (mms_io_t *io, mms_t *instance, unsigned long packet_seq); /* * mms_seek() will try to seek using mms_request_packet_seek(), if the server * ignore the packet seek command, it will return unchanged current_pos, rather * than trying to mms_read() until the destination pos is reached. This is to * let the caller, by itself, to decide to choose the alternate method, such * as, mms_time_seek() and/or mms_read() until the destination pos is reached. * One can do binary search using time offset (mms_time_seek()) as a search * index, to approach the desired byte offset. It is to systematically guess * the time offset to reach for the byte offset. * * Known BUG: Application must be compiled with 64-bit file offset * (-D_FILE_OFFSET_BITS=64) to use this function correctly. */ off_t mms_seek (mms_io_t *io, mms_t *instance, off_t offset, int origin); /* return total playback time in seconds */ double mms_get_time_length (mms_t *instance); /* return raw total playback time in 100 nanosecs (10^-7) */ uint64_t mms_get_raw_time_length (mms_t *instance); __________________________________________________ คุณใช้ Yahoo! รึเปล่า คุณเบื่อหน่ายอีเมลขยะใช่ไหม Yahoo! เมล มีการป้องกันอีเมลขยะที่ดีที่สุด http://th.mail.yahoo.com
diff -urN --exclude={diff} libmms-0.2-7/src/mms.c libmms-0.2-7.develop/src/mms.c --- libmms-0.2-7/src/mms.c 2006-09-13 14:40:47.665865104 +0700 +++ libmms-0.2-7.develop/src/mms.c 2006-09-10 15:44:55.000000000 +0700 @@ -134,6 +134,8 @@ uint8_t buf[BUF_SIZE]; int buf_size; int buf_read; + off_t buf_packet_seq_offset; /* packet sequence offset residing in + buf */ uint8_t asf_header[ASF_HEADER_LEN]; uint32_t asf_header_len; @@ -142,10 +144,14 @@ int num_stream_ids; int stream_ids[ASF_MAX_NUM_STREAMS]; int stream_types[ASF_MAX_NUM_STREAMS]; + uint8_t packet_id_type; off_t start_packet_seq; /* for live streams != 0, need to keep it around */ int need_discont; /* whether we need to set start_packet_seq */ uint32_t asf_packet_len; uint64_t file_len; + uint64_t time_len; /* playback time in 100 nanosecs (10^-7) */ + uint64_t preroll; + uint64_t asf_num_packets; char guid[37]; uint32_t bitrates[ASF_MAX_NUM_STREAMS]; uint32_t bitrates_pos[ASF_MAX_NUM_STREAMS]; @@ -528,6 +534,7 @@ header->flags = 0; header->packet_id_type = 0; len = io_read(io, this->s, this->buf, 8); + this->buf_packet_seq_offset = -1; if (len != 8) goto error; @@ -578,6 +585,7 @@ lprintf("packet_len: %d bytes\n", packet_len); len = io_read(io, this->s, this->buf + 12, packet_len) ; + //this->buf_packet_seq_offset = -1; // already set in get_packet_header if (len != packet_len) { return 0; } @@ -736,6 +744,9 @@ break; } this->file_len = LE_64(this->asf_header + i + 40 - 24); + this->time_len = LE_64(this->asf_header + i + 64 - 24); + //this->time_len = LE_64(this->asf_header + i + 72 - 24); + this->preroll = LE_64(this->asf_header + i + 80 - 24); lprintf ("file object, packet length = %d (%d)\n", this->asf_packet_len, LE_32(this->asf_header + i + 96 - 24)); break; @@ -806,6 +817,10 @@ } break; + case GUID_ASF_DATA: + this->asf_num_packets = LE_64(this->asf_header + i + 40 - 24); + break; + default: lprintf ("unknown object\n"); break; @@ -1055,6 +1070,7 @@ this->need_discont = 1; this->buf_size = 0; this->buf_read = 0; + this->buf_packet_seq_offset = -1; this->has_audio = 0; this->has_video = 0; this->bandwidth = bandwidth; @@ -1244,6 +1260,7 @@ /* report_progress (stream, 80); */ /* command 0x07 */ + this->packet_id_type = ASF_MEDIA_PACKET_ID_TYPE; { mms_buffer_t command_buffer; mms_buffer_init(&command_buffer, this->scmd_body); @@ -1255,7 +1272,7 @@ mms_buffer_put_8 (&command_buffer, 0xFF); mms_buffer_put_8 (&command_buffer, 0xFF); mms_buffer_put_8 (&command_buffer, 0x00); /* stream time limit flag */ - mms_buffer_put_32 (&command_buffer, ASF_MEDIA_PACKET_ID_TYPE); /* asf media packet id type */ + mms_buffer_put_32 (&command_buffer, this->packet_id_type); /* asf media packet id type */ if (!send_command (io, this, 0x07, 1, 0x0001FFFF, command_buffer.pos)) { /* FIXME: de-xine-ification */ lprintf ( "***LOG:*** -- " @@ -1406,7 +1423,8 @@ /* FIXME: probably needs some more sophisticated logic, but until we do seeking, this should work */ - if(this->need_discont) + if(this->need_discont && + header.packet_id_type == ASF_MEDIA_PACKET_ID_TYPE) { this->need_discont = 0; this->start_packet_seq = header.packet_seq; @@ -1443,11 +1461,22 @@ (start < end) && (end < (base+BUF_SIZE-1))) { memset(this->buf + header.packet_len, 0, this->asf_packet_len - header.packet_len); } + if (header.packet_id_type == this->packet_id_type) { if (this->asf_packet_len > BUF_SIZE) { this->buf_size = BUF_SIZE; } else { this->buf_size = this->asf_packet_len; } + this->buf_packet_seq_offset = + header.packet_seq - this->start_packet_seq; + } else { + this->buf_size = 0; + // Don't set this packet sequence for reuse in seek(), since the + // subsequence packet may be discontinued. + //this->buf_packet_seq_offset = header.packet_seq; + // already set to -1 in get_packet_header + //this->buf_packet_seq_offset = -1; + } } } break; @@ -1521,6 +1550,255 @@ return total; } +// To be inline function? +static int mms_request_data_packet (mms_io_t *io, mms_t *this, + double time_sec, unsigned long first_packet, unsigned long time_msec_limit) { + /* command 0x07 */ + { + mms_buffer_t command_buffer; + //mms_buffer_init(&command_buffer, this->scmd_body); + //mms_buffer_put_32 (&command_buffer, 0x00000000); /* 64 byte float timestamp */ + //mms_buffer_put_32 (&command_buffer, 0x00000000); + memcpy(this->scmd_body, &time_sec, 8); + mms_buffer_init(&command_buffer, this->scmd_body+8); + mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* ?? */ + //mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* first packet sequence */ + mms_buffer_put_32 (&command_buffer, first_packet); /* first packet sequence */ + //mms_buffer_put_8 (&command_buffer, 0xFF); /* max stream time limit (3 bytes) */ + //mms_buffer_put_8 (&command_buffer, 0xFF); + //mms_buffer_put_8 (&command_buffer, 0xFF); + //mms_buffer_put_8 (&command_buffer, 0x00); /* stream time limit flag */ + mms_buffer_put_32 (&command_buffer, time_msec_limit & 0x00FFFFFF);/* max stream time limit (3 bytes) */ + mms_buffer_put_32 (&command_buffer, this->packet_id_type); /* asf media packet id type */ + if (!send_command (io, this, 0x07, 1, 0x0001FFFF, 8+command_buffer.pos)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: failed to send command 0x07\n"); + return 0; + } + } + /* TODO: adjust current_pos, considering asf_header_read */ + return 1; +} + +int mms_request_time_seek (mms_io_t *io, mms_t *this, double time_sec) { + if (++this->packet_id_type <= ASF_MEDIA_PACKET_ID_TYPE) + this->packet_id_type = ASF_MEDIA_PACKET_ID_TYPE+1; + //return mms_request_data_packet (io, this, time_sec, 0xFFFFFFFF, 0x00FFFFFF); + // also adjust time by preroll + return mms_request_data_packet (io, this, + time_sec+(double)(this->preroll)/1000, + 0xFFFFFFFF, 0x00FFFFFF); +} + +// set current_pos to the first byte of the requested packet by peeking at +// the first packet. +// To be inline function? +static int peek_and_set_pos (mms_io_t *io, mms_t *this) { + uint8_t saved_buf[BUF_SIZE]; + int saved_buf_size; + off_t saved_buf_packet_seq_offset; + // save buf and buf_size that may be changed in get_media_packet() + memcpy(saved_buf, this->buf, this->buf_size); + saved_buf_size = this->buf_size; + saved_buf_packet_seq_offset = this->buf_packet_seq_offset; + //this->buf_size = this->buf_read = 0; // reset buf, only if success peeking + this->buf_size = 0; + while (!this->eos) { + // get_media_packet() will set current_pos if data packet is read. + if (!get_media_packet (io, this)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: get_media_packet failed\n"); + // restore buf and buf_size that may be changed in get_media_packet() + memcpy(this->buf, saved_buf, saved_buf_size); + this->buf_size = saved_buf_size; + this->buf_packet_seq_offset = saved_buf_packet_seq_offset; + return 0; + } + if (this->buf_size > 0) break; + } + // flush header and reset buf_read, only if success peeking + this->asf_header_read = this->asf_header_len; + this->buf_read = 0; + return 1; + //return this->current_pos; +} + +// send time seek request, and update current_pos corresponding to the next +// requested packet +// Note that, the current_pos will always does not less than asf_header_len +int mms_time_seek (mms_io_t *io, mms_t *this, double time_sec) { + if (!mms_request_time_seek (io, this, time_sec)) return 0; + return peek_and_set_pos (io, this); +} + +// http://sdp.ppona.com/zipfiles/MMSprotocol_pdf.zip said that, this +// packet_seq value make no difference in version 9 servers. +// But from my experiment with +// mms://202.142.200.130/tltk/56k/tltkD2006-08-08ID-7209.wmv and +// mms://202.142.200.130/tltk/56k/tltkD2006-09-01ID-7467.wmv (the url may valid +// in only 2-3 months) whose server is version 9, it does response and return +// the requested packet. +int mms_request_packet_seek (mms_io_t *io, mms_t *this, + unsigned long packet_seq) { + if (++this->packet_id_type <= ASF_MEDIA_PACKET_ID_TYPE) + this->packet_id_type = ASF_MEDIA_PACKET_ID_TYPE+1; + return mms_request_data_packet (io, this, 0, packet_seq, 0x00FFFFFF); +} + +// send packet seek request, and update current_pos corresponding to the next +// requested packet +// Note that, the current_pos will always does not less than asf_header_len +// Not export this function. Let user use mms_seek() instead? +static int mms_packet_seek (mms_io_t *io, mms_t *this, + unsigned long packet_seq) { + if (!mms_request_packet_seek (io, this, packet_seq)) return 0; + return peek_and_set_pos (io, this); +} + +/* +TODO: To use this table to calculate buf_packet_seq_offset rather than store +and retrieve it from this->buf_packet_seq_offset? +current_packet_seq == (current_pos - asf_header_len) / asf_packet_len +current_packet_seq == -1 if current_pos < asf_header_len +buf_packet_seq_offset indicating which packet sequence are residing in the buf. +Possible status after read(), "last" means last value or unchange. +current_packet_seq | buf_read | buf_size | buf_packet_seq_offset +-------------------+----------------+-----------+--------------- +<= 0 | 0 (last) | 0 (last) | none +<= 0 | 0 (last) | 0 (last) | eos at #0 +<= 0 | 0 (last) | 0 (last) | eos at > #0 +<= 0 | 0 (last) | > 0 (last)| #0 +<= 0 | buf_size (last)| > 0 (last)| > #0 +> 0 | 0 | 0 | eos at current_packet_seq +> 0 | 0(never happen)| > 0 | (never happen) +> 0 | buf_size | > 0 | current_packet_seq-1 +*/ +// TODO: How to handle seek() in multi stream source? +// The stream that follows 0x20 ("new stream") command. +off_t mms_seek (mms_io_t *io, mms_t *this, off_t offset, int origin) { + off_t dest; + off_t dest_packet_seq; + //off_t buf_packet_seq_offset; + + switch (origin) { + case SEEK_SET: + dest = offset; + break; + case SEEK_CUR: + dest = this->current_pos + offset; + break; + case SEEK_END: + //if (this->asf_num_packets == 0) { + // //printf ("input_mms: unknown end position in seek!\n"); + // return this->current_pos; + //} + dest = mms_get_length (this) + offset; + default: + printf ("input_mms: unknown origin in seek!\n"); + return this->current_pos; + } + + dest_packet_seq = dest - this->asf_header_len; + //if (dest_packet_seq > 0) dest_packet_seq /= this->asf_packet_len; + dest_packet_seq = dest_packet_seq >= 0 ? + dest_packet_seq / this->asf_packet_len : -1; +#if 0 + // buf_packet_seq_offset will identify which packet sequence are residing in + // the buf. +#if 1 /* To show both of the alternate styles :D */ + buf_packet_seq_offset = this->current_pos - this->asf_header_len; + //if (buf_packet_seq_offset > 0) buf_packet_seq_offset /= this->asf_packet_len; + buf_packet_seq_offset = buf_packet_seq_offset >= 0 ? + buf_packet_seq_offset / this->asf_packet_len : -1; + // Note: buf_read == buf_size == 0 may means that it is eos, + // eos means that the packet has been peek'ed. + if (this->buf_read >= this->buf_size && this->buf_size > 0 && + buf_packet_seq_offset >= 0 || + // assuming packet not peek'ed in the following condition + /*this->buf_read >= this->buf_size && */this->buf_size == 0 && + buf_packet_seq_offset == 0) + // The buf is all read but the packet has not been peek'ed. + --buf_packet_seq_offset; +#else + buf_packet_seq_offset = this->current_pos - this->asf_header_len - 1; + //if (buf_packet_seq_offset > 0) buf_packet_seq_offset /= this->asf_packet_len; + buf_packet_seq_offset = buf_packet_seq_offset >= 0 ? + buf_packet_seq_offset / this->asf_packet_len : -1; + // Note: buf_read == buf_size == 0 may means that it is eos, + // eos means that the packet has been peek'ed. + if (this->buf_read == 0/* && buf_packet_seq_offset >= 0*/) + // Since the packet has just been peek'ed. + ++buf_packet_seq_offset; +#endif +#endif + + if (dest_packet_seq < 0) { + if (this->buf_packet_seq_offset > 0) { + if (!mms_request_packet_seek (io, this, 0xFFFFFFFF)) + return this->current_pos; +#if 1 + // clear buf + this->buf_read = this->buf_size = 0; + this->buf_packet_seq_offset = -1; + } else { +#else + // clear buf + this->buf_read = this->buf_size; + // Set this packet sequence not to be reused, since the subsequence + // packet may be discontinued. + this->buf_packet_seq_offset = -1; + // don't reset buf_read if buf_packet_seq_offset < 0, since the previous + // buf may not be cleared. + } else if (this->buf_packet_seq_offset == 0) { +#endif + // reset buf_read + this->buf_read = 0; + } + this->asf_header_read = dest; + return this->current_pos = dest; + } + // dest_packet_seq >= 0 + if (this->asf_num_packets > 0 && dest == this->asf_header_len + + this->asf_num_packets*this->asf_packet_len) { + // Requesting the packet beyond the last packet, can cause the server to + // not return any packet or any eos command. This can cause + // mms_packet_seek() to hang. + // This is to allow seeking at end of stream, and avoid hanging. + --dest_packet_seq; + } + if (dest_packet_seq != this->buf_packet_seq_offset) { + if (dest_packet_seq >= this->asf_num_packets) { + // Do not seek beyond the last packet. + return this->current_pos; + } + if (!mms_packet_seek (io, this, this->start_packet_seq + dest_packet_seq)) + return this->current_pos; + // Check if current_pos is correct. + // This can happen if the server ignore packet seek command. + // If so, just return unchanged current_pos, rather than trying to + // mms_read() to reach the destination pos. + // It should let the caller to decide to choose the alternate method, such + // as, mms_time_seek() and/or mms_read() until the destination pos is + // reached. + if (dest_packet_seq != this->buf_packet_seq_offset) + return this->current_pos; + // This has already been set in mms_packet_seek(). + //if (current_packet_seq < 0) + // this->asf_header_read = this->asf_header_len; + //this->asf_header_read = this->asf_header_len; + } + // eos is reached ? + //if (this->buf_size <= 0) return this->current_pos; + //this->buf_read = (dest - this->asf_header_len) % this->asf_packet_len; + this->buf_read = dest - + (this->asf_header_len + dest_packet_seq*this->asf_packet_len); + // will never happen ? + //if (this->buf_size <= this->buf_read) return this->current_pos; + return this->current_pos = dest; +} + void mms_close (mms_t *this) { @@ -1542,8 +1820,18 @@ free (this); } +double mms_get_time_length (mms_t *this) { + return (double)(this->time_len) / 1e7; +} + +uint64_t mms_get_raw_time_length (mms_t *this) { + return this->time_len; +} + uint32_t mms_get_length (mms_t *this) { return this->file_len; + // use this rather? + //return this->asf_header_len + this->asf_num_packets*this->asf_packet_len; } off_t mms_get_current_pos (mms_t *this) { diff -urN --exclude={diff} libmms-0.2-7/src/mms.h libmms-0.2-7.develop/src/mms.h --- libmms-0.2-7/src/mms.h 2006-09-13 14:40:47.662865560 +0700 +++ libmms-0.2-7.develop/src/mms.h 2006-09-11 13:48:15.000000000 +0700 @@ -50,6 +50,28 @@ mms_t* mms_connect (mms_io_t *io, void *data, const char *url, int bandwidth); int mms_read (mms_io_t *io, mms_t *instance, char *data, int len); +int mms_request_time_seek (mms_io_t *io, mms_t *instance, double time_sec); +int mms_time_seek (mms_io_t *io, mms_t *instance, double time_sec); +int mms_request_packet_seek (mms_io_t *io, mms_t *instance, + unsigned long packet_seq); +/* + * mms_seek() will try to seek using mms_request_packet_seek(), if the server + * ignore the packet seek command, it will return unchanged current_pos, rather + * than trying to mms_read() until the destination pos is reached. This is to + * let the caller, by itself, to decide to choose the alternate method, such + * as, mms_time_seek() and/or mms_read() until the destination pos is reached. + * One can do binary search using time offset (mms_time_seek()) as a search + * index, to approach the desired byte offset. It is to systematically guess + * the time offset to reach for the byte offset. + * + * Known BUG: Application must be compiled with 64-bit file offset + * (-D_FILE_OFFSET_BITS=64) to use this function correctly. + */ +off_t mms_seek (mms_io_t *io, mms_t *instance, off_t offset, int origin); +/* return total playback time in seconds */ +double mms_get_time_length (mms_t *instance); +/* return raw total playback time in 100 nanosecs (10^-7) */ +uint64_t mms_get_raw_time_length (mms_t *instance); uint32_t mms_get_length (mms_t *instance); void mms_close (mms_t *instance);