Bug 1261900: Update in-tree libnestegg. r?kinetik draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 22 Apr 2016 22:54:27 +1000
changeset 356744 33956d40af71e8733df78a58b83822cdc6f95a83
parent 356743 55d02c8b8c248e9d3e7517ce743141eaa4e94aed
child 356745 df7c45d44c2a5925f61f7d69e384ba78461025a8
push id16586
push userbmo:jyavenard@mozilla.com
push dateWed, 27 Apr 2016 02:47:40 +0000
reviewerskinetik
bugs1261900
milestone49.0a1
Bug 1261900: Update in-tree libnestegg. r?kinetik This commit adds nestegg_save_state/nestegg_restore_state methods. MozReview-Commit-ID: BdDdYg4D7T
media/libnestegg/README_MOZILLA
media/libnestegg/include/nestegg.h
media/libnestegg/src/align.h
media/libnestegg/src/nestegg.c
--- 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 3a86ef7bf7981860319dbcee526a1de8ae19be88.
+The git commit ID used was 4ade5bc1ff4dc5838786d9b481c3d6914bb466ee
--- a/media/libnestegg/include/nestegg.h
+++ b/media/libnestegg/include/nestegg.h
@@ -86,16 +86,17 @@ extern "C" {
 #define NESTEGG_LOG_DEBUG    1     /**< Debug level log message. */
 #define NESTEGG_LOG_INFO     10    /**< Informational level log message. */
 #define NESTEGG_LOG_WARNING  100   /**< Warning level log message. */
 #define NESTEGG_LOG_ERROR    1000  /**< Error level log message. */
 #define NESTEGG_LOG_CRITICAL 10000 /**< Critical level log message. */
 
 typedef struct nestegg nestegg;               /**< Opaque handle referencing the stream state. */
 typedef struct nestegg_packet nestegg_packet; /**< Opaque handle referencing a packet of data. */
+typedef struct nestegg_state nestegg_state;   /**< Opaque handle referencing the stream state backup. */
 
 /** User supplied IO context. */
 typedef struct {
   /** User supplied read callback.
       @param buffer   Buffer to read data into.
       @param length   Length of supplied buffer in bytes.
       @param userdata The #userdata supplied by the user.
       @retval  1 Read succeeded.
@@ -327,17 +328,17 @@ int nestegg_packet_tstamp(nestegg_packet
     @param packet Packet initialized by #nestegg_read_packet.
     @param duration Storage for the queried duration in nanoseconds.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_duration(nestegg_packet * packet, uint64_t * duration);
 
 /** Query the number of data chunks contained in @a packet.
     @param packet Packet initialized by #nestegg_read_packet.
-    @param count  Storage for the queried timestamp in nanoseconds.
+    @param count  Storage for the queried chunk count.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_count(nestegg_packet * packet, unsigned int * count);
 
 /** Get a pointer to chunk number @a item of packet data.
     @param packet  Packet initialized by #nestegg_read_packet.
     @param item    Zero based chunk item number.
     @param data    Storage for the queried data pointer.
@@ -384,13 +385,32 @@ int nestegg_sniff(unsigned char const * 
 
 /** Set the underlying allocation function for library allocations.
     @param realloc_func The desired function.
     @retval 1 Success.  realloc_func(p, 0) acts as free()
     @retval 0 Failure. realloc_func(p, 0) does not act as free()
     @retval -1 Failure. realloc_func(NULL, 1) failed. */
 int nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t));
 
+/** Save a nestegg context. It is intended to resume a parsing operation.
+    @param context  Stream context initialized by #nestegg_init.
+    @param context  Storage for the new nestegg state backup
+                    @see nestegg_destroy_state.
+    @retval  0 Success.
+    @retval -1 Error. */
+int nestegg_save_state(nestegg * context, nestegg_state ** state);
+
+/** Restore a nestegg state backup allocated with nestegg_save_state
+    @param context  Stream context initialized by #nestegg_init.
+    @param state    State backup to be restored.  @see nestegg_save_state
+                    The state object will be freed upon exit and is no longer
+                    usable. */
+void nestegg_restore_state(nestegg * context, nestegg_state * state);
+
+/** Destroy a nestegg state backup allocated with nestegg_save_state
+    @param state    #nestegg state backup to be freed.  @see nestegg_save_state */
+void nestegg_destroy_state(nestegg_state * state);
+
 #if defined(__cplusplus)
 }
 #endif
 
 #endif /* NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79 */
