Bug 1064705 - Don't treat EOS as fatal when reading optional block subelements in nestegg_read_packet. r=cajbir
authorMatthew Gregan <kinetik@flim.org>
Wed, 10 Sep 2014 10:51:28 +1200
changeset 205408 170ce237e4a0
parent 205407 ed5f95178615
child 205409 7027efe7fae3
push id27492
push usernigelbabu@gmail.com
push dateTue, 16 Sep 2014 03:01:01 +0000
treeherdermozilla-central@4e8c6c5c0961 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir
bugs1064705
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1064705 - Don't treat EOS as fatal when reading optional block subelements in nestegg_read_packet. r=cajbir
content/media/mediasource/test/test_MediaSource.html
content/media/mediasource/test/test_SplitAppend.html
content/media/mediasource/test/test_SplitAppendDelay.html
media/libnestegg/README_MOZILLA
media/libnestegg/include/nestegg.h
media/libnestegg/src/nestegg.c
--- a/content/media/mediasource/test/test_MediaSource.html
+++ b/content/media/mediasource/test/test_MediaSource.html
@@ -67,18 +67,19 @@ runWithMSE(function () {
   });
 
   ms.addEventListener("sourceended", function () {
     ok(true, "Receive a sourceended event");
     is(ms.readyState, "ended", "MediaSource must be in ended state after sourceended");
   });
 
   v.addEventListener("ended", function () {
-    is(v.duration, 4, "Video has correct duration");
-    is(v.currentTime, 4, "Video has played to end");
+    // XXX: Duration should be exactly 4.0, see bug 1065207.
+    is(v.duration, 4.001, "Video has correct duration");
+    is(v.currentTime, 4.001, "Video has played to end");
     v.parentNode.removeChild(v);
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
 </body>
--- a/content/media/mediasource/test/test_SplitAppend.html
+++ b/content/media/mediasource/test/test_SplitAppend.html
@@ -27,18 +27,19 @@ runWithMSE(function (ms, v) {
           ms.endOfStream();
         }
       });
       v.play();
     });
   });
 
   v.addEventListener("ended", function () {
-    is(v.duration, 4, "Video has correct duration");
-    is(v.currentTime, 4, "Video has played to end");
+    // XXX: Duration should be exactly 4.0, see bug 1065207.
+    is(v.duration, 4.001, "Video has correct duration");
+    is(v.currentTime, 4.001, "Video has played to end");
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/mediasource/test/test_SplitAppendDelay.html
+++ b/content/media/mediasource/test/test_SplitAppendDelay.html
@@ -29,18 +29,19 @@ runWithMSE(function (ms, v) {
           ms.endOfStream();
         }
       });
       v.play();
     });
   });
 
   v.addEventListener("ended", function () {
-    is(v.duration, 4, "Video has correct duration");
-    is(v.currentTime, 4, "Video has played to end");
+    // XXX: Duration should be exactly 4.0, see bug 1065207.
+    is(v.duration, 4.001, "Video has correct duration");
+    is(v.currentTime, 4.001, "Video has played to end");
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/media/libnestegg/README_MOZILLA
+++ b/media/libnestegg/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the nestegg
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The nestegg git repository is: git://github.com/kinetiknz/nestegg.git
 
-The git commit ID used was 276ccd80e723b0edaf0ef42dbacb5a3a42e02d5c.
+The git commit ID used was 46ab96bcc8b099704cc8a15993f80fe0269a5284.
--- a/media/libnestegg/include/nestegg.h
+++ b/media/libnestegg/include/nestegg.h
@@ -128,16 +128,17 @@ typedef struct {
   unsigned int width;          /**< Width of the video frame in pixels. */
   unsigned int height;         /**< Height of the video frame in pixels. */
   unsigned int display_width;  /**< Display width of the video frame in pixels. */
   unsigned int display_height; /**< Display height of the video frame in pixels. */
   unsigned int crop_bottom;    /**< Pixels to crop from the bottom of the frame. */
   unsigned int crop_top;       /**< Pixels to crop from the top of the frame. */
   unsigned int crop_left;      /**< Pixels to crop from the left of the frame. */
   unsigned int crop_right;     /**< Pixels to crop from the right of the frame. */
+  unsigned int alpha_mode;     /**< 1 if an additional opacity stream is available, otherwise 0. */
 } nestegg_video_params;
 
 /** Parameters specific to an audio track. */
 typedef struct {
   double rate;           /**< Sampling rate in Hz. */
   unsigned int channels; /**< Number of audio channels. */
   unsigned int depth;    /**< Bits per sample. */
   uint64_t  codec_delay; /**< Nanoseconds that must be discarded from the start. */
@@ -335,16 +336,29 @@ int nestegg_packet_count(nestegg_packet 
     @param data    Storage for the queried data pointer.
                    The data is owned by the #nestegg_packet packet.
     @param length  Storage for the queried data size.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_data(nestegg_packet * packet, unsigned int item,
                         unsigned char ** data, size_t * length);
 
+/** Get a pointer to additional data with identifier @a id of additional packet
+    data. If @a id isn't present in the packet, returns -1.
+    @param packet  Packet initialized by #nestegg_read_packet.
+    @param id      Codec specific identifer. For VP8, use 1 to get a VP8 encoded
+                   frame containing an alpha channel in its Y plane.
+    @param data    Storage for the queried data pointer.
+                   The data is owned by the #nestegg_packet packet.
+    @param length  Storage for the queried data size.
+    @retval  0 Success.
+    @retval -1 Error. */
+int nestegg_packet_additional_data(nestegg_packet * packet, unsigned int id,
+                                   unsigned char ** data, size_t * length);
+
 /** Returns discard_padding for given packet
     @param packet  Packet initialized by #nestegg_read_packet.
     @param discard_padding pointer to store discard padding in.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_discard_padding(nestegg_packet * packet,
                                    int64_t * discard_padding);
 
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -42,20 +42,28 @@
 /* Cluster Elements */
 #define ID_CLUSTER              0x1f43b675
 #define ID_TIMECODE             0xe7
 #define ID_BLOCK_GROUP          0xa0
 #define ID_SIMPLE_BLOCK         0xa3
 
 /* BlockGroup Elements */
 #define ID_BLOCK                0xa1
+#define ID_BLOCK_ADDITIONS      0x75a1
 #define ID_BLOCK_DURATION       0x9b
 #define ID_REFERENCE_BLOCK      0xfb
 #define ID_DISCARD_PADDING      0x75a2
 
+/* BlockAdditions Elements */
+#define ID_BLOCK_MORE           0xa6
+
+/* BlockMore Elements */
+#define ID_BLOCK_ADD_ID         0xee
+#define ID_BLOCK_ADDITIONAL     0xa5
+
 /* Tracks Elements */
 #define ID_TRACKS               0x1654ae6b
 #define ID_TRACK_ENTRY          0xae
 #define ID_TRACK_NUMBER         0xd7
 #define ID_TRACK_UID            0x73c5
 #define ID_TRACK_TYPE           0x83
 #define ID_FLAG_ENABLED         0xb9
 #define ID_FLAG_DEFAULT         0x88
@@ -66,16 +74,17 @@
 #define ID_CODEC_PRIVATE        0x63a2
 #define ID_CODEC_DELAY          0x56aa
 #define ID_SEEK_PREROLL         0x56bb
 #define ID_DEFAULT_DURATION     0x23e383
 
 /* Video Elements */
 #define ID_VIDEO                0xe0
 #define ID_STEREO_MODE          0x53b8
+#define ID_ALPHA_MODE           0x53c0
 #define ID_PIXEL_WIDTH          0xb0
 #define ID_PIXEL_HEIGHT         0xba
 #define ID_PIXEL_CROP_BOTTOM    0x54aa
 #define ID_PIXEL_CROP_TOP       0x54bb
 #define ID_PIXEL_CROP_LEFT      0x54cc
 #define ID_PIXEL_CROP_RIGHT     0x54dd
 #define ID_DISPLAY_WIDTH        0x54b0
 #define ID_DISPLAY_HEIGHT       0x54ba
@@ -190,29 +199,40 @@ struct seek_head {
   struct ebml_list seek;
 };
 
 struct info {
   struct ebml_type timecode_scale;
   struct ebml_type duration;
 };
 
+struct block_more {
+  struct ebml_type block_add_id;
+  struct ebml_type block_additional;
+};
+
+struct block_additions {
+  struct ebml_list block_more;
+};
+
 struct block_group {
+  struct ebml_type block_additions;
   struct ebml_type duration;
   struct ebml_type reference_block;
   struct ebml_type discard_padding;
 };
 
 struct cluster {
   struct ebml_type timecode;
   struct ebml_list block_group;
 };
 
 struct video {
   struct ebml_type stereo_mode;
+  struct ebml_type alpha_mode;
   struct ebml_type pixel_width;
   struct ebml_type pixel_height;
   struct ebml_type pixel_crop_bottom;
   struct ebml_type pixel_crop_top;
   struct ebml_type pixel_crop_left;
   struct ebml_type pixel_crop_right;
   struct ebml_type display_width;
   struct ebml_type display_height;
@@ -289,16 +309,23 @@ struct saved_state {
 };
 
 struct frame {
   unsigned char * data;
   size_t length;
   struct frame * next;
 };
 
+struct block_additional {
+  unsigned int id;
+  unsigned char * data;
+  size_t length;
+  struct block_additional * next;
+};
+
 /* Public (opaque) Structures */
 struct nestegg {
   nestegg_io * io;
   nestegg_log log;
   struct pool_ctx * alloc_pool;
   uint64_t last_id;
   uint64_t last_size;
   int last_valid;
@@ -309,16 +336,17 @@ struct nestegg {
   unsigned int track_count;
 };
 
 struct nestegg_packet {
   uint64_t track;
   uint64_t timecode;
   uint64_t duration;
   struct frame * frame;
+  struct block_additional * block_additional;
   int64_t discard_padding;
 };
 
 /* Element Descriptor */
 struct ebml_element_desc {
   char const * name;
   uint64_t id;
   enum ebml_type_enum type;
@@ -369,33 +397,46 @@ static struct ebml_element_desc ne_seek_
 };
 
 static struct ebml_element_desc ne_info_elements[] = {
   E_FIELD(ID_TIMECODE_SCALE, TYPE_UINT, struct info, timecode_scale),
   E_FIELD(ID_DURATION, TYPE_FLOAT, struct info, duration),
   E_LAST
 };
 
+static struct ebml_element_desc ne_block_more_elements[] = {
+  E_FIELD(ID_BLOCK_ADD_ID, TYPE_UINT, struct block_more, block_add_id),
+  E_FIELD(ID_BLOCK_ADDITIONAL, TYPE_BINARY, struct block_more, block_additional),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_block_additions_elements[] = {
+  E_MASTER(ID_BLOCK_MORE, TYPE_MASTER, struct block_additions, block_more),
+  E_LAST
+};
+
 static struct ebml_element_desc ne_block_group_elements[] = {
   E_SUSPEND(ID_BLOCK, TYPE_BINARY),
   E_FIELD(ID_BLOCK_DURATION, TYPE_UINT, struct block_group, duration),
   E_FIELD(ID_REFERENCE_BLOCK, TYPE_INT, struct block_group, reference_block),
   E_FIELD(ID_DISCARD_PADDING, TYPE_INT, struct block_group, discard_padding),
+  E_SINGLE_MASTER(ID_BLOCK_ADDITIONS, TYPE_MASTER, struct block_group, block_additions),
   E_LAST
 };
 
 static struct ebml_element_desc ne_cluster_elements[] = {
   E_FIELD(ID_TIMECODE, TYPE_UINT, struct cluster, timecode),
   E_MASTER(ID_BLOCK_GROUP, TYPE_MASTER, struct cluster, block_group),
   E_SUSPEND(ID_SIMPLE_BLOCK, TYPE_BINARY),
   E_LAST
 };
 
 static struct ebml_element_desc ne_video_elements[] = {
   E_FIELD(ID_STEREO_MODE, TYPE_UINT, struct video, stereo_mode),
+  E_FIELD(ID_ALPHA_MODE, TYPE_UINT, struct video, alpha_mode),
   E_FIELD(ID_PIXEL_WIDTH, TYPE_UINT, struct video, pixel_width),
   E_FIELD(ID_PIXEL_HEIGHT, TYPE_UINT, struct video, pixel_height),
   E_FIELD(ID_PIXEL_CROP_BOTTOM, TYPE_UINT, struct video, pixel_crop_bottom),
   E_FIELD(ID_PIXEL_CROP_TOP, TYPE_UINT, struct video, pixel_crop_top),
   E_FIELD(ID_PIXEL_CROP_LEFT, TYPE_UINT, struct video, pixel_crop_left),
   E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right),
   E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width),
   E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height),
@@ -684,24 +725,26 @@ ne_read_float(nestegg_io * io, double * 
 }
 
 static int
 ne_read_string(nestegg * ctx, char ** val, uint64_t length)
 {
   char * str;
   int r;
 
-  if (length == 0 || length > LIMIT_STRING)
+  if (length > LIMIT_STRING)
     return -1;
   str = ne_pool_alloc(length + 1, ctx->alloc_pool);
   if (!str)
     return -1;
-  r = ne_io_read(ctx->io, (unsigned char *) str, length);
-  if (r != 1)
-    return r;
+  if (length) {
+      r = ne_io_read(ctx->io, (unsigned char *) str, length);
+      if (r != 1)
+        return r;
+  }
   str[length] = '\0';
   *val = str;
   return 1;
 }
 
 static int
 ne_read_binary(nestegg * ctx, struct ebml_binary * val, uint64_t length)
 {
@@ -1415,16 +1458,17 @@ ne_read_block_duration(nestegg * ctx, ne
 
   element = ne_find_element(id, ctx->ancestor->node);
   if (!element)
     return 1;
 
   r = ne_read_simple(ctx, element, size);
   if (r != 1)
     return r;
+
   storage = (struct ebml_type *) (ctx->ancestor->data + element->offset);
   pkt->duration = storage->v.i * ne_get_timecode_scale(ctx);
 
   return 1;
 }
 
 static int
 ne_read_discard_padding(nestegg * ctx, nestegg_packet * pkt)
@@ -1443,22 +1487,133 @@ ne_read_discard_padding(nestegg * ctx, n
 
   element = ne_find_element(id, ctx->ancestor->node);
   if (!element)
     return 1;
 
   r = ne_read_simple(ctx, element, size);
   if (r != 1)
     return r;
+
   storage = (struct ebml_type *) (ctx->ancestor->data + element->offset);
   pkt->discard_padding = storage->v.i;
 
   return 1;
 }
 
+static int
+ne_read_block_additions(nestegg * ctx, nestegg_packet * pkt)
+{
+  int r;
+  uint64_t id, size, data_size;
+  int64_t block_additions_end, block_more_end;
+  void * data;
+  int has_data;
+  struct block_additional * block_additional;
+  uint64_t add_id;
+
+  assert(pkt != NULL);
+  assert(pkt->block_additional == NULL);
+
+  r = ne_peek_element(ctx, &id, &size);
+  if (r != 1)
+    return r;
+
+  if (id != ID_BLOCK_ADDITIONS)
+    return 1;
+
+  /* This makes ne_read_element read the next element instead of returning
+     information about the already "peeked" one. */
+  ctx->last_valid = 0;
+
+  block_additions_end = ne_io_tell(ctx->io) + size;
+
+  while (ne_io_tell(ctx->io) < block_additions_end) {
+    add_id = 1;
+    data = NULL;
+    has_data = 0;
+    r = ne_read_element(ctx, &id, &size);
+    if (r != 1)
+      return -1;
+
+    if (id != ID_BLOCK_MORE) {
+      /* We don't know what this element is, so skip over it */
+      if (id != ID_VOID && id != ID_CRC32)
+        ctx->log(ctx, NESTEGG_LOG_DEBUG,
+                 "unknown element %llx in BlockAdditions", id);
+      ne_io_read_skip(ctx->io, size);
+      continue;
+    }
+
+    block_more_end = ne_io_tell(ctx->io) + size;
+
+    while (ne_io_tell(ctx->io) < block_more_end) {
+      r = ne_read_element(ctx, &id, &size);
+      if (r != 1) {
+        free(data);
+        return r;
+      }
+
+      if (id == ID_BLOCK_ADD_ID) {
+        r = ne_read_uint(ctx->io, &add_id, size);
+        if (r != 1) {
+          free(data);
+          return r;
+        }
+
+        if (add_id == 0) {
+          ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed BlockAddId 0 used");
+          free(data);
+          return -1;
+        }
+      } else if (id == ID_BLOCK_ADDITIONAL) {
+        if (has_data) {
+          /* BlockAdditional is supposed to only occur once in a
+             BlockMore. */
+          ctx->log(ctx, NESTEGG_LOG_ERROR,
+                   "Multiple BlockAdditional elements in a BlockMore");
+          free(data);
+          return -1;
+        }
+
+        has_data = 1;
+        data_size = size;
+        if (size != 0) {
+          data = ne_alloc(size);
+          r = ne_io_read(ctx->io, data, size);
+          if (r != 1) {
+            free(data);
+            return r;
+          }
+        }
+      } else {
+        /* We don't know what this element is, so skip over it */
+        if (id != ID_VOID && id != ID_CRC32)
+          ctx->log(ctx, NESTEGG_LOG_DEBUG,
+                   "unknown element %llx in BlockMore", id);
+        ne_io_read_skip(ctx->io, size);
+      }
+    }
+
+    if (has_data == 0) {
+      ctx->log(ctx, NESTEGG_LOG_ERROR,
+               "No BlockAdditional element in a BlockMore");
+      return -1;
+    }
+
+    block_additional = ne_alloc(sizeof(*block_additional));
+    block_additional->next = pkt->block_additional;
+    block_additional->id = add_id;
+    block_additional->data = data;
+    block_additional->length = data_size;
+    pkt->block_additional = block_additional;
+  }
+
+  return 1;
+}
 
 static uint64_t
 ne_buf_read_id(unsigned char const * p, size_t length)
 {
   uint64_t id = 0;
 
   while (length--) {
     id <<= 8;
@@ -1722,18 +1877,18 @@ ne_match_webm(nestegg_io io, int64_t max
   if (id != ID_EBML) {
     nestegg_destroy(ctx);
     return 0;
   }
 
   ne_ctx_push(ctx, ne_top_level_elements, ctx);
 
   /* we don't check the return value of ne_parse, that might fail because
-   * max_offset is not on a valid element end point. We only want to check
-   * the EBML ID and that the doctype is "webm". */
+     max_offset is not on a valid element end point. We only want to check
+     the EBML ID and that the doctype is "webm". */
   ne_parse(ctx, NULL, max_offset);
 
   if (ne_get_string(ctx->ebml.doctype, &doctype) != 0 ||
       strcmp(doctype, "webm") != 0) {
     nestegg_destroy(ctx);
     return 0;
   }
 
@@ -2163,16 +2318,20 @@ nestegg_track_video_params(nestegg * ctx
     return -1;
 
   value = 0;
   ne_get_uint(entry->video.stereo_mode, &value);
   if (value <= NESTEGG_VIDEO_STEREO_TOP_BOTTOM ||
       value == NESTEGG_VIDEO_STEREO_RIGHT_LEFT)
     params->stereo_mode = value;
 
+  value = 0;
+  ne_get_uint(entry->video.alpha_mode, &value);
+  params->alpha_mode = value;
+
   if (ne_get_uint(entry->video.pixel_width, &value) != 0)
     return -1;
   params->width = value;
 
   if (ne_get_uint(entry->video.pixel_height, &value) != 0)
     return -1;
   params->height = value;
 
@@ -2257,17 +2416,17 @@ nestegg_track_default_duration(nestegg *
   *duration = value;
 
   return 0;
 }
 
 int
 nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt)
 {
-  int r;
+  int r, read_block = 0;
   uint64_t id, size;
 
   *pkt = NULL;
 
   for (;;) {
     r = ne_peek_element(ctx, &id, &size);
     if (r != 1)
       return r;
@@ -2279,47 +2438,67 @@ nestegg_read_packet(nestegg * ctx, neste
         return r;
 
       /* The only DESC_FLAG_SUSPEND fields are Blocks and SimpleBlocks, which we
          handle directly. */
       r = ne_read_block(ctx, id, size, pkt);
       if (r != 1)
         return r;
 
-      r = ne_read_block_duration(ctx, *pkt);
-      if (r != 1)
-        return r;
-
-      r = ne_read_discard_padding(ctx, *pkt);
-      if (r != 1)
-        return r;
-
-      return r;
+      read_block = 1;
+
+      /* These are not valid elements of a SimpleBlock, only a full-blown
+         Block. */
+      if (id != ID_SIMPLE_BLOCK) {
+        r = ne_read_block_duration(ctx, *pkt);
+        if (r < 0)
+          return r;
+
+        r = ne_read_discard_padding(ctx, *pkt);
+        if (r < 0)
+          return r;
+
+        r = ne_read_block_additions(ctx, *pkt);
+        if (r < 0)
+          return r;
+      }
+
+      /* If we have read a block and hit EOS when reading optional block
+         subelements, don't report EOS until the next call. */
+      return read_block;
     }
 
     r =  ne_parse(ctx, NULL, -1);
     if (r != 1)
       return r;
   }
 
   return 1;
 }
 
 void
 nestegg_free_packet(nestegg_packet * pkt)
 {
   struct frame * frame;
+  struct block_additional * block_additional;
 
   while (pkt->frame) {
     frame = pkt->frame;
     pkt->frame = frame->next;
     free(frame->data);
     free(frame);
   }
 
+  while (pkt->block_additional) {
+    block_additional = pkt->block_additional;
+    pkt->block_additional = block_additional->next;
+    free(block_additional->data);
+    free(block_additional);
+  }
+
  free(pkt);
 }
 
 int
 nestegg_packet_track(nestegg_packet * pkt, unsigned int * track)
 {
   *track = pkt->track;
   return 0;
@@ -2380,16 +2559,37 @@ nestegg_packet_data(nestegg_packet * pkt
     count += 1;
     f = f->next;
   }
 
   return -1;
 }
 
 int
+nestegg_packet_additional_data(nestegg_packet * pkt, unsigned int id,
+                               unsigned char ** data, size_t * length)
+{
+  struct block_additional * a = pkt->block_additional;
+
+  *data = NULL;
+  *length = 0;
+
+  while (a) {
+    if (a->id == id) {
+      *data = a->data;
+      *length = a->length;
+      return 0;
+    }
+    a = a->next;
+  }
+
+  return -1;
+}
+
+int
 nestegg_has_cues(nestegg * ctx)
 {
   return ctx->segment.cues.cue_point.head ||
          ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES);
 }
 
 int
 nestegg_sniff(unsigned char const * buffer, size_t length)