--- 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(©, 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(©);
+ }
+ 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;