--- a/media/libnestegg/src/align.h
+++ b/media/libnestegg/src/align.h
@@ -17,17 +17,17 @@
 
 #ifdef _MSC_VER
 
 /*
  * MSVC defines max_align_t as a double.
  */
 typedef double max_align_t;
 
-#else
+#elif !defined(__CLANG_MAX_ALIGN_T_DEFINED) && !defined(_GCC_MAX_ALIGN_T)
 
 /*
  *	a type with the most strict alignment requirements
  */
 union max_align
 {
 	char   c;
 	short  s;
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -105,17 +105,16 @@
 #define ID_CUE_BLOCK_NUMBER     0x5378
 
 /* EBML Types */
 enum ebml_type_enum {
   TYPE_UNKNOWN,
   TYPE_MASTER,
   TYPE_UINT,
   TYPE_FLOAT,
-  TYPE_INT,
   TYPE_STRING,
   TYPE_BINARY
 };
 
 #define LIMIT_STRING            (1 << 20)
 #define LIMIT_BINARY            (1 << 24)
 #define LIMIT_BLOCK             (1 << 30)
 #define LIMIT_FRAME             (1 << 28)
@@ -171,16 +170,17 @@ struct ebml_type {
     uint64_t u;
     double f;
     int64_t i;
     char * s;
     struct ebml_binary b;
   } v;
   enum ebml_type_enum type;
   int read;
+  int64_t offset;
 };
 
 /* EBML Definitions */
 struct ebml {
   struct ebml_type ebml_version;
   struct ebml_type ebml_read_version;
   struct ebml_type ebml_max_id_length;
   struct ebml_type ebml_max_size_length;
@@ -199,35 +199,18 @@ 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;
@@ -295,24 +278,16 @@ struct pool_ctx {
 };
 
 struct list_node {
   struct list_node * previous;
   struct ebml_element_desc * node;
   unsigned char * data;
 };
 
-struct saved_state {
-  int64_t stream_offset;
-  struct list_node * ancestor;
-  uint64_t last_id;
-  uint64_t last_size;
-  int last_valid;
-};
-
 struct frame {
   unsigned char * data;
   size_t length;
   struct frame * next;
 };
 
 struct block_additional {
   unsigned int id;
@@ -335,19 +310,29 @@ struct nestegg {
   int64_t segment_offset;
   unsigned int track_count;
 };
 
 struct nestegg_packet {
   uint64_t track;
   uint64_t timecode;
   uint64_t duration;
+  int read_duration;
   struct frame * frame;
   struct block_additional * block_additional;
   int64_t discard_padding;
+  int read_discard_padding;
+};
+
+struct nestegg_state {
+  int64_t stream_offset;
+  struct list_node * ancestor;
+  uint64_t last_id;
+  uint64_t last_size;
+  int last_valid;
 };
 
 /* Element Descriptor */
 struct ebml_element_desc {
   char const * name;
   uint64_t id;
   enum ebml_type_enum type;
   size_t offset;
@@ -397,39 +382,19 @@ 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_BLOCK_GROUP, TYPE_MASTER),
   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),
@@ -855,30 +820,30 @@ ne_ctx_pop(nestegg * ctx)
   struct list_node * item;
 
   item = ctx->ancestor;
   ctx->ancestor = item->previous;
   free(item);
 }
 
 static int
