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);
 

Reply via email to