diff --git a/Makefile b/Makefile index 1f3af5b..9be1e04 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PROG = oxframe -SRC = src/oxframe.c src/nestegg.c src/halloc/halloc.c +SRC = src/oxframe.c src/nestegg.c PREFIX ?= /usr/local BINDIR ?= ${DESTDIR}${PREFIX}/bin @@ -9,7 +9,7 @@ CC ?= gcc CFLAGS ?= -Wall -Wno-parantheses -O3 FLAGS += -ffast-math -fsigned-char -fforce-addr -fomit-frame-pointer -finline-functions -funroll-loops FLAGS = -DHAVE_VP9=1 ${CFLAGS} -FLAGS += -I. -Isrc -Isrc/halloc +FLAGS += -I. -Isrc INSTALL = install @@ -27,7 +27,7 @@ ${PROG}: ${SRC} ${CC} -std=c99 ${FLAGS} ${INCLUDEFLAGS} -o ${PROG} ${SRC} ${LINKFLAGS} clean: - -@rm -f ${PROG} *~ core *.core src/*.o src/halloc/src/*.o + -@rm -f ${PROG} *~ core *.core src/*.o install: ${PROG} ${INSTALL} -c -m 555 -o root -g bin ${PROG} ${BINDIR} diff --git a/src/halloc/README b/src/halloc/README deleted file mode 100644 index 337089d..0000000 --- a/src/halloc/README +++ /dev/null @@ -1,45 +0,0 @@ -halloc 1.2.1 -============ - - Hierarchical memory heap interface - an extension to standard - malloc/free interface that simplifies tasks of memory disposal - when allocated structures exhibit hierarchical properties. - - http://swapped.cc/halloc -= - To build libhalloc.a with GNU tools run - make - - To install in /usr/include and /usr/lib - make install - - To cleanup the build files - make clean -= - halloc-1.2.1 - * fixed a double-free bug in _set_allocator() as per - Matthew Gregan comments - - * switched to using NULL instead of 0 where applicable - - halloc-1.2.0 - * added missing include to halloc.c - - * improved standard compliance thanks to the feedback - received from Stan Tobias. Two things were fixed - - - - hblock_t structure no longer uses zero-sized 'data' - array, which happened to be common, but non-standard - extension; - - - secondly, added the code to test the behaviour of - realloc(ptr, 0). Standard allows it NOT to act as - free(), in which case halloc will use its own version - of allocator calling free() when neccessary. - - halloc-1.1.0 - * initial public release (rewrite of hhmalloc library) - -============================================================================= -Copyright (c) 2004-2010, Alex Pankratov (ap@swapped.cc). All rights reserved. - diff --git a/src/halloc/align.h b/src/halloc/align.h deleted file mode 100644 index 58c82b4..0000000 --- a/src/halloc/align.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2004-2010 Alex Pankratov. All rights reserved. - * - * Hierarchical memory allocator, 1.2.1 - * http://swapped.cc/halloc - */ - -/* - * The program is distributed under terms of BSD license. - * You can obtain the copy of the license by visiting: - * - * http://www.opensource.org/licenses/bsd-license.php - */ - -#ifndef _LIBP_ALIGN_H_ -#define _LIBP_ALIGN_H_ - -#ifdef _MSC_VER - -/* - * MSVC defines max_align_t as a double. - */ -typedef double max_align_t; - -#else - -/* - * a type with the most strict alignment requirements - */ -union max_align -{ - char c; - short s; - long l; - int i; - float f; - double d; - void * v; - void (*q)(void); -}; - -typedef union max_align max_align_t; - -#endif - -#endif - diff --git a/src/halloc/halloc.c b/src/halloc/halloc.c deleted file mode 100644 index 3187b3c..0000000 --- a/src/halloc/halloc.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2004i-2010 Alex Pankratov. All rights reserved. - * - * Hierarchical memory allocator, 1.2.1 - * http://swapped.cc/halloc - */ - -/* - * The program is distributed under terms of BSD license. - * You can obtain the copy of the license by visiting: - * - * http://www.opensource.org/licenses/bsd-license.php - */ - -#include /* realloc */ -#include /* memset & co */ - -#include "halloc.h" -#include "align.h" -#include "hlist.h" - -/* - * block control header - */ -typedef struct hblock -{ -#ifndef NDEBUG -#define HH_MAGIC 0x20040518L - long magic; -#endif - hlist_item_t siblings; /* 2 pointers */ - hlist_head_t children; /* 1 pointer */ - max_align_t data[1]; /* not allocated, see below */ - -} hblock_t; - -#define sizeof_hblock offsetof(hblock_t, data) - -/* - * - */ -realloc_t halloc_allocator = NULL; - -#define allocator halloc_allocator - -/* - * static methods - */ -int halloc_set_allocator(realloc_t realloc_func); -static void * _realloc(void * ptr, size_t n); - -static int _relate(hblock_t * b, hblock_t * p); -static void _free_children(hblock_t * p); - -/* - * Core API - */ -void * halloc(void * ptr, size_t len) -{ - hblock_t * p; - - /* set up default allocator */ - if (! allocator) - { - if (halloc_set_allocator(realloc) == 0) - { - halloc_set_allocator(_realloc); - } - assert(allocator); - } - - /* calloc */ - if (! ptr) - { - if (! len) - return NULL; - - p = allocator(0, len + sizeof_hblock); - if (! p) - return NULL; -#ifndef NDEBUG - p->magic = HH_MAGIC; -#endif - hlist_init(&p->children); - hlist_init_item(&p->siblings); - - return p->data; - } - - p = structof(ptr, hblock_t, data); - assert(p->magic == HH_MAGIC); - - /* realloc */ - if (len) - { - p = allocator(p, len + sizeof_hblock); - if (! p) - return NULL; - - hlist_relink(&p->siblings); - hlist_relink_head(&p->children); - - return p->data; - } - - /* free */ - _free_children(p); - hlist_del(&p->siblings); - allocator(p, 0); - - return NULL; -} - -void hattach(void * block, void * parent) -{ - hblock_t * b, * p; - - if (! block) - { - assert(! parent); - return; - } - - /* detach */ - b = structof(block, hblock_t, data); - assert(b->magic == HH_MAGIC); - - hlist_del(&b->siblings); - - if (! parent) - return; - - /* attach */ - p = structof(parent, hblock_t, data); - assert(p->magic == HH_MAGIC); - - /* sanity checks */ - assert(b != p); /* trivial */ - assert(! _relate(p, b)); /* heavy ! */ - - hlist_add(&p->children, &b->siblings); -} - -/* - * malloc/free api - */ -void * h_malloc(size_t len) -{ - return halloc(0, len); -} - -void * h_calloc(size_t n, size_t len) -{ - void * ptr = halloc(0, len*=n); - return ptr ? memset(ptr, 0, len) : NULL; -} - -void * h_realloc(void * ptr, size_t len) -{ - return halloc(ptr, len); -} - -void h_free(void * ptr) -{ - halloc(ptr, 0); -} - -char * h_strdup(const char * str) -{ - size_t len = strlen(str); - char * ptr = halloc(0, len + 1); - return ptr ? (ptr[len] = 0, memcpy(ptr, str, len)) : NULL; -} - -/* - * static stuff - */ -int halloc_set_allocator(realloc_t realloc_func) -{ - void * p; - assert(! allocator); - - /* - * the purpose of the test below is to check the behaviour - * of realloc(ptr, 0), which is defined in the standard - * as an implementation-specific. if it returns zero, - * then it's equivalent to free(). it can however return - * non-zero, in which case it cannot be used for freeing - * memory blocks and we'll need to supply our own version - * - * Thanks to Stan Tobias for pointing this tricky part out. - */ - if (! (p = realloc_func(NULL, 1))) - /* hmm */ - return -1; - - if ((p = realloc_func(p, 0))) - { - /* realloc_func cannot be used as free() */ - return 0; - } - allocator = realloc_func; - return 1; -} - -static void * _realloc(void * ptr, size_t n) -{ - /* - * free'ing realloc() - */ - if (n) - return realloc(ptr, n); - free(ptr); - return NULL; -} - -static int _relate(hblock_t * b, hblock_t * p) -{ - hlist_item_t * i; - - if (!b || !p) - return 0; - - /* - * since there is no 'parent' pointer, which would've allowed - * O(log(n)) upward traversal, the check must use O(n) downward - * iteration of the entire hierarchy; and this can be VERY SLOW - */ - hlist_for_each(i, &p->children) - { - hblock_t * q = structof(i, hblock_t, siblings); - if (q == b || _relate(b, q)) - return 1; - } - return 0; -} - -static void _free_children(hblock_t * p) -{ - hlist_item_t * i, * tmp; - -#ifndef NDEBUG - /* - * this catches loops in hierarchy with almost zero - * overhead (compared to _relate() running time) - */ - assert(p && p->magic == HH_MAGIC); - p->magic = 0; -#endif - hlist_for_each_safe(i, tmp, &p->children) - { - hblock_t * q = structof(i, hblock_t, siblings); - _free_children(q); - allocator(q, 0); - } -} - diff --git a/src/halloc/halloc.h b/src/halloc/halloc.h deleted file mode 100644 index 10af4e8..0000000 --- a/src/halloc/halloc.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2004-2010 Alex Pankratov. All rights reserved. - * - * Hierarchical memory allocator, 1.2.1 - * http://swapped.cc/halloc - */ - -/* - * The program is distributed under terms of BSD license. - * You can obtain the copy of the license by visiting: - * - * http://www.opensource.org/licenses/bsd-license.php - */ - -#ifndef _LIBP_HALLOC_H_ -#define _LIBP_HALLOC_H_ - -#include /* size_t */ - -/* - * Core API - */ -void * halloc (void * block, size_t len); -void hattach(void * block, void * parent); - -/* - * standard malloc/free api - */ -void * h_malloc (size_t len); -void * h_calloc (size_t n, size_t len); -void * h_realloc(void * p, size_t len); -void h_free (void * p); -char * h_strdup (const char * str); - -/* - * the underlying allocator - */ -typedef void * (* realloc_t)(void * ptr, size_t len); - -extern realloc_t halloc_allocator; - -#endif - diff --git a/src/halloc/hlist.h b/src/halloc/hlist.h deleted file mode 100644 index 2791f78..0000000 --- a/src/halloc/hlist.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2004-2010 Alex Pankratov. All rights reserved. - * - * Hierarchical memory allocator, 1.2.1 - * http://swapped.cc/halloc - */ - -/* - * The program is distributed under terms of BSD license. - * You can obtain the copy of the license by visiting: - * - * http://www.opensource.org/licenses/bsd-license.php - */ - -#ifndef _LIBP_HLIST_H_ -#define _LIBP_HLIST_H_ - -#include -#include "macros.h" /* static_inline */ - -/* - * weak double-linked list w/ tail sentinel - */ -typedef struct hlist_head hlist_head_t; -typedef struct hlist_item hlist_item_t; - -/* - * - */ -struct hlist_head -{ - hlist_item_t * next; -}; - -struct hlist_item -{ - hlist_item_t * next; - hlist_item_t ** prev; -}; - -/* - * shared tail sentinel - */ -struct hlist_item hlist_null; - -/* - * - */ -#define __hlist_init(h) { &hlist_null } -#define __hlist_init_item(i) { &hlist_null, &(i).next } - -static_inline void hlist_init(hlist_head_t * h); -static_inline void hlist_init_item(hlist_item_t * i); - -/* static_inline void hlist_purge(hlist_head_t * h); */ - -/* static_inline bool_t hlist_empty(const hlist_head_t * h); */ - -/* static_inline hlist_item_t * hlist_head(const hlist_head_t * h); */ - -/* static_inline hlist_item_t * hlist_next(const hlist_item_t * i); */ -/* static_inline hlist_item_t * hlist_prev(const hlist_item_t * i, - const hlist_head_t * h); */ - -static_inline void hlist_add(hlist_head_t * h, hlist_item_t * i); - -/* static_inline void hlist_add_prev(hlist_item_t * l, hlist_item_t * i); */ -/* static_inline void hlist_add_next(hlist_item_t * l, hlist_item_t * i); */ - -static_inline void hlist_del(hlist_item_t * i); - -static_inline void hlist_relink(hlist_item_t * i); -static_inline void hlist_relink_head(hlist_head_t * h); - -#define hlist_for_each(i, h) \ - for (i = (h)->next; i != &hlist_null; i = i->next) - -#define hlist_for_each_safe(i, tmp, h) \ - for (i = (h)->next, tmp = i->next; \ - i!= &hlist_null; \ - i = tmp, tmp = i->next) - -/* - * static - */ -static_inline void hlist_init(hlist_head_t * h) -{ - assert(h); - h->next = &hlist_null; -} - -static_inline void hlist_init_item(hlist_item_t * i) -{ - assert(i); - i->prev = &i->next; - i->next = &hlist_null; -} - -static_inline void hlist_add(hlist_head_t * h, hlist_item_t * i) -{ - hlist_item_t * next; - assert(h && i); - - next = i->next = h->next; - next->prev = &i->next; - h->next = i; - i->prev = &h->next; -} - -static_inline void hlist_del(hlist_item_t * i) -{ - hlist_item_t * next; - assert(i); - - next = i->next; - next->prev = i->prev; - *i->prev = next; - - hlist_init_item(i); -} - -static_inline void hlist_relink(hlist_item_t * i) -{ - assert(i); - *i->prev = i; - i->next->prev = &i->next; -} - -static_inline void hlist_relink_head(hlist_head_t * h) -{ - assert(h); - h->next->prev = &h->next; -} - -#endif - diff --git a/src/halloc/macros.h b/src/halloc/macros.h deleted file mode 100644 index 1f84bc2..0000000 --- a/src/halloc/macros.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2004-2010 Alex Pankratov. All rights reserved. - * - * Hierarchical memory allocator, 1.2.1 - * http://swapped.cc/halloc - */ - -/* - * The program is distributed under terms of BSD license. - * You can obtain the copy of the license by visiting: - * - * http://www.opensource.org/licenses/bsd-license.php - */ - -#ifndef _LIBP_MACROS_H_ -#define _LIBP_MACROS_H_ - -#include /* offsetof */ - -/* - restore pointer to the structure by a pointer to its field - */ -#define structof(p,t,f) ((t*)(- (ptrdiff_t) offsetof(t,f) + (char*)(p))) - -/* - * redefine for the target compiler - */ -#ifdef _WIN32 -#define static_inline static __inline -#else -#define static_inline static __inline__ -#endif - - -#endif - diff --git a/src/nestegg.c b/src/nestegg.c index 0c0914d..c8ffbcd 100644 --- a/src/nestegg.c +++ b/src/nestegg.c @@ -5,104 +5,116 @@ * accompanying file LICENSE for details. */ #include +#include #include #include -#include "halloc.h" #include "nestegg.h" /* EBML Elements */ -#define ID_EBML 0x1a45dfa3 -#define ID_EBML_VERSION 0x4286 -#define ID_EBML_READ_VERSION 0x42f7 -#define ID_EBML_MAX_ID_LENGTH 0x42f2 -#define ID_EBML_MAX_SIZE_LENGTH 0x42f3 -#define ID_DOCTYPE 0x4282 -#define ID_DOCTYPE_VERSION 0x4287 -#define ID_DOCTYPE_READ_VERSION 0x4285 +#define ID_EBML 0x1a45dfa3 +#define ID_EBML_VERSION 0x4286 +#define ID_EBML_READ_VERSION 0x42f7 +#define ID_EBML_MAX_ID_LENGTH 0x42f2 +#define ID_EBML_MAX_SIZE_LENGTH 0x42f3 +#define ID_DOCTYPE 0x4282 +#define ID_DOCTYPE_VERSION 0x4287 +#define ID_DOCTYPE_READ_VERSION 0x4285 /* Global Elements */ -#define ID_VOID 0xec -#define ID_CRC32 0xbf +#define ID_VOID 0xec +#define ID_CRC32 0xbf /* WebM Elements */ -#define ID_SEGMENT 0x18538067 +#define ID_SEGMENT 0x18538067 /* Seek Head Elements */ -#define ID_SEEK_HEAD 0x114d9b74 -#define ID_SEEK 0x4dbb -#define ID_SEEK_ID 0x53ab -#define ID_SEEK_POSITION 0x53ac +#define ID_SEEK_HEAD 0x114d9b74 +#define ID_SEEK 0x4dbb +#define ID_SEEK_ID 0x53ab +#define ID_SEEK_POSITION 0x53ac /* Info Elements */ -#define ID_INFO 0x1549a966 -#define ID_TIMECODE_SCALE 0x2ad7b1 -#define ID_DURATION 0x4489 +#define ID_INFO 0x1549a966 +#define ID_TIMECODE_SCALE 0x2ad7b1 +#define ID_DURATION 0x4489 /* Cluster Elements */ -#define ID_CLUSTER 0x1f43b675 -#define ID_TIMECODE 0xe7 -#define ID_BLOCK_GROUP 0xa0 -#define ID_SIMPLE_BLOCK 0xa3 +#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 +#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 +#define ID_BLOCK_MORE 0xa6 /* BlockMore Elements */ -#define ID_BLOCK_ADD_ID 0xee -#define ID_BLOCK_ADDITIONAL 0xa5 +#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 -#define ID_FLAG_LACING 0x9c -#define ID_TRACK_TIMECODE_SCALE 0x23314f -#define ID_LANGUAGE 0x22b59c -#define ID_CODEC_ID 0x86 -#define ID_CODEC_PRIVATE 0x63a2 -#define ID_CODEC_DELAY 0x56aa -#define ID_SEEK_PREROLL 0x56bb -#define ID_DEFAULT_DURATION 0x23e383 +#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 +#define ID_FLAG_LACING 0x9c +#define ID_TRACK_TIMECODE_SCALE 0x23314f +#define ID_LANGUAGE 0x22b59c +#define ID_CODEC_ID 0x86 +#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 +#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 /* Audio Elements */ -#define ID_AUDIO 0xe1 -#define ID_SAMPLING_FREQUENCY 0xb5 -#define ID_CHANNELS 0x9f -#define ID_BIT_DEPTH 0x6264 +#define ID_AUDIO 0xe1 +#define ID_SAMPLING_FREQUENCY 0xb5 +#define ID_CHANNELS 0x9f +#define ID_BIT_DEPTH 0x6264 /* Cues Elements */ -#define ID_CUES 0x1c53bb6b -#define ID_CUE_POINT 0xbb -#define ID_CUE_TIME 0xb3 -#define ID_CUE_TRACK_POSITIONS 0xb7 -#define ID_CUE_TRACK 0xf7 -#define ID_CUE_CLUSTER_POSITION 0xf1 -#define ID_CUE_BLOCK_NUMBER 0x5378 +#define ID_CUES 0x1c53bb6b +#define ID_CUE_POINT 0xbb +#define ID_CUE_TIME 0xb3 +#define ID_CUE_TRACK_POSITIONS 0xb7 +#define ID_CUE_TRACK 0xf7 +#define ID_CUE_CLUSTER_POSITION 0xf1 +#define ID_CUE_BLOCK_NUMBER 0x5378 + +/* Encoding Elements */ +#define ID_CONTENT_ENCODINGS 0x6d80 +#define ID_CONTENT_ENCODING 0x6240 +#define ID_CONTENT_ENCODING_TYPE 0x5033 + +/* Encryption Elements */ +#define ID_CONTENT_ENCRYPTION 0x5035 +#define ID_CONTENT_ENC_ALGO 0x47e1 +#define ID_CONTENT_ENC_KEY_ID 0x47e2 +#define ID_CONTENT_ENC_AES_SETTINGS 0x47e7 +#define ID_AES_SETTINGS_CIPHER_MODE 0x47e8 /* EBML Types */ enum ebml_type_enum { @@ -110,40 +122,52 @@ enum ebml_type_enum { 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) +#define LIMIT_STRING (1 << 20) +#define LIMIT_BINARY (1 << 24) +#define LIMIT_BLOCK (1 << 30) +#define LIMIT_FRAME (1 << 28) /* Field Flags */ -#define DESC_FLAG_NONE 0 -#define DESC_FLAG_MULTI (1 << 0) -#define DESC_FLAG_SUSPEND (1 << 1) -#define DESC_FLAG_OFFSET (1 << 2) +#define DESC_FLAG_NONE 0 +#define DESC_FLAG_MULTI (1 << 0) +#define DESC_FLAG_SUSPEND (1 << 1) +#define DESC_FLAG_OFFSET (1 << 2) /* Block Header Flags */ -#define BLOCK_FLAGS_LACING 6 +#define SIMPLE_BLOCK_FLAGS_KEYFRAME (1 << 7) +#define BLOCK_FLAGS_LACING 6 /* Lacing Constants */ -#define LACING_NONE 0 -#define LACING_XIPH 1 -#define LACING_FIXED 2 -#define LACING_EBML 3 +#define LACING_NONE 0 +#define LACING_XIPH 1 +#define LACING_FIXED 2 +#define LACING_EBML 3 /* Track Types */ -#define TRACK_TYPE_VIDEO 1 -#define TRACK_TYPE_AUDIO 2 +#define TRACK_TYPE_VIDEO 1 +#define TRACK_TYPE_AUDIO 2 /* Track IDs */ -#define TRACK_ID_VP8 "V_VP8" -#define TRACK_ID_VP9 "V_VP9" -#define TRACK_ID_VORBIS "A_VORBIS" -#define TRACK_ID_OPUS "A_OPUS" +#define TRACK_ID_VP8 "V_VP8" +#define TRACK_ID_VP9 "V_VP9" +#define TRACK_ID_VORBIS "A_VORBIS" +#define TRACK_ID_OPUS "A_OPUS" + +/* Track Encryption */ +#define CONTENT_ENC_ALGO_AES 5 +#define AES_SETTINGS_CIPHER_CTR 1 + +/* Packet Encryption */ +#define SIGNAL_BYTE_SIZE 1 +#define IV_SIZE 8 + +/* Signal Byte */ +#define PACKET_ENCRYPTED 1 +#define ENCRYPTED_BIT_MASK (1 << 0) enum vint_mask { MASK_NONE, @@ -204,27 +228,6 @@ struct info { 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; @@ -244,6 +247,25 @@ struct audio { struct ebml_type bit_depth; }; +struct content_enc_aes_settings { + struct ebml_type aes_settings_cipher_mode; +}; + +struct content_encryption { + struct ebml_type content_enc_algo; + struct ebml_type content_enc_key_id; + struct ebml_list content_enc_aes_settings; +}; + +struct content_encoding { + struct ebml_type content_encoding_type; + struct ebml_list content_encryption; +}; + +struct content_encodings { + struct ebml_list content_encoding; +}; + struct track_entry { struct ebml_type number; struct ebml_type uid; @@ -260,6 +282,7 @@ struct track_entry { struct ebml_type default_duration; struct video video; struct audio audio; + struct content_encodings content_encodings; }; struct tracks { @@ -284,14 +307,18 @@ struct cues { struct segment { struct ebml_list seek_head; struct info info; - struct ebml_list cluster; struct tracks tracks; struct cues cues; }; /* Misc. */ +struct pool_node { + struct pool_node * next; + void * data; +}; + struct pool_ctx { - char dummy; + struct pool_node * head; }; struct list_node { @@ -302,15 +329,21 @@ struct list_node { struct saved_state { int64_t stream_offset; - struct list_node * ancestor; uint64_t last_id; uint64_t last_size; int last_valid; }; +struct frame_encryption { + unsigned char * iv; + size_t length; + uint8_t signal_byte; +}; + struct frame { unsigned char * data; size_t length; + struct frame_encryption * frame_encryption; struct frame * next; }; @@ -334,15 +367,24 @@ struct nestegg { struct segment segment; int64_t segment_offset; unsigned int track_count; + /* Last read cluster. */ + uint64_t cluster_timecode; + int read_cluster_timecode; + struct saved_state saved; }; 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; + int64_t reference_block; + int read_reference_block; + uint8_t keyframe; }; /* Element Descriptor */ @@ -402,33 +444,6 @@ static struct ebml_element_desc ne_info_elements[] = { 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), @@ -450,6 +465,29 @@ static struct ebml_element_desc ne_audio_elements[] = { E_LAST }; +static struct ebml_element_desc ne_content_enc_aes_settings_elements[] = { + E_FIELD(ID_AES_SETTINGS_CIPHER_MODE, TYPE_UINT, struct content_enc_aes_settings, aes_settings_cipher_mode), + E_LAST +}; + +static struct ebml_element_desc ne_content_encryption_elements[] = { + E_FIELD(ID_CONTENT_ENC_ALGO, TYPE_UINT, struct content_encryption, content_enc_algo), + E_FIELD(ID_CONTENT_ENC_KEY_ID, TYPE_BINARY, struct content_encryption, content_enc_key_id), + E_MASTER(ID_CONTENT_ENC_AES_SETTINGS, TYPE_MASTER, struct content_encryption, content_enc_aes_settings), + E_LAST +}; + +static struct ebml_element_desc ne_content_encoding_elements[] = { + E_FIELD(ID_CONTENT_ENCODING_TYPE, TYPE_UINT, struct content_encoding, content_encoding_type), + E_MASTER(ID_CONTENT_ENCRYPTION, TYPE_MASTER, struct content_encoding, content_encryption), + E_LAST +}; + +static struct ebml_element_desc ne_content_encodings_elements[] = { + E_MASTER(ID_CONTENT_ENCODING, TYPE_MASTER, struct content_encodings, content_encoding), + E_LAST +}; + static struct ebml_element_desc ne_track_entry_elements[] = { E_FIELD(ID_TRACK_NUMBER, TYPE_UINT, struct track_entry, number), E_FIELD(ID_TRACK_UID, TYPE_UINT, struct track_entry, uid), @@ -466,6 +504,7 @@ static struct ebml_element_desc ne_track_entry_elements[] = { E_FIELD(ID_DEFAULT_DURATION, TYPE_UINT, struct track_entry, default_duration), E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video), E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio), + E_SINGLE_MASTER(ID_CONTENT_ENCODINGS, TYPE_MASTER, struct track_entry, content_encodings), E_LAST }; @@ -495,7 +534,7 @@ static struct ebml_element_desc ne_cues_elements[] = { static struct ebml_element_desc ne_segment_elements[] = { E_MASTER(ID_SEEK_HEAD, TYPE_MASTER, struct segment, seek_head), E_SINGLE_MASTER(ID_INFO, TYPE_MASTER, struct segment, info), - E_MASTER(ID_CLUSTER, TYPE_MASTER, struct segment, cluster), + E_SUSPEND(ID_CLUSTER, TYPE_MASTER), E_SINGLE_MASTER(ID_TRACKS, TYPE_MASTER, struct segment, tracks), E_SINGLE_MASTER(ID_CUES, TYPE_MASTER, struct segment, cues), E_LAST @@ -517,26 +556,41 @@ static struct ebml_element_desc ne_top_level_elements[] = { static struct pool_ctx * ne_pool_init(void) { - return h_malloc(sizeof(struct pool_ctx)); + return calloc(1, sizeof(struct pool_ctx)); } static void ne_pool_destroy(struct pool_ctx * pool) { - h_free(pool); + struct pool_node * node = pool->head; + while (node) { + struct pool_node * old = node; + node = node->next; + free(old->data); + free(old); + } + free(pool); } static void * ne_pool_alloc(size_t size, struct pool_ctx * pool) { - void * p; + struct pool_node * node; - p = h_malloc(size); - if (!p) + node = calloc(1, sizeof(*node)); + if (!node) return NULL; - hattach(p, pool); - memset(p, 0, size); - return p; + + node->data = calloc(1, size); + if (!node->data) { + free(node); + return NULL; + } + + node->next = pool->head; + pool->head = node; + + return node->data; } static void * @@ -688,8 +742,8 @@ ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) base = 1; base <<= length * 8 - 1; if (uval >= base) { - base = 1; - base <<= length * 8; + base = 1; + base <<= length * 8; } else { base = 0; } @@ -736,9 +790,9 @@ ne_read_string(nestegg * ctx, char ** val, uint64_t length) if (!str) return -1; if (length) { - r = ne_io_read(ctx->io, (unsigned char *) str, length); - if (r != 1) - return r; + r = ne_io_read(ctx->io, (unsigned char *) str, length); + if (r != 1) + return r; } str[length] = '\0'; *val = str; @@ -865,7 +919,6 @@ ne_ctx_save(nestegg * ctx, struct saved_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; @@ -877,10 +930,11 @@ ne_ctx_restore(nestegg * ctx, struct saved_state * s) { int r; + if (s->stream_offset < 0) + return -1; 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; ctx->last_size = s->last_size; ctx->last_valid = s->last_valid; @@ -985,7 +1039,7 @@ static int ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) { struct ebml_type * storage; - int r = 0; + int r; storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset); @@ -1007,9 +1061,6 @@ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) 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; @@ -1018,8 +1069,8 @@ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) break; case TYPE_MASTER: case TYPE_UNKNOWN: + default: assert(0); - r = 0; break; } @@ -1037,8 +1088,7 @@ ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset uint64_t id, size, peeked_id; struct ebml_element_desc * element; - if (!ctx->ancestor) - return -1; + assert(ctx->ancestor); for (;;) { if (max_offset > 0 && ne_io_tell(ctx->io) >= max_offset) { @@ -1054,7 +1104,7 @@ ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset 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_CLUSTER && element->type == TYPE_MASTER); ctx->log(ctx, NESTEGG_LOG_DEBUG, "suspend parse at %llx", id); r = 1; break; @@ -1116,23 +1166,53 @@ ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset return r; } -static uint64_t -ne_xiph_lace_value(unsigned char ** np) +static int +ne_read_block_encryption(nestegg * ctx, struct track_entry const * entry, + uint64_t * encoding_type, uint64_t * encryption_algo, + uint64_t * encryption_mode) { - uint64_t lace; - uint64_t value; - unsigned char * p = *np; + struct content_encoding * encoding; + struct content_encryption * encryption; + struct content_enc_aes_settings * aes_settings; - lace = *p++; - value = lace; - while (lace == 255) { - lace = *p++; - value += lace; + *encoding_type = 0; + if (entry->content_encodings.content_encoding.head) { + encoding = entry->content_encodings.content_encoding.head->data; + if (ne_get_uint(encoding->content_encoding_type, encoding_type) != 0) + return -1; + + if (*encoding_type == NESTEGG_ENCODING_ENCRYPTION) { + /* Metadata states content is encrypted */ + if (!encoding->content_encryption.head) + return -1; + + encryption = encoding->content_encryption.head->data; + if (ne_get_uint(encryption->content_enc_algo, encryption_algo) != 0) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAlgo element found"); + return -1; + } + + if (*encryption_algo != CONTENT_ENC_ALGO_AES) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo used"); + return -1; + } + + if (!encryption->content_enc_aes_settings.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAESSettings element found"); + return -1; + } + + aes_settings = encryption->content_enc_aes_settings.head->data; + *encryption_mode = AES_SETTINGS_CIPHER_CTR; + ne_get_uint(aes_settings->aes_settings_cipher_mode, encryption_mode); + + if (*encryption_mode != AES_SETTINGS_CIPHER_CTR) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingsCipherMode used"); + return -1; + } + } } - - *np = p; - - return value; + return 1; } static int @@ -1284,13 +1364,14 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac int r; int64_t timecode, abs_timecode; nestegg_packet * pkt; - struct cluster * cluster; struct frame * f, * last; struct track_entry * entry; double track_scale; - uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total; + uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total, + encoding_type, encryption_algo, encryption_mode; unsigned int i, lacing, track; - size_t consumed = 0; + uint8_t signal_byte, keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN; + size_t consumed = 0, data_size, encryption_size; *data = NULL; @@ -1320,6 +1401,12 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac frames = 0; + /* Simple blocks have an explicit flag for if the contents a keyframes*/ + if (block_id == ID_SIMPLE_BLOCK) + keyframe = (flags & SIMPLE_BLOCK_FLAGS_KEYFRAME) == SIMPLE_BLOCK_FLAGS_KEYFRAME ? + NESTEGG_PACKET_HAS_KEYFRAME_TRUE : + NESTEGG_PACKET_HAS_KEYFRAME_FALSE; + /* Flags are different between Block and SimpleBlock, but lacing is encoded the same way. */ lacing = (flags & BLOCK_FLAGS_LACING) >> 1; @@ -1336,6 +1423,10 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac return r; consumed += 1; frames += 1; + break; + default: + assert(0); + return -1; } if (frames > 256) @@ -1365,6 +1456,9 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac if (r != 1) return r; break; + default: + assert(0); + return -1; } /* Sanity check unlaced frame sizes against total block size. */ @@ -1381,15 +1475,26 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac if (!entry) return -1; + r = ne_read_block_encryption(ctx, entry, &encoding_type, &encryption_algo, &encryption_mode); + if (r != 1) + return r; + + /* Encryption does not support lacing */ + if (lacing != LACING_NONE && encoding_type == NESTEGG_ENCODING_ENCRYPTION) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Encrypted blocks may not also be laced"); + return -1; + } + track_scale = 1.0; tc_scale = ne_get_timecode_scale(ctx); - - assert(ctx->segment.cluster.tail->id == ID_CLUSTER); - cluster = ctx->segment.cluster.tail->data; - if (ne_get_uint(cluster->timecode, &cluster_tc) != 0) + if (tc_scale == 0) return -1; + if (!ctx->read_cluster_timecode) + return -1; + cluster_tc = ctx->cluster_timecode; + abs_timecode = timecode + cluster_tc; if (abs_timecode < 0) return -1; @@ -1399,6 +1504,7 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac return -1; pkt->track = track; pkt->timecode = abs_timecode * tc_scale * track_scale; + pkt->keyframe = keyframe; ctx->log(ctx, NESTEGG_LOG_DEBUG, "%sblock t %lld pts %f f %llx frames: %llu", block_id == ID_BLOCK ? "" : "simple", pkt->track, pkt->timecode / 1e9, flags, frames); @@ -1414,19 +1520,69 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac nestegg_free_packet(pkt); return -1; } - f->data = ne_alloc(frame_sizes[i]); + /* Parse encryption */ + if (encoding_type == NESTEGG_ENCODING_ENCRYPTION) { + r = ne_io_read(ctx->io, &signal_byte, SIGNAL_BYTE_SIZE); + if (r != 1) { + free(f); + nestegg_free_packet(pkt); + return r; + } + f->frame_encryption = ne_alloc(sizeof(*f->frame_encryption)); + if (!f->frame_encryption) { + free(f); + nestegg_free_packet(pkt); + return -1; + } + f->frame_encryption->signal_byte = signal_byte; + if ((signal_byte & ENCRYPTED_BIT_MASK) == PACKET_ENCRYPTED) { + f->frame_encryption->iv = ne_alloc(IV_SIZE); + if (!f->frame_encryption->iv) { + free(f->frame_encryption); + free(f); + nestegg_free_packet(pkt); + return -1; + } + r = ne_io_read(ctx->io, f->frame_encryption->iv, IV_SIZE); + if (r != 1) { + free(f->frame_encryption->iv); + free(f->frame_encryption); + free(f); + nestegg_free_packet(pkt); + return r; + } + f->frame_encryption->length = IV_SIZE; + encryption_size = SIGNAL_BYTE_SIZE + IV_SIZE; + } else { + f->frame_encryption->iv = NULL; + f->frame_encryption->length = 0; + encryption_size = SIGNAL_BYTE_SIZE; + } + } else { + f->frame_encryption = NULL; + encryption_size = 0; + } + data_size = frame_sizes[i] - encryption_size; + /* Encryption parsed */ + f->data = ne_alloc(data_size); if (!f->data) { + if (f->frame_encryption) + free(f->frame_encryption->iv); + free(f->frame_encryption); free(f); nestegg_free_packet(pkt); return -1; } - f->length = frame_sizes[i]; - r = ne_io_read(ctx->io, f->data, frame_sizes[i]); + f->length = data_size; + r = ne_io_read(ctx->io, f->data, data_size); if (r != 1) { + if (f->frame_encryption) + free(f->frame_encryption->iv); + free(f->frame_encryption); free(f->data); free(f); nestegg_free_packet(pkt); - return -1; + return r; } if (!last) @@ -1442,63 +1598,7 @@ ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_pac } 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_size, struct block_additional ** pkt_block_additional) { int r; uint64_t id, size, data_size; @@ -1508,21 +1608,9 @@ ne_read_block_additions(nestegg * ctx, nestegg_packet * pkt) struct block_additional * block_additional; uint64_t add_id; - assert(pkt != NULL); - assert(pkt->block_additional == 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; + block_additions_end = ne_io_tell(ctx->io) + block_size; while (ne_io_tell(ctx->io) < block_additions_end) { add_id = 1; @@ -1530,14 +1618,16 @@ ne_read_block_additions(nestegg * ctx, nestegg_packet * pkt) has_data = 0; r = ne_read_element(ctx, &id, &size); if (r != 1) - return -1; + return r; 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); + r = ne_io_read_skip(ctx->io, size); + if (r != 1) + return r; continue; } @@ -1574,9 +1664,11 @@ ne_read_block_additions(nestegg * ctx, nestegg_packet * pkt) has_data = 1; data_size = size; - if (size != 0) { - data = ne_alloc(size); - r = ne_io_read(ctx->io, data, size); + if (data_size != 0 && data_size < LIMIT_FRAME) { + data = ne_alloc(data_size); + if (!data) + return -1; + r = ne_io_read(ctx->io, data, data_size); if (r != 1) { free(data); return r; @@ -1587,7 +1679,11 @@ ne_read_block_additions(nestegg * ctx, nestegg_packet * pkt) 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); + r = ne_io_read_skip(ctx->io, size); + if (r != 1) { + free(data); + return r; + } } } @@ -1598,11 +1694,11 @@ ne_read_block_additions(nestegg * ctx, nestegg_packet * pkt) } 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; @@ -1701,14 +1797,6 @@ ne_find_cue_point_for_tstamp(nestegg * ctx, struct ebml_list_node * cue_point, u return prev; } -static int -ne_is_suspend_element(uint64_t id) -{ - if (id == ID_SIMPLE_BLOCK || id == ID_BLOCK) - return 1; - return 0; -} - static void ne_null_log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...) { @@ -1753,7 +1841,7 @@ ne_init_cue_points(nestegg * ctx, int64_t max_offset) if (id != ID_CUES) return -1; - ctx->ancestor = NULL; + assert(ctx->ancestor == NULL); if (ne_ctx_push(ctx, ne_top_level_elements, ctx) < 0) return -1; if (ne_ctx_push(ctx, ne_segment_elements, &ctx->segment) < 0) @@ -1782,126 +1870,65 @@ ne_init_cue_points(nestegg * ctx, int64_t max_offset) } /* Three functions that implement the nestegg_io interface, operating on a - * sniff_buffer. */ -struct sniff_buffer { + io_buffer. */ +struct io_buffer { unsigned char const * buffer; size_t length; int64_t offset; }; static int -ne_buffer_read(void * buffer, size_t length, void * user_data) +ne_buffer_read(void * buffer, size_t length, void * userdata) { - struct sniff_buffer * sb = user_data; + struct io_buffer * iob = userdata; int rv = 1; - size_t available = sb->length - sb->offset; + size_t available = iob->length - iob->offset; if (available < length) return 0; - memcpy(buffer, sb->buffer + sb->offset, length); - sb->offset += length; + memcpy(buffer, iob->buffer + iob->offset, length); + iob->offset += length; return rv; } static int -ne_buffer_seek(int64_t offset, int whence, void * user_data) +ne_buffer_seek(int64_t offset, int whence, void * userdata) { - struct sniff_buffer * sb = user_data; - int64_t o = sb->offset; + struct io_buffer * iob = userdata; + int64_t o = iob->offset; switch(whence) { - case NESTEGG_SEEK_SET: - o = offset; - break; - case NESTEGG_SEEK_CUR: - o += offset; - break; - case NESTEGG_SEEK_END: - o = sb->length + offset; - break; + case NESTEGG_SEEK_SET: + o = offset; + break; + case NESTEGG_SEEK_CUR: + o += offset; + break; + case NESTEGG_SEEK_END: + o = iob->length + offset; + break; } - if (o < 0 || o > (int64_t) sb->length) + if (o < 0 || o > (int64_t) iob->length) return -1; - sb->offset = o; + iob->offset = o; return 0; } static int64_t -ne_buffer_tell(void * user_data) +ne_buffer_tell(void * userdata) { - struct sniff_buffer * sb = user_data; - return sb->offset; + struct io_buffer * iob = userdata; + return iob->offset; } static int -ne_match_webm(nestegg_io io, int64_t max_offset) +ne_context_new(nestegg ** context, nestegg_io io, nestegg_log callback) { - int r; - uint64_t id; - char * doctype; - nestegg * ctx; - - if (!(io.read && io.seek && io.tell)) - return -1; - - ctx = ne_alloc(sizeof(*ctx)); - if (!ctx) - return -1; - - ctx->io = ne_alloc(sizeof(*ctx->io)); - if (!ctx->io) { - nestegg_destroy(ctx); - return -1; - } - *ctx->io = io; - ctx->alloc_pool = ne_pool_init(); - if (!ctx->alloc_pool) { - nestegg_destroy(ctx); - return -1; - } - ctx->log = ne_null_log_callback; - - r = ne_peek_element(ctx, &id, NULL); - if (r != 1) { - nestegg_destroy(ctx); - return 0; - } - - 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". */ - ne_parse(ctx, NULL, max_offset); - - if (ne_get_string(ctx->ebml.doctype, &doctype) != 0 || - strcmp(doctype, "webm") != 0) { - nestegg_destroy(ctx); - return 0; - } - - nestegg_destroy(ctx); - - return 1; -} - -int -nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t max_offset) -{ - int r; - uint64_t id, version, docversion; - struct ebml_list_node * track; - char * doctype; nestegg * ctx; if (!(io.read && io.seek && io.tell)) @@ -1927,6 +1954,64 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma if (!ctx->log) ctx->log = ne_null_log_callback; + *context = ctx; + return 0; +} + +static int +ne_match_webm(nestegg_io io, int64_t max_offset) +{ + int r; + uint64_t id; + char * doctype; + nestegg * ctx; + + if (ne_context_new(&ctx, io, NULL) != 0) + return -1; + + r = ne_peek_element(ctx, &id, NULL); + if (r != 1) { + nestegg_destroy(ctx); + return 0; + } + + 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". */ + ne_parse(ctx, NULL, max_offset); + while (ctx->ancestor) + ne_ctx_pop(ctx); + + if (ne_get_string(ctx->ebml.doctype, &doctype) != 0 || + strcmp(doctype, "webm") != 0) { + nestegg_destroy(ctx); + return 0; + } + + nestegg_destroy(ctx); + + return 1; +} + +int +nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t max_offset) +{ + int r; + uint64_t id, version, docversion; + struct ebml_list_node * track; + char * doctype; + nestegg * ctx; + + if (ne_context_new(&ctx, io, callback) != 0) + return -1; + r = ne_peek_element(ctx, &id, NULL); if (r != 1) { nestegg_destroy(ctx); @@ -1943,6 +2028,8 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma ne_ctx_push(ctx, ne_top_level_elements, ctx); r = ne_parse(ctx, NULL, max_offset); + while (ctx->ancestor) + ne_ctx_pop(ctx); if (r != 1) { nestegg_destroy(ctx); @@ -1983,6 +2070,12 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma track = track->next; } + r = ne_ctx_save(ctx, &ctx->saved); + if (r != 0) { + nestegg_destroy(ctx); + return -1; + } + *context = ctx; return 0; @@ -1991,8 +2084,7 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma void nestegg_destroy(nestegg * ctx) { - while (ctx->ancestor) - ne_ctx_pop(ctx); + assert(ctx->ancestor == NULL); ne_pool_destroy(ctx->alloc_pool); free(ctx->io); free(ctx); @@ -2008,6 +2100,13 @@ nestegg_duration(nestegg * ctx, uint64_t * duration) return -1; tc_scale = ne_get_timecode_scale(ctx); + if (tc_scale == 0) + return -1; + + if (unscaled_duration != unscaled_duration || + unscaled_duration < 0 || unscaled_duration > (double) UINT64_MAX || + (uint64_t) unscaled_duration > UINT64_MAX / tc_scale) + return -1; *duration = (uint64_t) (unscaled_duration * tc_scale); return 0; @@ -2017,6 +2116,8 @@ int nestegg_tstamp_scale(nestegg * ctx, uint64_t * scale) { *scale = ne_get_timecode_scale(ctx); + if (*scale == 0) + return -1; return 0; } @@ -2059,6 +2160,8 @@ nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offse nestegg_track_count(ctx, &track_count); tc_scale = ne_get_timecode_scale(ctx); + if (tc_scale == 0) + return -1; while (cues_node && !range_obtained) { assert(cues_node->id == ID_CUE_POINT); @@ -2067,7 +2170,7 @@ nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offse while (cue_pos_node) { assert(cue_pos_node->id == ID_CUE_TRACK_POSITIONS); pos = cue_pos_node->data; - for (track = 0; track < track_count; track++) { + for (track = 0; track < track_count; ++track) { if (ne_get_uint(pos->track, &track_number) != 0) return -1; @@ -2078,12 +2181,12 @@ nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offse if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) return -1; if (cluster_count == cluster_num) { - *start_pos = ctx->segment_offset+seek_pos; + *start_pos = ctx->segment_offset + seek_pos; if (ne_get_uint(cue_point->time, &time) != 0) return -1; *tstamp = time * tc_scale; - } else if (cluster_count == cluster_num+1) { - *end_pos = (ctx->segment_offset+seek_pos)-1; + } else if (cluster_count == cluster_num + 1) { + *end_pos = ctx->segment_offset + seek_pos - 1; range_obtained = 1; break; } @@ -2103,22 +2206,16 @@ nestegg_offset_seek(nestegg * ctx, uint64_t offset) { int r; + if (offset > INT64_MAX) + return -1; + /* Seek and set up parser state for segment-level element (Cluster). */ r = ne_io_seek(ctx->io, offset, NESTEGG_SEEK_SET); if (r != 0) return -1; ctx->last_valid = 0; - while (ctx->ancestor) - ne_ctx_pop(ctx); - - ne_ctx_push(ctx, ne_top_level_elements, ctx); - ne_ctx_push(ctx, ne_segment_elements, &ctx->segment); - - ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cluster elements"); - r = ne_parse(ctx, NULL, -1); - if (r != 1) - return -1; + assert(ctx->ancestor == NULL); return 0; } @@ -2140,6 +2237,8 @@ nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp) } tc_scale = ne_get_timecode_scale(ctx); + if (tc_scale == 0) + return -1; cue_point = ne_find_cue_point_for_tstamp(ctx, ctx->segment.cues.cue_point.head, track, tc_scale, tstamp); @@ -2153,10 +2252,9 @@ nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp) if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) return -1; - /* Seek and set up parser state for segment-level element (Cluster). */ + /* Seek to (we assume) the start of a Cluster element. */ r = nestegg_offset_seek(ctx, ctx->segment_offset + seek_pos); - - if (!ne_is_suspend_element(ctx->last_id)) + if (r != 0) return -1; return 0; @@ -2181,7 +2279,7 @@ nestegg_track_type(nestegg * ctx, unsigned int track) if (type & TRACK_TYPE_AUDIO) return NESTEGG_TRACK_AUDIO; - return -1; + return NESTEGG_TRACK_UNKNOWN; } int @@ -2209,7 +2307,7 @@ nestegg_track_codec_id(nestegg * ctx, unsigned int track) if (strcmp(codec_id, TRACK_ID_OPUS) == 0) return NESTEGG_CODEC_OPUS; - return -1; + return NESTEGG_CODEC_UNKNOWN; } int @@ -2218,6 +2316,7 @@ nestegg_track_codec_data_count(nestegg * ctx, unsigned int track, { struct track_entry * entry; struct ebml_binary codec_private; + int codec_id; unsigned char * p; *count = 0; @@ -2226,7 +2325,14 @@ nestegg_track_codec_data_count(nestegg * ctx, unsigned int track, if (!entry) return -1; - if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS) + codec_id = nestegg_track_codec_id(ctx, track); + + if (codec_id == NESTEGG_CODEC_OPUS) { + *count = 1; + return 0; + } + + if (codec_id != NESTEGG_CODEC_VORBIS) return -1; if (ne_get_binary(entry->codec_private, &codec_private) != 0) @@ -2250,9 +2356,6 @@ nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, { struct track_entry * entry; struct ebml_binary codec_private; - uint64_t sizes[3], total; - unsigned char * p; - unsigned int count, i; *data = NULL; *length = 0; @@ -2261,37 +2364,60 @@ nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, if (!entry) return -1; - if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS - && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) + if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS && + nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) return -1; if (ne_get_binary(entry->codec_private, &codec_private) != 0) return -1; if (nestegg_track_codec_id(ctx, track) == NESTEGG_CODEC_VORBIS) { - p = codec_private.data; - count = *p++ + 1; + uint64_t count; + uint64_t sizes[3], total; + unsigned char * p; + unsigned int i; + int r; - if (count > 3) - return -1; + nestegg_io io; + struct io_buffer userdata; + userdata.buffer = codec_private.data; + userdata.length = codec_private.length; + userdata.offset = 0; - i = 0; - total = 0; - while (--count) { - sizes[i] = ne_xiph_lace_value(&p); - total += sizes[i]; - i += 1; - } - sizes[i] = codec_private.length - total - (p - codec_private.data); + io.read = ne_buffer_read; + io.seek = ne_buffer_seek; + io.tell = ne_buffer_tell; + io.userdata = &userdata; - for (i = 0; i < item; ++i) { - if (sizes[i] > LIMIT_FRAME) - return -1; - p += sizes[i]; - } - *data = p; - *length = sizes[item]; + total = 0; + + r = ne_read_uint(&io, &count, 1); + if (r != 1) + return r; + total += 1; + count += 1; + + if (count > 3) + return -1; + r = ne_read_xiph_lacing(&io, codec_private.length, &total, count, sizes); + if (r != 1) + return r; + + if (item >= count) + return -1; + + p = codec_private.data + total; + for (i = 0; i < item; ++i) { + p += sizes[i]; + } + assert((size_t) (p - codec_private.data) <= codec_private.length && + codec_private.length - (p - codec_private.data) >= sizes[item]); + *data = p; + *length = sizes[item]; } else { + if (item >= 1) + return -1; + *data = codec_private.data; *length = codec_private.length; } @@ -2398,6 +2524,108 @@ nestegg_track_audio_params(nestegg * ctx, unsigned int track, return 0; } +int +nestegg_track_encoding(nestegg * ctx, unsigned int track) +{ + struct track_entry * entry; + struct content_encoding * encoding; + uint64_t encoding_value; + + entry = ne_find_track_entry(ctx, track); + if (!entry) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found"); + return -1; + } + + if (!entry->content_encodings.content_encoding.head) { + /* Default encoding is compression */ + return NESTEGG_ENCODING_COMPRESSION; + } + + encoding = entry->content_encodings.content_encoding.head->data; + + encoding_value = NESTEGG_ENCODING_COMPRESSION; + ne_get_uint(encoding->content_encoding_type, &encoding_value); + if (encoding_value != NESTEGG_ENCODING_COMPRESSION && encoding_value != NESTEGG_ENCODING_ENCRYPTION) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Invalid ContentEncoding element found"); + return -1; + } + + return encoding_value; +} + +int +nestegg_track_content_enc_key_id(nestegg * ctx, unsigned int track, unsigned char const ** content_enc_key_id, + size_t * content_enc_key_id_length) +{ + struct track_entry * entry; + struct content_encoding * encoding; + struct content_encryption * encryption; + struct content_enc_aes_settings * aes_settings; + struct nestegg_encryption_params; + uint64_t value; + struct ebml_binary enc_key_id; + + entry = ne_find_track_entry(ctx, track); + if (!entry) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found"); + return -1; + } + + if (!entry->content_encodings.content_encoding.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncoding element found"); + return -1; + } + + encoding = entry->content_encodings.content_encoding.head->data; + + value = 0; + ne_get_uint(encoding->content_encoding_type, &value); + if (value != NESTEGG_ENCODING_ENCRYPTION) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncodingType found"); + return -1; + } + + if (!encoding->content_encryption.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncryption element found"); + return -1; + } + + encryption = encoding->content_encryption.head->data; + + value = 0; + ne_get_uint(encryption->content_enc_algo, &value); + + if (value != CONTENT_ENC_ALGO_AES) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo found"); + return -1; + } + + if (!encryption->content_enc_aes_settings.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAesSettings element found"); + return -1; + } + + aes_settings = encryption->content_enc_aes_settings.head->data; + value = AES_SETTINGS_CIPHER_CTR; + ne_get_uint(aes_settings->aes_settings_cipher_mode, &value); + + if (value != AES_SETTINGS_CIPHER_CTR) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingCipherMode used"); + return -1; + } + + if (ne_get_binary(encryption->content_enc_key_id, &enc_key_id) != 0) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Could not retrieve track ContentEncKeyId"); + return -1; + } + + *content_enc_key_id = enc_key_id.data; + *content_enc_key_id_length = enc_key_id.length; + + return 0; +} + int nestegg_track_default_duration(nestegg * ctx, unsigned int track, uint64_t * duration) @@ -2416,49 +2644,207 @@ nestegg_track_default_duration(nestegg * ctx, unsigned int track, return 0; } +int +nestegg_read_reset(nestegg * ctx) +{ + assert(ctx->ancestor == NULL); + return ne_ctx_restore(ctx, &ctx->saved); +} + 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); + assert(ctx->ancestor == NULL); + + /* Prepare for read_reset to resume parsing from this point upon error. */ + r = ne_ctx_save(ctx, &ctx->saved); + if (r != 0) + return -1; + + while (!read_block) { + r = ne_read_element(ctx, &id, &size); if (r != 1) return r; - /* Any DESC_FLAG_SUSPEND fields must be handled here. */ - if (ne_is_suspend_element(id)) { + switch (id) { + case ID_CLUSTER: { r = ne_read_element(ctx, &id, &size); if (r != 1) return r; - /* The only DESC_FLAG_SUSPEND fields are Blocks and SimpleBlocks, which we - handle directly. */ + /* Timecode must be the first element in a Cluster, per spec. */ + if (id != ID_TIMECODE) + return -1; + + r = ne_read_uint(ctx->io, &ctx->cluster_timecode, size); + if (r != 1) + return r; + ctx->read_cluster_timecode = 1; + break; + } + case ID_SIMPLE_BLOCK: 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; + read_block = 1; + break; + case ID_BLOCK_GROUP: { + 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; + int64_t reference_block = 0; + int read_reference_block = 0; + struct block_additional * block_additional = NULL; + uint64_t tc_scale; - r = ne_read_discard_padding(ctx, *pkt); - if (r != 1) - return r; + block_group_end = ne_io_tell(ctx->io) + size; - r = ne_read_block_additions(ctx, *pkt); - if (r != 1) - return r; + /* Read the entire BlockGroup manually. */ + while (ne_io_tell(ctx->io) < block_group_end) { + r = ne_read_element(ctx, &id, &size); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } - return r; + switch (id) { + case ID_BLOCK: { + r = ne_read_block(ctx, id, size, pkt); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } + + read_block = 1; + break; + } + case ID_BLOCK_DURATION: { + r = ne_read_uint(ctx->io, &block_duration, size); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } + tc_scale = ne_get_timecode_scale(ctx); + if (tc_scale == 0) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return -1; + } + block_duration *= tc_scale; + read_block_duration = 1; + break; + } + case ID_DISCARD_PADDING: { + r = ne_read_int(ctx->io, &discard_padding, size); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } + read_discard_padding = 1; + break; + } + case ID_BLOCK_ADDITIONS: { + /* There should only be one BlockAdditions; treat multiple as an error. */ + if (block_additional) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return -1; + } + r = ne_read_block_additions(ctx, size, &block_additional); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } + break; + } + case ID_REFERENCE_BLOCK: { + r = ne_read_int(ctx->io, &reference_block, size); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } + read_reference_block = 1; + 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, + "read_packet: unknown element %llx in BlockGroup", id); + r = ne_io_read_skip(ctx->io, size); + if (r != 1) { + free(block_additional); + if (*pkt) { + nestegg_free_packet(*pkt); + *pkt = NULL; + } + return r; + } + } + } + + 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)->reference_block = reference_block; + (*pkt)->read_reference_block = read_reference_block; + (*pkt)->block_additional = block_additional; + if ((*pkt)->read_reference_block) + /* If a packet has a reference block it contains + predictive frames and no keyframes */ + (*pkt)->keyframe = NESTEGG_PACKET_HAS_KEYFRAME_FALSE; + } else { + free(block_additional); + } + break; + } + default: + ctx->log(ctx, NESTEGG_LOG_DEBUG, "read_packet: unknown element %llx", id); + r = ne_io_read_skip(ctx->io, size); + if (r != 1) + return r; } - - r = ne_parse(ctx, NULL, -1); - if (r != 1) - return r; } return 1; @@ -2473,6 +2859,10 @@ nestegg_free_packet(nestegg_packet * pkt) while (pkt->frame) { frame = pkt->frame; pkt->frame = frame->next; + if (frame->frame_encryption) { + free(frame->frame_encryption->iv); + } + free(frame->frame_encryption); free(frame->data); free(frame); } @@ -2484,7 +2874,13 @@ nestegg_free_packet(nestegg_packet * pkt) free(block_additional); } - free(pkt); + free(pkt); +} + +int +nestegg_packet_has_keyframe(nestegg_packet * pkt) +{ + return pkt->keyframe; } int @@ -2504,6 +2900,8 @@ nestegg_packet_tstamp(nestegg_packet * pkt, uint64_t * tstamp) int nestegg_packet_duration(nestegg_packet * pkt, uint64_t * duration) { + if (!pkt->read_duration) + return -1; *duration = pkt->duration; return 0; } @@ -2511,10 +2909,21 @@ nestegg_packet_duration(nestegg_packet * pkt, uint64_t * duration) 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_reference_block(nestegg_packet * pkt, int64_t * reference_block) +{ + if (!pkt->read_reference_block) + return -1; + *reference_block = pkt->reference_block; + return 0; +} + int nestegg_packet_count(nestegg_packet * pkt, unsigned int * count) { @@ -2574,32 +2983,70 @@ nestegg_packet_additional_data(nestegg_packet * pkt, unsigned int id, return -1; } +int +nestegg_packet_encryption(nestegg_packet * pkt) +{ + struct frame * f = pkt->frame; + unsigned char encrypted_bit; + + if (!f->frame_encryption) + return NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE; + + /* Should never have parsed blocks with both encryption and lacing */ + assert(f->next == NULL); + + encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK; + + if (encrypted_bit != PACKET_ENCRYPTED) + return NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED; + + return NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED; +} + +int +nestegg_packet_iv(nestegg_packet * pkt, unsigned char const ** iv, size_t * length) +{ + struct frame * f = pkt->frame; + unsigned char encrypted_bit; + + *iv = NULL; + *length = 0; + if (!f->frame_encryption) + return -1; + + /* Should never have parsed blocks with both encryption and lacing */ + assert(f->next == NULL); + + encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK; + + if (encrypted_bit != PACKET_ENCRYPTED) + return 0; + + *iv = f->frame_encryption->iv; + *length = f->frame_encryption->length; + return 0; +} + 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); + ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); } int nestegg_sniff(unsigned char const * buffer, size_t length) { nestegg_io io; - struct sniff_buffer user_data; + struct io_buffer userdata; - user_data.buffer = buffer; - user_data.length = length; - user_data.offset = 0; + userdata.buffer = buffer; + userdata.length = length; + userdata.offset = 0; io.read = ne_buffer_read; io.seek = ne_buffer_seek; io.tell = ne_buffer_tell; - io.userdata = &user_data; + io.userdata = &userdata; return ne_match_webm(io, length); } - -void -nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)) -{ - halloc_allocator = realloc_func; -} diff --git a/src/nestegg.h b/src/nestegg.h index 589ef4d..d91483b 100644 --- a/src/nestegg.h +++ b/src/nestegg.h @@ -7,6 +7,7 @@ #if !defined(NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79) #define NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79 +#include #include #if defined(__cplusplus) @@ -62,13 +63,15 @@ extern "C" { /** @file The libnestegg C API. */ -#define NESTEGG_TRACK_VIDEO 0 /**< Track is of type video. */ -#define NESTEGG_TRACK_AUDIO 1 /**< Track is of type audio. */ +#define NESTEGG_TRACK_VIDEO 0 /**< Track is of type video. */ +#define NESTEGG_TRACK_AUDIO 1 /**< Track is of type audio. */ +#define NESTEGG_TRACK_UNKNOWN INT_MAX /**< Track is of type unknown. */ -#define NESTEGG_CODEC_VP8 0 /**< Track uses Google On2 VP8 codec. */ -#define NESTEGG_CODEC_VORBIS 1 /**< Track uses Xiph Vorbis codec. */ -#define NESTEGG_CODEC_VP9 2 /**< Track uses Google On2 VP9 codec. */ -#define NESTEGG_CODEC_OPUS 3 /**< Track uses Xiph Opus codec. */ +#define NESTEGG_CODEC_VP8 0 /**< Track uses Google On2 VP8 codec. */ +#define NESTEGG_CODEC_VORBIS 1 /**< Track uses Xiph Vorbis codec. */ +#define NESTEGG_CODEC_VP9 2 /**< Track uses Google On2 VP9 codec. */ +#define NESTEGG_CODEC_OPUS 3 /**< Track uses Xiph Opus codec. */ +#define NESTEGG_CODEC_UNKNOWN INT_MAX /**< Track uses unknown codec. */ #define NESTEGG_VIDEO_MONO 0 /**< Track is mono video. */ #define NESTEGG_VIDEO_STEREO_LEFT_RIGHT 1 /**< Track is side-by-side stereo video. Left first. */ @@ -86,6 +89,17 @@ extern "C" { #define NESTEGG_LOG_ERROR 1000 /**< Error level log message. */ #define NESTEGG_LOG_CRITICAL 10000 /**< Critical level log message. */ +#define NESTEGG_ENCODING_COMPRESSION 0 /**< Content encoding type is compression. */ +#define NESTEGG_ENCODING_ENCRYPTION 1 /**< Content encoding type is encryption. */ + +#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE 0 /**< Packet does not have signal byte */ +#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED 1 /**< Packet has signal byte and is unencrypted */ +#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED 2 /**< Packet has signal byte and is encrypted */ + +#define NESTEGG_PACKET_HAS_KEYFRAME_FALSE 0 /**< Packet contains only keyframes. */ +#define NESTEGG_PACKET_HAS_KEYFRAME_TRUE 1 /**< Packet does not contain any keyframes */ +#define NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN 2 /**< Packet may or may not contain keyframes */ + typedef struct nestegg nestegg; /**< Opaque handle referencing the stream state. */ typedef struct nestegg_packet nestegg_packet; /**< Opaque handle referencing a packet of data. */ @@ -171,8 +185,8 @@ void nestegg_destroy(nestegg * context); int nestegg_duration(nestegg * context, uint64_t * duration); /** Query the tstamp scale of the media stream in nanoseconds. - Timecodes presented by nestegg have been scaled by this value - before presentation to the caller. + @note Timecodes presented by nestegg have been scaled by this value + before presentation to the caller. @param context Stream context initialized by #nestegg_init. @param scale Storage for the queried scale factor. @retval 0 Success. @@ -201,8 +215,8 @@ int nestegg_get_cue_point(nestegg * context, unsigned int cluster_num, int64_t * end_pos, uint64_t * tstamp); /** Seek to @a offset. Stream will seek directly to offset. - Should be used to seek to the start of a resync point, i.e. cluster; the - parser will not be able to understand other offsets. + Must be used to seek to the start of a cluster; the parser will not be + able to understand other offsets. @param context Stream context initialized by #nestegg_init. @param offset Absolute offset in bytes. @retval 0 Success. @@ -222,16 +236,20 @@ int nestegg_track_seek(nestegg * context, unsigned int track, uint64_t tstamp); /** Query the type specified by @a track. @param context Stream context initialized by #nestegg_init. @param track Zero based track number. - @retval #NESTEGG_TRACK_VIDEO Track type is video. - @retval #NESTEGG_TRACK_AUDIO Track type is audio. + @retval #NESTEGG_TRACK_VIDEO Track type is video. + @retval #NESTEGG_TRACK_AUDIO Track type is audio. + @retval #NESTEGG_TRACK_UNKNOWN Track type is unknown. @retval -1 Error. */ int nestegg_track_type(nestegg * context, unsigned int track); /** Query the codec ID specified by @a track. @param context Stream context initialized by #nestegg_init. @param track Zero based track number. - @retval #NESTEGG_CODEC_VP8 Track codec is VP8. - @retval #NESTEGG_CODEC_VORBIS Track codec is Vorbis. + @retval #NESTEGG_CODEC_VP8 Track codec is VP8. + @retval #NESTEGG_CODEC_VP9 Track codec is VP9. + @retval #NESTEGG_CODEC_VORBIS Track codec is Vorbis. + @retval #NESTEGG_CODEC_OPUS Track codec is Opus. + @retval #NESTEGG_CODEC_UNKNOWN Track codec is unknown. @retval -1 Error. */ int nestegg_track_codec_id(nestegg * context, unsigned int track); @@ -277,6 +295,28 @@ int nestegg_track_video_params(nestegg * context, unsigned int track, int nestegg_track_audio_params(nestegg * context, unsigned int track, nestegg_audio_params * params); +/** Query the encoding status for @a track. If a track has multiple encodings + the first will be returned. + @param context Stream context initialized by #nestegg_init. + @param track Zero based track number. + @retval #NESTEGG_ENCODING_COMPRESSION The track is compressed, but not encrypted. + @retval #NESTEGG_ENCODING_ENCRYPTION The track is encrypted and compressed. + @retval -1 Error. */ +int nestegg_track_encoding(nestegg * context, unsigned int track); + +/** Query the ContentEncKeyId for @a track. Will return an error if the track + in not encrypted, or is not recognized. + @param context Stream context initialized by #nestegg_init. + @param track Zero based track number. + @param content_enc_key_id Storage for queried id. The content encryption key used. + Owned by nestegg and will be freed separately. + @param content_enc_key_id_length Length of the queried ContentEncKeyId in bytes. + @retval 0 Success. + @retval -1 Error. */ +int nestegg_track_content_enc_key_id(nestegg * context, unsigned int track, + unsigned char const ** content_enc_key_id, + size_t * content_enc_key_id_length); + /** Query the default frame duration for @a track. For a video track, this is typically the inverse of the video frame rate. @param context Stream context initialized by #nestegg_init. @@ -287,6 +327,12 @@ int nestegg_track_audio_params(nestegg * context, unsigned int track, int nestegg_track_default_duration(nestegg * context, unsigned int track, uint64_t * duration); +/** Reset parser state to the last valid state before nestegg_read_packet failed. + @param context Stream context initialized by #nestegg_init. + @retval 0 Success. + @retval -1 Error. */ +int nestegg_read_reset(nestegg * context); + /** Read a packet of media data. A packet consists of one or more chunks of data associated with a single track. nestegg_read_packet should be called in a loop while the return value is 1 to drive the stream parser @@ -302,6 +348,14 @@ int nestegg_read_packet(nestegg * context, nestegg_packet ** packet); @param packet #nestegg_packet to be freed. @see nestegg_read_packet */ void nestegg_free_packet(nestegg_packet * packet); +/** Query the keyframe status for a given packet. + @param packet Packet initialized by #nestegg_read_packet. + @retval #NESTEGG_PACKET_HAS_KEYFRAME_FALSE Packet contains no keyframes. + @retval #NESTEGG_PACKET_HAS_KEYFRAME_TRUE Packet contains keyframes. + @retval #NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN Unknown packet keyframe content. + @retval -1 Error. */ +int nestegg_packet_has_keyframe(nestegg_packet * packet); + /** Query the track number of @a packet. @param packet Packet initialized by #nestegg_read_packet. @param track Storage for the queried zero based track index. @@ -325,7 +379,7 @@ 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); @@ -362,28 +416,50 @@ int nestegg_packet_additional_data(nestegg_packet * packet, unsigned int id, int nestegg_packet_discard_padding(nestegg_packet * packet, int64_t * discard_padding); +/** Query if a packet is encrypted. + @param packet Packet initialized by #nestegg_read_packet. + @retval #NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE No signal byte, encryption + information not read from packet. + @retval #NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED Encrypted bit not + set, encryption information not read from packet. + @retval #NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED Encrypted bit set, + encryption infomation read from packet. + @retval -1 Error.*/ +int nestegg_packet_encryption(nestegg_packet * packet); + +/** Query the IV for an encrypted packet. Expects a packet from an encrypted + track, and will return error if given a packet that has no signal btye. + @param packet Packet initialized by #nestegg_read_packet. + @param iv Storage for queried iv. + @param length Length of returned iv, may be 0. + The data is owned by the #nestegg_packet packet. + @retval 0 Success. + @retval -1 Error. + */ +int nestegg_packet_iv(nestegg_packet * packet, unsigned char const ** iv, + size_t * length); + +/** Returns reference_block given packet + @param packet Packet initialized by #nestegg_read_packet. + @param reference_block pointer to store reference block in. + @retval 0 Success. + @retval -1 Error. */ +int nestegg_packet_reference_block(nestegg_packet * packet, + int64_t * reference_block); + /** Query the presence of cues. @param context Stream context initialized by #nestegg_init. @retval 0 The media has no cues. @retval 1 The media has cues. */ int nestegg_has_cues(nestegg * context); -/** - * Try to determine if the buffer looks like the beginning of a WebM file. - * - * @param buffer A buffer containing the beginning of a media file. - * @param length The size of the buffer. - * @retval 0 The file is not a WebM file. - * @retval 1 The file is a WebM file. */ +/** Try to determine if the buffer looks like the beginning of a WebM file. + @param buffer A buffer containing the beginning of a media file. + @param length The size of the buffer. + @retval 0 The file is not a WebM file. + @retval 1 The file is a WebM file. */ int nestegg_sniff(unsigned char const * buffer, size_t length); -/** - * Set the underlying allocation function for library allocations. - * - * @param realloc_func The desired function. - */ -void nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)); - #if defined(__cplusplus) } #endif