-ne_ctx_save(nestegg * ctx, struct saved_state * s)
+ne_ctx_save(nestegg * ctx, nestegg_state * s)
 {
   s->stream_offset = ne_io_tell(ctx->io);
   if (s->stream_offset < 0)
     return -1;
   s->ancestor = ctx->ancestor;
   s->last_id = ctx->last_id;
   s->last_size = ctx->last_size;
   s->last_valid = ctx->last_valid;
   return 0;
 }
 
 static int
-ne_ctx_restore(nestegg * ctx, struct saved_state * s)
+ne_ctx_restore(nestegg * ctx, nestegg_state * s)
 {
   int r;
 
   r = ne_io_seek(ctx->io, s->stream_offset, NESTEGG_SEEK_SET);
   if (r != 0)
     return -1;
   ctx->ancestor = s->ancestor;
   ctx->last_id = s->last_id;
@@ -985,52 +950,57 @@ static int
 ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
 {
   struct ebml_type * storage;
   int r;
 
   storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset);
 
   if (storage->read) {
-    ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) already read, skipping",
+    ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) already read",
              desc->id, desc->name);
-    return 0;
+    /* We do not need to re-read the element, however we do need to move the IO
+       position back to the original offset */
+    if (storage->offset >= 0) {
+      return ne_io_seek(ctx->io, storage->offset, NESTEGG_SEEK_SET);
+    } else {
+      return 0;
+    }
   }
 
   storage->type = desc->type;
 
   ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) -> %p (%u)",
            desc->id, desc->name, storage, desc->offset);
 
   switch (desc->type) {
   case TYPE_UINT:
     r = ne_read_uint(ctx->io, &storage->v.u, length);
     break;
   case TYPE_FLOAT:
     r = ne_read_float(ctx->io, &storage->v.f, length);
     break;
-  case TYPE_INT:
-    r = ne_read_int(ctx->io, &storage->v.i, length);
-    break;
   case TYPE_STRING:
     r = ne_read_string(ctx, &storage->v.s, length);
     break;
   case TYPE_BINARY:
     r = ne_read_binary(ctx, &storage->v.b, length);
     break;
   case TYPE_MASTER:
   case TYPE_UNKNOWN:
   default:
     r = 0;
     assert(0);
     break;
   }
 
-  if (r == 1)
+  if (r == 1) {
+    storage->offset = ne_io_tell(ctx->io);
     storage->read = 1;
+  }
 
   return r;
 }
 
 static int
 ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset)
 {
   int r;
@@ -1050,17 +1020,18 @@ ne_parse(nestegg * ctx, struct ebml_elem
     r = ne_peek_element(ctx, &id, &size);
     if (r != 1)
       break;
     peeked_id = id;
 
     element = ne_find_element(id, ctx->ancestor->node);
     if (element) {
       if (element->flags & DESC_FLAG_SUSPEND) {
-        assert(element->type == TYPE_BINARY);
+        assert((element->id == ID_SIMPLE_BLOCK && element->type == TYPE_BINARY) ||
+               (element->id == ID_BLOCK_GROUP && element->type == TYPE_MASTER));
         ctx->log(ctx, NESTEGG_LOG_DEBUG, "suspend parse at %llx", id);
         r = 1;
         break;
       }
 
       r = ne_read_element(ctx, &id, &size);
       if (r != 1)
         break;
@@ -1419,99 +1390,32 @@ ne_read_block(nestegg * ctx, uint64_t bl
   }
 
   *data = pkt;
 
   return 1;
 }
 
 static int
-ne_read_block_duration(nestegg * ctx, nestegg_packet * pkt)
-{
-  int r;
-  uint64_t id, size;
-  struct ebml_element_desc * element;
-  struct ebml_type * storage;
-
-  r = ne_peek_element(ctx, &id, &size);
-  if (r != 1)
-    return r;
-
-  if (id != ID_BLOCK_DURATION)
-    return 1;
-
-  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)
-{
-  int r;
-  uint64_t id, size;
-  struct ebml_element_desc * element;
-  struct ebml_type * storage;
-
-  r = ne_peek_element(ctx, &id, &size);
-  if (r != 1)
-    return r;
-
-  if (id != ID_DISCARD_PADDING)
-    return 1;
-
-  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)
+ne_read_block_additions(nestegg * ctx, uint64_t block_id, uint64_t block_size, struct block_additional ** pkt_block_additional)
 {
   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)
+  assert(*pkt_block_additional == NULL);
+
+  if (block_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;
+  block_additions_end = ne_io_tell(ctx->io) + block_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;
@@ -1579,21 +1483,21 @@ ne_read_block_additions(nestegg * ctx, n
 
     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->next = *pkt_block_additional;
     block_additional->id = add_id;
     block_additional->data = data;
     block_additional->length = data_size;
-    pkt->block_additional = block_additional;
+    *pkt_block_additional = block_additional;
   }
 
   return 1;
 }
 
 static uint64_t
 ne_buf_read_id(unsigned char const * p, size_t length)
 {
@@ -1685,17 +1589,17 @@ ne_find_cue_point_for_tstamp(nestegg * c
   }
 
   return prev;
 }
 
 static int
 ne_is_suspend_element(uint64_t id)
 {
-  if (id == ID_SIMPLE_BLOCK || id == ID_BLOCK)
+  if (id == ID_SIMPLE_BLOCK || id == ID_BLOCK_GROUP)
     return 1;
   return 0;
 }
 
 static void
 ne_null_log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...)
 {
   if (ctx && severity && fmt)
@@ -1704,17 +1608,17 @@ ne_null_log_callback(nestegg * ctx, unsi
 
 static int
 ne_init_cue_points(nestegg * ctx, int64_t max_offset)
 {
   int r;
   struct ebml_list_node * node = ctx->segment.cues.cue_point.head;
   struct seek * found;
   uint64_t seek_pos, id;
-  struct saved_state state;
+  nestegg_state state;
 
   /* If there are no cues loaded, check for cues element in the seek head
      and load it. */
   if (!node) {
     found = ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES);
     if (!found)
       return -1;
 
@@ -1980,16 +1884,79 @@ nestegg_destroy(nestegg * ctx)
   while (ctx->ancestor)
     ne_ctx_pop(ctx);
   ne_pool_destroy(ctx->alloc_pool);
   free(ctx->io);
   free(ctx);
 }
 
 int
+nestegg_save_state(nestegg * ctx, nestegg_state ** state)
+{
+  struct list_node * item;
+  struct nestegg copy;
+  nestegg_state * s;
+
+  s = ne_alloc(sizeof(*s));
+  if (!s) {
+    return -1;
+  }
+
+  if (ne_ctx_save(ctx, s) < 0) {
+    free(s);
+    return -1;
+  }
+
+  copy.ancestor = NULL;
+  item = ctx->ancestor;
+  while (item) {
+    ne_ctx_push(&copy, item->node, item->data);
+    item = item->previous;
+  }
+  /* ancestor now point to the first item of the context stack. */
+  s->ancestor = copy.ancestor;
+  *state = s;
+  return 0;
+}
+
+void
+nestegg_restore_state(nestegg * ctx, nestegg_state * s)
+{
+  struct nestegg copy;
+
+  if (!s->ancestor) {
+    free(s);
+    return;
+  }
+
+  while (ctx->ancestor)
+    ne_ctx_pop(ctx);
+
+  copy.ancestor = s->ancestor;
+  while (copy.ancestor) {
+    ne_ctx_push(ctx, copy.ancestor->node, copy.ancestor->data);
+    ne_ctx_pop(&copy);
+  }
+  s->ancestor = ctx->ancestor;
+  ne_ctx_restore(ctx, s);
+  free(s);
+  return;
+}
+
+void
+nestegg_destroy_state(nestegg_state * state)
+{
+  nestegg ctx;
+  ctx.ancestor = state->ancestor;
+  while (ctx.ancestor)
+    ne_ctx_pop(&ctx);
+  free(state);
+}
+
+int
 nestegg_duration(nestegg * ctx, uint64_t * duration)
 {
   uint64_t tc_scale;
   double unscaled_duration;
 
   if (ne_get_float(ctx->segment.info.duration, &unscaled_duration) != 0)
     return -1;
 
@@ -2447,38 +2414,95 @@ nestegg_read_packet(nestegg * ctx, neste
       return r;
 
     /* Any DESC_FLAG_SUSPEND fields must be handled here. */
     if (ne_is_suspend_element(id)) {
       r = ne_read_element(ctx, &id, &size);
       if (r != 1)
         return r;
 
-      /* The only DESC_FLAG_SUSPEND fields are Blocks and SimpleBlocks, which we
+      /* The only DESC_FLAG_SUSPEND fields are BlocksGroups and SimpleBlocks, which we
          handle directly. */
-      r = ne_read_block(ctx, id, size, pkt);
-      if (r != 1)
-        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)
+      if (id == ID_SIMPLE_BLOCK) {
+        r = ne_read_block(ctx, id, size, pkt);
+        if (r != 1)
           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;
+        read_block = 1;
+      } else {
+        int64_t block_group_end;
+        uint64_t block_duration = 0;
+        int read_block_duration = 0;
+        int64_t discard_padding = 0;
+        int read_discard_padding = 0;
+        struct block_additional * block_additional = NULL;
+
+        assert(id == ID_BLOCK_GROUP);
+
+        /* This makes ne_read_element read the next element instead of returning
+           information about the already "peeked" one. */
+        ctx->last_valid = 0;
+
+        block_group_end = ne_io_tell(ctx->io) + size;
+
+        /* Read the entire BlockGroup manually. */
+        while (ne_io_tell(ctx->io) < block_group_end) {
+          r = ne_read_element(ctx, &id, &size);
+          if (r != 1)
+            return r;
+
+          switch (id) {
+          case ID_BLOCK: {
+            r = ne_read_block(ctx, id, size, pkt);
+            if (r != 1)
+              return r;
+
+            read_block = 1;
+            break;
+          }
+          case ID_BLOCK_DURATION: {
+            r = ne_read_uint(ctx->io, &block_duration, size);
+            if (r < 0)
+              return r;
+            block_duration *= ne_get_timecode_scale(ctx);
+            read_block_duration = 1;
+            break;
+          }
+          case ID_DISCARD_PADDING: {
+            r = ne_read_int(ctx->io, &discard_padding, size);
+            if (r < 0)
+              return r;
+            read_discard_padding = 1;
+            break;
+          }
+          case ID_BLOCK_ADDITIONS: {
+            r = ne_read_block_additions(ctx, id, size, &block_additional);
+            if (r < 0)
+              return r;
+            break;
+          }
+          default:
+            /* 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 BlockGroup", id);
+            ne_io_read_skip(ctx->io, size);
+          }
+        }
+
+        assert(read_block == (pkt != NULL));
+        if (*pkt) {
+          (*pkt)->duration = block_duration;
+          (*pkt)->read_duration = read_block_duration;
+          (*pkt)->discard_padding = discard_padding;
+          (*pkt)->read_discard_padding = read_discard_padding;
+          (*pkt)->block_additional = block_additional;
+        } else {
+          free(block_additional);
+        }
       }
 
       /* 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);
@@ -2524,23 +2548,27 @@ nestegg_packet_tstamp(nestegg_packet * p
 {
   *tstamp = pkt->timecode;
   return 0;
 }
 
 int
 nestegg_packet_duration(nestegg_packet * pkt, uint64_t * duration)
 {
+  if (!pkt->read_duration)
+    return -1;
   *duration = pkt->duration;
   return 0;
 }
 
 int
 nestegg_packet_discard_padding(nestegg_packet * pkt, int64_t * discard_padding)
 {
+  if (!pkt->read_discard_padding)
+    return -1;
   *discard_padding = pkt->discard_padding;
   return 0;
 }
 
 int
 nestegg_packet_count(nestegg_packet * pkt, unsigned int * count)
 {
   struct frame * f = pkt->frame;