ref: bb3b9aa93f5f699c986d8ac5e6d6f3ceffab41c9
parent: a0f0655b5e5739f1acbefb4e4b4fe1f778ecea45
author: James Zern <jzern@google.com>
date: Thu Feb 20 15:26:53 EST 2014
move nestegg to third_party Change-Id: Idf58109195a88dec66c5e1ea6a51c61e6c659ff1
--- a/examples.mk
+++ b/examples.mk
@@ -26,13 +26,13 @@
vpxdec.SRCS += tools_common.c tools_common.h
vpxdec.SRCS += webmdec.c webmdec.h
vpxdec.SRCS += y4menc.c y4menc.h
-vpxdec.SRCS += nestegg/halloc/halloc.h
-vpxdec.SRCS += nestegg/halloc/src/align.h
-vpxdec.SRCS += nestegg/halloc/src/halloc.c
-vpxdec.SRCS += nestegg/halloc/src/hlist.h
-vpxdec.SRCS += nestegg/halloc/src/macros.h
-vpxdec.SRCS += nestegg/include/nestegg/nestegg.h
-vpxdec.SRCS += nestegg/src/nestegg.c
+vpxdec.SRCS += third_party/nestegg/halloc/halloc.h
+vpxdec.SRCS += third_party/nestegg/halloc/src/align.h
+vpxdec.SRCS += third_party/nestegg/halloc/src/halloc.c
+vpxdec.SRCS += third_party/nestegg/halloc/src/hlist.h
+vpxdec.SRCS += third_party/nestegg/halloc/src/macros.h
+vpxdec.SRCS += third_party/nestegg/include/nestegg/nestegg.h
+vpxdec.SRCS += third_party/nestegg/src/nestegg.c
vpxdec.SRCS += $(LIBYUV_SRCS)
vpxdec.GUID = BA5FE66F-38DD-E034-F542-B1578C5FB950
vpxdec.DESCRIPTION = Full featured decoder
--- a/nestegg/0001-include-paths.diff
+++ /dev/null
@@ -1,41 +1,0 @@
-diff --git a/nestegg/halloc/src/halloc.c b/nestegg/halloc/src/halloc.c
-index 5758fc0..837b3ff 100644
---- a/nestegg/halloc/src/halloc.c
-+++ b/nestegg/halloc/src/halloc.c
-@@ -15,7 +15,7 @@
- #include <stdlib.h> /* realloc */
- #include <string.h> /* memset & co */
-
--#include "halloc.h"
-+#include "nestegg/halloc/halloc.h"
- #include "align.h"
- #include "hlist.h"
-
-diff --git a/nestegg/include/nestegg/nestegg.h b/nestegg/include/nestegg/nestegg.h
-index ff13728..c18d1d3 100644
---- a/nestegg/include/nestegg/nestegg.h
-+++ b/nestegg/include/nestegg/nestegg.h
-@@ -7,7 +7,7 @@
- #if !defined(NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79)
- #define NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79
-
--#include <nestegg/nestegg-stdint.h>
-+#include "vpx/vpx_integer.h"
-
- #if defined(__cplusplus)
- extern "C" {
-diff --git a/nestegg/src/nestegg.c b/nestegg/src/nestegg.c
-index daf1eed..4fb10e7 100644
---- a/nestegg/src/nestegg.c
-+++ b/nestegg/src/nestegg.c
-@@ -8,8 +8,8 @@
- #include <stdlib.h>
- #include <string.h>
-
--#include "halloc.h"
--#include "nestegg/nestegg.h"
-+#include "nestegg/halloc/halloc.h"
-+#include "nestegg/include/nestegg/nestegg.h"
-
- /* EBML Elements */
- #define ID_EBML 0x1a45dfa3
--- a/nestegg/0002-ne_read_simple-uninitialized_variable.diff
+++ /dev/null
@@ -1,21 +1,0 @@
-diff --git a/nestegg/src/nestegg.c b/nestegg/src/nestegg.c
-index 4fb10e7..b6bc460 100644
---- a/nestegg/src/nestegg.c
-+++ b/nestegg/src/nestegg.c
-@@ -934,7 +934,7 @@ static int
- ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
- {
- struct ebml_type * storage;
-- int r;
-+ int r = 0;
-
- storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset);
-
-@@ -968,7 +968,6 @@ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
- case TYPE_MASTER:
- case TYPE_UNKNOWN:
- assert(0);
-- r = 0;
- break;
- }
-
--- a/nestegg/AUTHORS
+++ /dev/null
@@ -1,3 +1,0 @@
-Matthew Gregan <kinetik@flim.org>
-Steve Workman <sjhworkman@gmail.com>
-Paul Adenot <paul@paul.cx>
--- a/nestegg/INSTALL
+++ /dev/null
@@ -1,8 +1,0 @@
-Build instructions for libnestegg
-=================================
-
-0. Change directory into the source directory.
-1. Run |autoreconf --install| to generate configure.
-2. Run |./configure| to configure the build.
-3. Run |make| to build.
-4. Run |make check| to run the test suite.
--- a/nestegg/LICENSE
+++ /dev/null
@@ -1,13 +1,0 @@
-Copyright © 2010 Mozilla Foundation
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- a/nestegg/README
+++ /dev/null
@@ -1,6 +1,0 @@
-See INSTALL for build instructions.
-
-Licensed under an ISC-style license. See LICENSE for details.
-
-The source under the halloc/ directory is licensed under a BSD license. See
-halloc/halloc.h for details.
--- a/nestegg/README.webm
+++ /dev/null
@@ -1,16 +1,0 @@
-URL: https://github.com/kinetiknz/nestegg.git
-Version: f46223191d8116a36bf299b5b9793fcb798417b1
-License: ISC-style
-License File: LICENSE
-
-Description:
-The source under the halloc/ directory is licensed under a BSD license. See
-halloc/halloc.h for details.
-
-Local Modifications:
-- delete unnecessary docs and build files
-- nestegg/0001-include-paths.diff
- include path modifications for the libvpx build system
-- 0002-ne_read_simple-uninitialized_variable.diff
- fixes:
-nestegg.c|975 col 6| warning: ‘r’ may be used uninitialized in this function [-Wuninitialized]
--- a/nestegg/TODO
+++ /dev/null
@@ -1,21 +1,0 @@
-- Document when read, seek, tell callbacks are used.
-- Add an automated testsuite.
-- Test (and fix, if necessary) support for unknown sizes.
-- Test (and fix, if necessary) support for large files.
-- Read past unknown elements rather than seeking.
-- Try to handle unknown elements with unknown sizes.
-- Formalize handling of default element values.
-- Try to resynchronize stream when read_block fails so that failure to parse
- a single block can be treated as non-fatal.
-- Make logging more useful to API users.
-- Avoid reparsing Cues and ignore any SeekHead at end of file.
-- Optionally build a Cue index as Clusters are parsed.
-- Support seeking without Cues.
-- Avoid building a list of Clusters as they are parsed and retain only the
- last one parsed.
-- Add an asynchronous error code to struct nestegg and ensure that API calls
- continue to fail safely one a fatal error has been returned.
-- Modify parser/data structures to provide a clean separation. Perhaps the
- parser should return a generic tree of nodes that a second pass uses to
- initialize the main data structures.
-- Use pool allocator for all allocations.
--- a/nestegg/halloc/README
+++ /dev/null
@@ -1,45 +1,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 <string.h> 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.
-
--- a/nestegg/halloc/halloc.h
+++ /dev/null
@@ -1,43 +1,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 <stddef.h> /* 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
-
--- a/nestegg/halloc/src/align.h
+++ /dev/null
@@ -1,36 +1,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_
-
-/*
- * 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
-
--- a/nestegg/halloc/src/halloc.c
+++ /dev/null
@@ -1,254 +1,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 <stdlib.h> /* realloc */
-#include <string.h> /* memset & co */
-
-#include "nestegg/halloc/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
- */
-static void _set_allocator(void);
-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)
- {
- _set_allocator();
- 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
- */
-static void _set_allocator(void)
-{
- 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.
- */
- allocator = realloc;
- if (! (p = malloc(1)))
- /* hmm */
- return;
-
- if ((p = realloc(p, 0)))
- {
- /* realloc cannot be used as free() */
- allocator = _realloc;
- free(p);
- }
-}
-
-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);
- }
-}
-
--- a/nestegg/halloc/src/hlist.h
+++ /dev/null
@@ -1,136 +1,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 <assert.h>
-#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
-
--- a/nestegg/halloc/src/macros.h
+++ /dev/null
@@ -1,36 +1,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 <stddef.h> /* 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
-
--- a/nestegg/include/nestegg/nestegg.h
+++ /dev/null
@@ -1,353 +1,0 @@
-/*
- * Copyright © 2010 Mozilla Foundation
- *
- * This program is made available under an ISC-style license. See the
- * accompanying file LICENSE for details.
- */
-#if !defined(NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79)
-#define NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79
-
-#include "vpx/vpx_integer.h"
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-/** @mainpage
-
- @section intro Introduction
-
- This is the documentation for the <tt>libnestegg</tt> C API.
- <tt>libnestegg</tt> is a demultiplexing library for <a
- href="http://www.webmproject.org/code/specs/container/">WebM</a>
- media files.
-
- @section example Example code
-
- @code
- nestegg * demux_ctx;
- nestegg_init(&demux_ctx, io, NULL);
-
- nestegg_packet * pkt;
- while ((r = nestegg_read_packet(demux_ctx, &pkt)) > 0) {
- unsigned int track;
-
- nestegg_packet_track(pkt, &track);
-
- // This example decodes the first track only.
- if (track == 0) {
- unsigned int chunk, chunks;
-
- nestegg_packet_count(pkt, &chunks);
-
- // Decode each chunk of data.
- for (chunk = 0; chunk < chunks; ++chunk) {
- unsigned char * data;
- size_t data_size;
-
- nestegg_packet_data(pkt, chunk, &data, &data_size);
-
- example_codec_decode(codec_ctx, data, data_size);
- }
- }
-
- nestegg_free_packet(pkt);
- }
-
- nestegg_destroy(demux_ctx);
- @endcode
-*/
-
-
-/** @file
- The <tt>libnestegg</tt> C API. */
-
-#define NESTEGG_TRACK_VIDEO 0 /**< Track is of type video. */
-#define NESTEGG_TRACK_AUDIO 1 /**< Track is of type audio. */
-
-#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_VIDEO_MONO 0 /**< Track is mono video. */
-#define NESTEGG_VIDEO_STEREO_LEFT_RIGHT 1 /**< Track is side-by-side stereo video. Left first. */
-#define NESTEGG_VIDEO_STEREO_BOTTOM_TOP 2 /**< Track is top-bottom stereo video. Right first. */
-#define NESTEGG_VIDEO_STEREO_TOP_BOTTOM 3 /**< Track is top-bottom stereo video. Left first. */
-#define NESTEGG_VIDEO_STEREO_RIGHT_LEFT 11 /**< Track is side-by-side stereo video. Right first. */
-
-#define NESTEGG_SEEK_SET 0 /**< Seek offset relative to beginning of stream. */
-#define NESTEGG_SEEK_CUR 1 /**< Seek offset relative to current position in stream. */
-#define NESTEGG_SEEK_END 2 /**< Seek offset relative to end of stream. */
-
-#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. */
-
-/** 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.
- @retval 0 End of stream.
- @retval -1 Error. */
- int (* read)(void * buffer, size_t length, void * userdata);
-
- /** User supplied seek callback.
- @param offset Offset within the stream to seek to.
- @param whence Seek direction. One of #NESTEGG_SEEK_SET,
- #NESTEGG_SEEK_CUR, or #NESTEGG_SEEK_END.
- @param userdata The #userdata supplied by the user.
- @retval 0 Seek succeeded.
- @retval -1 Error. */
- int (* seek)(int64_t offset, int whence, void * userdata);
-
- /** User supplied tell callback.
- @param userdata The #userdata supplied by the user.
- @returns Current position within the stream.
- @retval -1 Error. */
- int64_t (* tell)(void * userdata);
-
- /** User supplied pointer to be passed to the IO callbacks. */
- void * userdata;
-} nestegg_io;
-
-/** Parameters specific to a video track. */
-typedef struct {
- unsigned int stereo_mode; /**< Video mode. One of #NESTEGG_VIDEO_MONO,
- #NESTEGG_VIDEO_STEREO_LEFT_RIGHT,
- #NESTEGG_VIDEO_STEREO_BOTTOM_TOP, or
- #NESTEGG_VIDEO_STEREO_TOP_BOTTOM. */
- unsigned int width; /**< Width of the video frame in pixels. */
- unsigned int height; /**< Height of the video frame in pixels. */
- unsigned int display_width; /**< Display width of the video frame in pixels. */
- unsigned int display_height; /**< Display height of the video frame in pixels. */
- unsigned int crop_bottom; /**< Pixels to crop from the bottom of the frame. */
- unsigned int crop_top; /**< Pixels to crop from the top of the frame. */
- unsigned int crop_left; /**< Pixels to crop from the left of the frame. */
- unsigned int crop_right; /**< Pixels to crop from the right of the frame. */
-} nestegg_video_params;
-
-/** Parameters specific to an audio track. */
-typedef struct {
- double rate; /**< Sampling rate in Hz. */
- unsigned int channels; /**< Number of audio channels. */
- unsigned int depth; /**< Bits per sample. */
- uint64_t codec_delay; /**< Nanoseconds that must be discarded from the start. */
- uint64_t seek_preroll;/**< Nanoseconds that must be discarded after a seek. */
-} nestegg_audio_params;
-
-/** Logging callback function pointer. */
-typedef void (* nestegg_log)(nestegg * context, unsigned int severity, char const * format, ...);
-
-/** Initialize a nestegg context. During initialization the parser will
- read forward in the stream processing all elements until the first
- block of media is reached. All track metadata has been processed at this point.
- @param context Storage for the new nestegg context. @see nestegg_destroy
- @param io User supplied IO context.
- @param callback Optional logging callback function pointer. May be NULL.
- @param max_offset Optional maximum offset to be read. Set -1 to ignore.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t max_offset);
-
-/** Destroy a nestegg context and free associated memory.
- @param context #nestegg context to be freed. @see nestegg_init */
-void nestegg_destroy(nestegg * context);
-
-/** Query the duration of the media stream in nanoseconds.
- @param context Stream context initialized by #nestegg_init.
- @param duration Storage for the queried duration.
- @retval 0 Success.
- @retval -1 Error. */
-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.
- @param context Stream context initialized by #nestegg_init.
- @param scale Storage for the queried scale factor.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_tstamp_scale(nestegg * context, uint64_t * scale);
-
-/** Query the number of tracks in the media stream.
- @param context Stream context initialized by #nestegg_init.
- @param tracks Storage for the queried track count.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_track_count(nestegg * context, unsigned int * tracks);
-
-/** Query the start and end offset for a particular cluster.
- @param context Stream context initialized by #nestegg_init.
- @param cluster_num Zero-based cluster number; order they appear in cues.
- @param max_offset Optional maximum offset to be read. Set -1 to ignore.
- @param start_pos Starting offset of the cluster. -1 means non-existant.
- @param end_pos Starting offset of the cluster. -1 means non-existant or
- final cluster.
- @param tstamp Starting timestamp of the cluster.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_get_cue_point(nestegg * context, unsigned int cluster_num,
- int64_t max_offset, int64_t * start_pos,
- 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.
- @param context Stream context initialized by #nestegg_init.
- @param offset Absolute offset in bytes.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_offset_seek(nestegg * context, uint64_t offset);
-
-/** Seek @a track to @a tstamp. Stream seek will terminate at the earliest
- key point in the stream at or before @a tstamp. Other tracks in the
- stream will output packets with unspecified but nearby timestamps.
- @param context Stream context initialized by #nestegg_init.
- @param track Zero based track number.
- @param tstamp Absolute timestamp in nanoseconds.
- @retval 0 Success.
- @retval -1 Error. */
-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 -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 -1 Error. */
-int nestegg_track_codec_id(nestegg * context, unsigned int track);
-
-/** Query the number of codec initialization chunks for @a track. Each
- chunk of data should be passed to the codec initialization functions in
- the order returned.
- @param context Stream context initialized by #nestegg_init.
- @param track Zero based track number.
- @param count Storage for the queried chunk count.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_track_codec_data_count(nestegg * context, unsigned int track,
- unsigned int * count);
-
-/** Get a pointer to chunk number @a item of codec initialization data for
- @a track.
- @param context Stream context initialized by #nestegg_init.
- @param track Zero based track number.
- @param item Zero based chunk item number.
- @param data Storage for the queried data pointer.
- The data is owned by the #nestegg context.
- @param length Storage for the queried data size.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_track_codec_data(nestegg * context, unsigned int track, unsigned int item,
- unsigned char ** data, size_t * length);
-
-/** Query the video parameters specified by @a track.
- @param context Stream context initialized by #nestegg_init.
- @param track Zero based track number.
- @param params Storage for the queried video parameters.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_track_video_params(nestegg * context, unsigned int track,
- nestegg_video_params * params);
-
-/** Query the audio parameters specified by @a track.
- @param context Stream context initialized by #nestegg_init.
- @param track Zero based track number.
- @param params Storage for the queried audio parameters.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_track_audio_params(nestegg * context, unsigned int track,
- nestegg_audio_params * params);
-
-/** 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
- forward. @see nestegg_free_packet
- @param context Context returned by #nestegg_init.
- @param packet Storage for the returned nestegg_packet.
- @retval 1 Additional packets may be read in subsequent calls.
- @retval 0 End of stream.
- @retval -1 Error. */
-int nestegg_read_packet(nestegg * context, nestegg_packet ** packet);
-
-/** Destroy a nestegg_packet and free associated memory.
- @param packet #nestegg_packet to be freed. @see nestegg_read_packet */
-void nestegg_free_packet(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.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_packet_track(nestegg_packet * packet, unsigned int * track);
-
-/** Query the time stamp in nanoseconds of @a packet.
- @param packet Packet initialized by #nestegg_read_packet.
- @param tstamp Storage for the queried timestamp in nanoseconds.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_packet_tstamp(nestegg_packet * packet, uint64_t * tstamp);
-
-/** 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.
- @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.
- The data is owned by the #nestegg_packet packet.
- @param length Storage for the queried data size.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_packet_data(nestegg_packet * packet, unsigned int item,
- unsigned char ** data, size_t * length);
-
-/** Returns discard_padding for given packet
- @param packet Packet initialized by #nestegg_read_packet.
- @param discard_padding pointer to store discard padding in.
- @retval 0 Success.
- @retval -1 Error. */
-int nestegg_packet_discard_padding(nestegg_packet * packet,
- int64_t * discard_padding);
-
-/** 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. */
-int nestegg_sniff(unsigned char const * buffer, size_t length);
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif /* NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79 */
--- a/nestegg/src/nestegg.c
+++ /dev/null
@@ -1,2306 +1,0 @@
-/*
- * Copyright © 2010 Mozilla Foundation
- *
- * This program is made available under an ISC-style license. See the
- * accompanying file LICENSE for details.
- */
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "nestegg/halloc/halloc.h"
-#include "nestegg/include/nestegg/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
-
-/* Global Elements */
-#define ID_VOID 0xec
-#define ID_CRC32 0xbf
-
-/* WebM Elements */
-#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
-
-/* Info Elements */
-#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
-
-/* BlockGroup Elements */
-#define ID_BLOCK 0xa1
-#define ID_BLOCK_DURATION 0x9b
-#define ID_REFERENCE_BLOCK 0xfb
-#define ID_DISCARD_PADDING 0x75a2
-
-/* 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
-
-/* Video Elements */
-#define ID_VIDEO 0xe0
-#define ID_STEREO_MODE 0x53b8
-#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
-
-/* 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
-
-/* 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)
-
-/* 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)
-
-/* Block Header Flags */
-#define BLOCK_FLAGS_LACING 6
-
-/* Lacing Constants */
-#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
-
-/* 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"
-
-enum vint_mask {
- MASK_NONE,
- MASK_FIRST_BIT
-};
-
-struct ebml_binary {
- unsigned char * data;
- size_t length;
-};
-
-struct ebml_list_node {
- struct ebml_list_node * next;
- uint64_t id;
- void * data;
-};
-
-struct ebml_list {
- struct ebml_list_node * head;
- struct ebml_list_node * tail;
-};
-
-struct ebml_type {
- union ebml_value {
- uint64_t u;
- double f;
- int64_t i;
- char * s;
- struct ebml_binary b;
- } v;
- enum ebml_type_enum type;
- int read;
-};
-
-/* 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;
- struct ebml_type doctype;
- struct ebml_type doctype_version;
- struct ebml_type doctype_read_version;
-};
-
-/* Matroksa Definitions */
-struct seek {
- struct ebml_type id;
- struct ebml_type position;
-};
-
-struct seek_head {
- struct ebml_list seek;
-};
-
-struct info {
- struct ebml_type timecode_scale;
- struct ebml_type duration;
-};
-
-struct block_group {
- 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 pixel_width;
- struct ebml_type pixel_height;
- struct ebml_type pixel_crop_bottom;
- struct ebml_type pixel_crop_top;
- struct ebml_type pixel_crop_left;
- struct ebml_type pixel_crop_right;
- struct ebml_type display_width;
- struct ebml_type display_height;
-};
-
-struct audio {
- struct ebml_type sampling_frequency;
- struct ebml_type channels;
- struct ebml_type bit_depth;
-};
-
-struct track_entry {
- struct ebml_type number;
- struct ebml_type uid;
- struct ebml_type type;
- struct ebml_type flag_enabled;
- struct ebml_type flag_default;
- struct ebml_type flag_lacing;
- struct ebml_type track_timecode_scale;
- struct ebml_type language;
- struct ebml_type codec_id;
- struct ebml_type codec_private;
- struct ebml_type codec_delay;
- struct ebml_type seek_preroll;
- struct video video;
- struct audio audio;
-};
-
-struct tracks {
- struct ebml_list track_entry;
-};
-
-struct cue_track_positions {
- struct ebml_type track;
- struct ebml_type cluster_position;
- struct ebml_type block_number;
-};
-
-struct cue_point {
- struct ebml_type time;
- struct ebml_list cue_track_positions;
-};
-
-struct cues {
- struct ebml_list cue_point;
-};
-
-struct segment {
- struct ebml_list seek_head;
- struct info info;
- struct ebml_list cluster;
- struct tracks tracks;
- struct cues cues;
-};
-
-/* Misc. */
-struct pool_ctx {
- char dummy;
-};
-
-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;
-};
-
-/* Public (opaque) Structures */
-struct nestegg {
- nestegg_io * io;
- nestegg_log log;
- struct pool_ctx * alloc_pool;
- uint64_t last_id;
- uint64_t last_size;
- int last_valid;
- struct list_node * ancestor;
- struct ebml ebml;
- struct segment segment;
- int64_t segment_offset;
- unsigned int track_count;
-};
-
-struct nestegg_packet {
- uint64_t track;
- uint64_t timecode;
- struct frame * frame;
- int64_t discard_padding;
-};
-
-/* Element Descriptor */
-struct ebml_element_desc {
- char const * name;
- uint64_t id;
- enum ebml_type_enum type;
- size_t offset;
- unsigned int flags;
- struct ebml_element_desc * children;
- size_t size;
- size_t data_offset;
-};
-
-#define E_FIELD(ID, TYPE, STRUCT, FIELD) \
- { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, NULL, 0, 0 }
-#define E_MASTER(ID, TYPE, STRUCT, FIELD) \
- { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_MULTI, ne_ ## FIELD ## _elements, \
- sizeof(struct FIELD), 0 }
-#define E_SINGLE_MASTER_O(ID, TYPE, STRUCT, FIELD) \
- { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_OFFSET, ne_ ## FIELD ## _elements, 0, \
- offsetof(STRUCT, FIELD ## _offset) }
-#define E_SINGLE_MASTER(ID, TYPE, STRUCT, FIELD) \
- { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, ne_ ## FIELD ## _elements, 0, 0 }
-#define E_SUSPEND(ID, TYPE) \
- { #ID, ID, TYPE, 0, DESC_FLAG_SUSPEND, NULL, 0, 0 }
-#define E_LAST \
- { NULL, 0, 0, 0, DESC_FLAG_NONE, NULL, 0, 0 }
-
-/* EBML Element Lists */
-static struct ebml_element_desc ne_ebml_elements[] = {
- E_FIELD(ID_EBML_VERSION, TYPE_UINT, struct ebml, ebml_version),
- E_FIELD(ID_EBML_READ_VERSION, TYPE_UINT, struct ebml, ebml_read_version),
- E_FIELD(ID_EBML_MAX_ID_LENGTH, TYPE_UINT, struct ebml, ebml_max_id_length),
- E_FIELD(ID_EBML_MAX_SIZE_LENGTH, TYPE_UINT, struct ebml, ebml_max_size_length),
- E_FIELD(ID_DOCTYPE, TYPE_STRING, struct ebml, doctype),
- E_FIELD(ID_DOCTYPE_VERSION, TYPE_UINT, struct ebml, doctype_version),
- E_FIELD(ID_DOCTYPE_READ_VERSION, TYPE_UINT, struct ebml, doctype_read_version),
- E_LAST
-};
-
-/* WebM Element Lists */
-static struct ebml_element_desc ne_seek_elements[] = {
- E_FIELD(ID_SEEK_ID, TYPE_BINARY, struct seek, id),
- E_FIELD(ID_SEEK_POSITION, TYPE_UINT, struct seek, position),
- E_LAST
-};
-
-static struct ebml_element_desc ne_seek_head_elements[] = {
- E_MASTER(ID_SEEK, TYPE_MASTER, struct seek_head, seek),
- E_LAST
-};
-
-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_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_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_PIXEL_WIDTH, TYPE_UINT, struct video, pixel_width),
- E_FIELD(ID_PIXEL_HEIGHT, TYPE_UINT, struct video, pixel_height),
- E_FIELD(ID_PIXEL_CROP_BOTTOM, TYPE_UINT, struct video, pixel_crop_bottom),
- E_FIELD(ID_PIXEL_CROP_TOP, TYPE_UINT, struct video, pixel_crop_top),
- E_FIELD(ID_PIXEL_CROP_LEFT, TYPE_UINT, struct video, pixel_crop_left),
- E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right),
- E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width),
- E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height),
- E_LAST
-};
-
-static struct ebml_element_desc ne_audio_elements[] = {
- E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency),
- E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels),
- E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth),
- 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),
- E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type),
- E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled),
- E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default),
- E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing),
- E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale),
- E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language),
- E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id),
- E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private),
- E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay),
- E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll),
- E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video),
- E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio),
- E_LAST
-};
-
-static struct ebml_element_desc ne_tracks_elements[] = {
- E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry),
- E_LAST
-};
-
-static struct ebml_element_desc ne_cue_track_positions_elements[] = {
- E_FIELD(ID_CUE_TRACK, TYPE_UINT, struct cue_track_positions, track),
- E_FIELD(ID_CUE_CLUSTER_POSITION, TYPE_UINT, struct cue_track_positions, cluster_position),
- E_FIELD(ID_CUE_BLOCK_NUMBER, TYPE_UINT, struct cue_track_positions, block_number),
- E_LAST
-};
-
-static struct ebml_element_desc ne_cue_point_elements[] = {
- E_FIELD(ID_CUE_TIME, TYPE_UINT, struct cue_point, time),
- E_MASTER(ID_CUE_TRACK_POSITIONS, TYPE_MASTER, struct cue_point, cue_track_positions),
- E_LAST
-};
-
-static struct ebml_element_desc ne_cues_elements[] = {
- E_MASTER(ID_CUE_POINT, TYPE_MASTER, struct cues, cue_point),
- E_LAST
-};
-
-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_SINGLE_MASTER(ID_TRACKS, TYPE_MASTER, struct segment, tracks),
- E_SINGLE_MASTER(ID_CUES, TYPE_MASTER, struct segment, cues),
- E_LAST
-};
-
-static struct ebml_element_desc ne_top_level_elements[] = {
- E_SINGLE_MASTER(ID_EBML, TYPE_MASTER, nestegg, ebml),
- E_SINGLE_MASTER_O(ID_SEGMENT, TYPE_MASTER, nestegg, segment),
- E_LAST
-};
-
-#undef E_FIELD
-#undef E_MASTER
-#undef E_SINGLE_MASTER_O
-#undef E_SINGLE_MASTER
-#undef E_SUSPEND
-#undef E_LAST
-
-static struct pool_ctx *
-ne_pool_init(void)
-{
- struct pool_ctx * pool;
-
- pool = h_malloc(sizeof(*pool));
- if (!pool)
- abort();
- return pool;
-}
-
-static void
-ne_pool_destroy(struct pool_ctx * pool)
-{
- h_free(pool);
-}
-
-static void *
-ne_pool_alloc(size_t size, struct pool_ctx * pool)
-{
- void * p;
-
- p = h_malloc(size);
- if (!p)
- abort();
- hattach(p, pool);
- memset(p, 0, size);
- return p;
-}
-
-static void *
-ne_alloc(size_t size)
-{
- void * p;
-
- p = calloc(1, size);
- if (!p)
- abort();
- return p;
-}
-
-static int
-ne_io_read(nestegg_io * io, void * buffer, size_t length)
-{
- return io->read(buffer, length, io->userdata);
-}
-
-static int
-ne_io_seek(nestegg_io * io, int64_t offset, int whence)
-{
- return io->seek(offset, whence, io->userdata);
-}
-
-static int
-ne_io_read_skip(nestegg_io * io, size_t length)
-{
- size_t get;
- unsigned char buf[8192];
- int r = 1;
-
- while (length > 0) {
- get = length < sizeof(buf) ? length : sizeof(buf);
- r = ne_io_read(io, buf, get);
- if (r != 1)
- break;
- length -= get;
- }
-
- return r;
-}
-
-static int64_t
-ne_io_tell(nestegg_io * io)
-{
- return io->tell(io->userdata);
-}
-
-static int
-ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag)
-{
- int r;
- unsigned char b;
- size_t maxlen = 8;
- unsigned int count = 1, mask = 1 << 7;
-
- r = ne_io_read(io, &b, 1);
- if (r != 1)
- return r;
-
- while (count < maxlen) {
- if ((b & mask) != 0)
- break;
- mask >>= 1;
- count += 1;
- }
-
- if (length)
- *length = count;
- *value = b;
-
- if (maskflag == MASK_FIRST_BIT)
- *value = b & ~mask;
-
- while (--count) {
- r = ne_io_read(io, &b, 1);
- if (r != 1)
- return r;
- *value <<= 8;
- *value |= b;
- }
-
- return 1;
-}
-
-static int
-ne_read_id(nestegg_io * io, uint64_t * value, uint64_t * length)
-{
- return ne_bare_read_vint(io, value, length, MASK_NONE);
-}
-
-static int
-ne_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length)
-{
- return ne_bare_read_vint(io, value, length, MASK_FIRST_BIT);
-}
-
-static int
-ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length)
-{
- int r;
- uint64_t uvalue;
- uint64_t ulength;
- int64_t svint_subtr[] = {
- 0x3f, 0x1fff,
- 0xfffff, 0x7ffffff,
- 0x3ffffffffLL, 0x1ffffffffffLL,
- 0xffffffffffffLL, 0x7fffffffffffffLL
- };
-
- r = ne_bare_read_vint(io, &uvalue, &ulength, MASK_FIRST_BIT);
- if (r != 1)
- return r;
- *value = uvalue - svint_subtr[ulength - 1];
- if (length)
- *length = ulength;
- return r;
-}
-
-static int
-ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length)
-{
- unsigned char b;
- int r;
-
- if (length == 0 || length > 8)
- return -1;
- r = ne_io_read(io, &b, 1);
- if (r != 1)
- return r;
- *val = b;
- while (--length) {
- r = ne_io_read(io, &b, 1);
- if (r != 1)
- return r;
- *val <<= 8;
- *val |= b;
- }
- return 1;
-}
-
-static int
-ne_read_int(nestegg_io * io, int64_t * val, uint64_t length)
-{
- int r;
- uint64_t uval, base;
-
- r = ne_read_uint(io, &uval, length);
- if (r != 1)
- return r;
-
- if (length < sizeof(int64_t)) {
- base = 1;
- base <<= length * 8 - 1;
- if (uval >= base) {
- base = 1;
- base <<= length * 8;
- } else {
- base = 0;
- }
- *val = uval - base;
- } else {
- *val = (int64_t) uval;
- }
-
- return 1;
-}
-
-static int
-ne_read_float(nestegg_io * io, double * val, uint64_t length)
-{
- union {
- uint64_t u;
- float f;
- double d;
- } value;
- int r;
-
- /* Length == 10 not implemented. */
- if (length != 4 && length != 8)
- return -1;
- r = ne_read_uint(io, &value.u, length);
- if (r != 1)
- return r;
- if (length == 4)
- *val = value.f;
- else
- *val = value.d;
- return 1;
-}
-
-static int
-ne_read_string(nestegg * ctx, char ** val, uint64_t length)
-{
- char * str;
- int r;
-
- if (length == 0 || length > LIMIT_STRING)
- return -1;
- str = ne_pool_alloc(length + 1, ctx->alloc_pool);
- r = ne_io_read(ctx->io, (unsigned char *) str, length);
- if (r != 1)
- return r;
- str[length] = '\0';
- *val = str;
- return 1;
-}
-
-static int
-ne_read_binary(nestegg * ctx, struct ebml_binary * val, uint64_t length)
-{
- if (length == 0 || length > LIMIT_BINARY)
- return -1;
- val->data = ne_pool_alloc(length, ctx->alloc_pool);
- val->length = length;
- return ne_io_read(ctx->io, val->data, length);
-}
-
-static int
-ne_get_uint(struct ebml_type type, uint64_t * value)
-{
- if (!type.read)
- return -1;
-
- assert(type.type == TYPE_UINT);
-
- *value = type.v.u;
-
- return 0;
-}
-
-static int
-ne_get_float(struct ebml_type type, double * value)
-{
- if (!type.read)
- return -1;
-
- assert(type.type == TYPE_FLOAT);
-
- *value = type.v.f;
-
- return 0;
-}
-
-static int
-ne_get_string(struct ebml_type type, char ** value)
-{
- if (!type.read)
- return -1;
-
- assert(type.type == TYPE_STRING);
-
- *value = type.v.s;
-
- return 0;
-}
-
-static int
-ne_get_binary(struct ebml_type type, struct ebml_binary * value)
-{
- if (!type.read)
- return -1;
-
- assert(type.type == TYPE_BINARY);
-
- *value = type.v.b;
-
- return 0;
-}
-
-static int
-ne_is_ancestor_element(uint64_t id, struct list_node * ancestor)
-{
- struct ebml_element_desc * element;
-
- for (; ancestor; ancestor = ancestor->previous)
- for (element = ancestor->node; element->id; ++element)
- if (element->id == id)
- return 1;
-
- return 0;
-}
-
-static struct ebml_element_desc *
-ne_find_element(uint64_t id, struct ebml_element_desc * elements)
-{
- struct ebml_element_desc * element;
-
- for (element = elements; element->id; ++element)
- if (element->id == id)
- return element;
-
- return NULL;
-}
-
-static void
-ne_ctx_push(nestegg * ctx, struct ebml_element_desc * ancestor, void * data)
-{
- struct list_node * item;
-
- item = ne_alloc(sizeof(*item));
- item->previous = ctx->ancestor;
- item->node = ancestor;
- item->data = data;
- ctx->ancestor = item;
-}
-
-static void
-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)
-{
- 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)
-{
- 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;
- ctx->last_size = s->last_size;
- ctx->last_valid = s->last_valid;
- return 0;
-}
-
-static int
-ne_peek_element(nestegg * ctx, uint64_t * id, uint64_t * size)
-{
- int r;
-
- if (ctx->last_valid) {
- if (id)
- *id = ctx->last_id;
- if (size)
- *size = ctx->last_size;
- return 1;
- }
-
- r = ne_read_id(ctx->io, &ctx->last_id, NULL);
- if (r != 1)
- return r;
-
- r = ne_read_vint(ctx->io, &ctx->last_size, NULL);
- if (r != 1)
- return r;
-
- if (id)
- *id = ctx->last_id;
- if (size)
- *size = ctx->last_size;
-
- ctx->last_valid = 1;
-
- return 1;
-}
-
-static int
-ne_read_element(nestegg * ctx, uint64_t * id, uint64_t * size)
-{
- int r;
-
- r = ne_peek_element(ctx, id, size);
- if (r != 1)
- return r;
-
- ctx->last_valid = 0;
-
- return 1;
-}
-
-static void
-ne_read_master(nestegg * ctx, struct ebml_element_desc * desc)
-{
- struct ebml_list * list;
- struct ebml_list_node * node, * oldtail;
-
- assert(desc->type == TYPE_MASTER && desc->flags & DESC_FLAG_MULTI);
-
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "multi master element %llx (%s)",
- desc->id, desc->name);
-
- list = (struct ebml_list *) (ctx->ancestor->data + desc->offset);
-
- node = ne_pool_alloc(sizeof(*node), ctx->alloc_pool);
- node->id = desc->id;
- node->data = ne_pool_alloc(desc->size, ctx->alloc_pool);
-
- oldtail = list->tail;
- if (oldtail)
- oldtail->next = node;
- list->tail = node;
- if (!list->head)
- list->head = node;
-
- ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p", node->data);
-
- ne_ctx_push(ctx, desc->children, node->data);
-}
-
-static void
-ne_read_single_master(nestegg * ctx, struct ebml_element_desc * desc)
-{
- assert(desc->type == TYPE_MASTER && !(desc->flags & DESC_FLAG_MULTI));
-
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "single master element %llx (%s)",
- desc->id, desc->name);
- ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p (%u)",
- ctx->ancestor->data + desc->offset, desc->offset);
-
- ne_ctx_push(ctx, desc->children, ctx->ancestor->data + desc->offset);
-}
-
-static int
-ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
-{
- struct ebml_type * storage;
- int r = 0;
-
- storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset);
-
- if (storage->read) {
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) already read, skipping",
- desc->id, desc->name);
- 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:
- assert(0);
- break;
- }
-
- if (r == 1)
- storage->read = 1;
-
- return r;
-}
-
-static int
-ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset)
-{
- int r;
- int64_t * data_offset;
- uint64_t id, size, peeked_id;
- struct ebml_element_desc * element;
-
- if (!ctx->ancestor)
- return -1;
-
- for (;;) {
- if (max_offset > 0 && ne_io_tell(ctx->io) >= max_offset) {
- /* Reached end of offset allowed for parsing - return gracefully */
- r = 1;
- break;
- }
- 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);
- 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;
- assert(id == peeked_id);
-
- if (element->flags & DESC_FLAG_OFFSET) {
- data_offset = (int64_t *) (ctx->ancestor->data + element->data_offset);
- *data_offset = ne_io_tell(ctx->io);
- if (*data_offset < 0) {
- r = -1;
- break;
- }
- }
-
- if (element->type == TYPE_MASTER) {
- if (element->flags & DESC_FLAG_MULTI)
- ne_read_master(ctx, element);
- else
- ne_read_single_master(ctx, element);
- continue;
- } else {
- r = ne_read_simple(ctx, element, size);
- if (r < 0)
- break;
- }
- } else if (ne_is_ancestor_element(id, ctx->ancestor->previous)) {
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "parent element %llx", id);
- if (top_level && ctx->ancestor->node == top_level) {
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "*** parse about to back up past top_level");
- r = 1;
- break;
- }
- ne_ctx_pop(ctx);
- } else {
- r = ne_read_element(ctx, &id, &size);
- if (r != 1)
- break;
-
- if (id != ID_VOID && id != ID_CRC32)
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "unknown element %llx", id);
- r = ne_io_read_skip(ctx->io, size);
- if (r != 1)
- break;
- }
- }
-
- if (r != 1)
- while (ctx->ancestor)
- ne_ctx_pop(ctx);
-
- return r;
-}
-
-static uint64_t
-ne_xiph_lace_value(unsigned char ** np)
-{
- uint64_t lace;
- uint64_t value;
- unsigned char * p = *np;
-
- lace = *p++;
- value = lace;
- while (lace == 255) {
- lace = *p++;
- value += lace;
- }
-
- *np = p;
-
- return value;
-}
-
-static int
-ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed)
-{
- int r;
- uint64_t lace;
-
- r = ne_read_uint(io, &lace, 1);
- if (r != 1)
- return r;
- *consumed += 1;
-
- *value = lace;
- while (lace == 255) {
- r = ne_read_uint(io, &lace, 1);
- if (r != 1)
- return r;
- *consumed += 1;
- *value += lace;
- }
-
- return 1;
-}
-
-static int
-ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes)
-{
- int r;
- size_t i = 0;
- uint64_t sum = 0;
-
- while (--n) {
- r = ne_read_xiph_lace_value(io, &sizes[i], read);
- if (r != 1)
- return r;
- sum += sizes[i];
- i += 1;
- }
-
- if (*read + sum > block)
- return -1;
-
- /* Last frame is the remainder of the block. */
- sizes[i] = block - *read - sum;
- return 1;
-}
-
-static int
-ne_read_ebml_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes)
-{
- int r;
- uint64_t lace, sum, length;
- int64_t slace;
- size_t i = 0;
-
- r = ne_read_vint(io, &lace, &length);
- if (r != 1)
- return r;
- *read += length;
-
- sizes[i] = lace;
- sum = sizes[i];
-
- i += 1;
- n -= 1;
-
- while (--n) {
- r = ne_read_svint(io, &slace, &length);
- if (r != 1)
- return r;
- *read += length;
- sizes[i] = sizes[i - 1] + slace;
- sum += sizes[i];
- i += 1;
- }
-
- if (*read + sum > block)
- return -1;
-
- /* Last frame is the remainder of the block. */
- sizes[i] = block - *read - sum;
- return 1;
-}
-
-static uint64_t
-ne_get_timecode_scale(nestegg * ctx)
-{
- uint64_t scale;
-
- if (ne_get_uint(ctx->segment.info.timecode_scale, &scale) != 0)
- scale = 1000000;
-
- return scale;
-}
-
-static int
-ne_map_track_number_to_index(nestegg * ctx,
- unsigned int track_number,
- unsigned int * track_index)
-{
- struct ebml_list_node * node;
- struct track_entry * t_entry;
- uint64_t t_number = 0;
-
- if (!track_index)
- return -1;
- *track_index = 0;
-
- if (track_number == 0)
- return -1;
-
- node = ctx->segment.tracks.track_entry.head;
- while (node) {
- assert(node->id == ID_TRACK_ENTRY);
- t_entry = node->data;
- if (ne_get_uint(t_entry->number, &t_number) != 0)
- return -1;
- if (t_number == track_number)
- return 0;
- *track_index += 1;
- node = node->next;
- }
-
- return -1;
-}
-
-static struct track_entry *
-ne_find_track_entry(nestegg * ctx, unsigned int track)
-{
- struct ebml_list_node * node;
- unsigned int tracks = 0;
-
- node = ctx->segment.tracks.track_entry.head;
- while (node) {
- assert(node->id == ID_TRACK_ENTRY);
- if (track == tracks)
- return node->data;
- tracks += 1;
- node = node->next;
- }
-
- return NULL;
-}
-
-static int
-ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data)
-{
- 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;
- unsigned int i, lacing, track;
- size_t consumed = 0;
-
- *data = NULL;
-
- if (block_size > LIMIT_BLOCK)
- return -1;
-
- r = ne_read_vint(ctx->io, &track_number, &length);
- if (r != 1)
- return r;
-
- if (track_number == 0)
- return -1;
-
- consumed += length;
-
- r = ne_read_int(ctx->io, &timecode, 2);
- if (r != 1)
- return r;
-
- consumed += 2;
-
- r = ne_read_uint(ctx->io, &flags, 1);
- if (r != 1)
- return r;
-
- consumed += 1;
-
- frames = 0;
-
- /* Flags are different between Block and SimpleBlock, but lacing is
- encoded the same way. */
- lacing = (flags & BLOCK_FLAGS_LACING) >> 1;
-
- switch (lacing) {
- case LACING_NONE:
- frames = 1;
- break;
- case LACING_XIPH:
- case LACING_FIXED:
- case LACING_EBML:
- r = ne_read_uint(ctx->io, &frames, 1);
- if (r != 1)
- return r;
- consumed += 1;
- frames += 1;
- }
-
- if (frames > 256)
- return -1;
-
- switch (lacing) {
- case LACING_NONE:
- frame_sizes[0] = block_size - consumed;
- break;
- case LACING_XIPH:
- if (frames == 1)
- return -1;
- r = ne_read_xiph_lacing(ctx->io, block_size, &consumed, frames, frame_sizes);
- if (r != 1)
- return r;
- break;
- case LACING_FIXED:
- if ((block_size - consumed) % frames)
- return -1;
- for (i = 0; i < frames; ++i)
- frame_sizes[i] = (block_size - consumed) / frames;
- break;
- case LACING_EBML:
- if (frames == 1)
- return -1;
- r = ne_read_ebml_lacing(ctx->io, block_size, &consumed, frames, frame_sizes);
- if (r != 1)
- return r;
- break;
- }
-
- /* Sanity check unlaced frame sizes against total block size. */
- total = consumed;
- for (i = 0; i < frames; ++i)
- total += frame_sizes[i];
- if (total > block_size)
- return -1;
-
- if (ne_map_track_number_to_index(ctx, track_number, &track) != 0)
- return -1;
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- 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)
- return -1;
-
- abs_timecode = timecode + cluster_tc;
- if (abs_timecode < 0)
- return -1;
-
- pkt = ne_alloc(sizeof(*pkt));
- pkt->track = track;
- pkt->timecode = abs_timecode * tc_scale * track_scale;
-
- 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);
-
- last = NULL;
- for (i = 0; i < frames; ++i) {
- if (frame_sizes[i] > LIMIT_FRAME) {
- nestegg_free_packet(pkt);
- return -1;
- }
- f = ne_alloc(sizeof(*f));
- f->data = ne_alloc(frame_sizes[i]);
- f->length = frame_sizes[i];
- r = ne_io_read(ctx->io, f->data, frame_sizes[i]);
- if (r != 1) {
- free(f->data);
- free(f);
- nestegg_free_packet(pkt);
- return -1;
- }
-
- if (!last)
- pkt->frame = f;
- else
- last->next = f;
- last = f;
- }
-
- *data = pkt;
-
- 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 uint64_t
-ne_buf_read_id(unsigned char const * p, size_t length)
-{
- uint64_t id = 0;
-
- while (length--) {
- id <<= 8;
- id |= *p++;
- }
-
- return id;
-}
-
-static struct seek *
-ne_find_seek_for_id(struct ebml_list_node * seek_head, uint64_t id)
-{
- struct ebml_list * head;
- struct ebml_list_node * seek;
- struct ebml_binary binary_id;
- struct seek * s;
-
- while (seek_head) {
- assert(seek_head->id == ID_SEEK_HEAD);
- head = seek_head->data;
- seek = head->head;
-
- while (seek) {
- assert(seek->id == ID_SEEK);
- s = seek->data;
-
- if (ne_get_binary(s->id, &binary_id) == 0 &&
- ne_buf_read_id(binary_id.data, binary_id.length) == id)
- return s;
-
- seek = seek->next;
- }
-
- seek_head = seek_head->next;
- }
-
- return NULL;
-}
-
-static struct cue_track_positions *
-ne_find_cue_position_for_track(nestegg * ctx, struct ebml_list_node * node, unsigned int track)
-{
- struct cue_track_positions * pos = NULL;
- uint64_t track_number;
- unsigned int t;
-
- while (node) {
- assert(node->id == ID_CUE_TRACK_POSITIONS);
- pos = node->data;
- if (ne_get_uint(pos->track, &track_number) != 0)
- return NULL;
-
- if (ne_map_track_number_to_index(ctx, track_number, &t) != 0)
- return NULL;
-
- if (t == track)
- return pos;
-
- node = node->next;
- }
-
- return NULL;
-}
-
-static struct cue_point *
-ne_find_cue_point_for_tstamp(nestegg * ctx, struct ebml_list_node * cue_point, unsigned int track, uint64_t scale, uint64_t tstamp)
-{
- uint64_t time;
- struct cue_point * c, * prev = NULL;
-
- while (cue_point) {
- assert(cue_point->id == ID_CUE_POINT);
- c = cue_point->data;
-
- if (!prev)
- prev = c;
-
- if (ne_get_uint(c->time, &time) == 0 && time * scale > tstamp)
- break;
-
- if (ne_find_cue_position_for_track(ctx, c->cue_track_positions.head, track) != NULL)
- prev = c;
-
- cue_point = cue_point->next;
- }
-
- 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, ...)
-{
- if (ctx && severity && fmt)
- return;
-}
-
-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;
-
- /* 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;
-
- if (ne_get_uint(found->position, &seek_pos) != 0)
- return -1;
-
- /* Save old parser state. */
- r = ne_ctx_save(ctx, &state);
- if (r != 0)
- return -1;
-
- /* Seek and set up parser state for segment-level element (Cues). */
- r = ne_io_seek(ctx->io, ctx->segment_offset + seek_pos, NESTEGG_SEEK_SET);
- if (r != 0)
- return -1;
- ctx->last_valid = 0;
-
- r = ne_read_element(ctx, &id, NULL);
- if (r != 1)
- return -1;
-
- if (id != ID_CUES)
- return -1;
-
- ctx->ancestor = NULL;
- ne_ctx_push(ctx, ne_top_level_elements, ctx);
- ne_ctx_push(ctx, ne_segment_elements, &ctx->segment);
- ne_ctx_push(ctx, ne_cues_elements, &ctx->segment.cues);
- /* parser will run until end of cues element. */
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cue elements");
- r = ne_parse(ctx, ne_cues_elements, max_offset);
- while (ctx->ancestor)
- ne_ctx_pop(ctx);
-
- /* Reset parser state to original state and seek back to old position. */
- if (ne_ctx_restore(ctx, &state) != 0)
- return -1;
-
- if (r < 0)
- return -1;
-
- node = ctx->segment.cues.cue_point.head;
- if (!node)
- return -1;
- }
-
- return 0;
-}
-
-/* Three functions that implement the nestegg_io interface, operating on a
- * sniff_buffer. */
-struct sniff_buffer {
- unsigned char const * buffer;
- size_t length;
- int64_t offset;
-};
-
-static int
-ne_buffer_read(void * buffer, size_t length, void * user_data)
-{
- struct sniff_buffer * sb = user_data;
-
- int rv = 1;
- size_t available = sb->length - sb->offset;
-
- if (available < length)
- return 0;
-
- memcpy(buffer, sb->buffer + sb->offset, length);
- sb->offset += length;
-
- return rv;
-}
-
-static int
-ne_buffer_seek(int64_t offset, int whence, void * user_data)
-{
- struct sniff_buffer * sb = user_data;
- int64_t o = sb->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;
- }
-
- if (o < 0 || o > (int64_t) sb->length)
- return -1;
-
- sb->offset = o;
- return 0;
-}
-
-static int64_t
-ne_buffer_tell(void * user_data)
-{
- struct sniff_buffer * sb = user_data;
- return sb->offset;
-}
-
-static int
-ne_match_webm(nestegg_io io, int64_t max_offset)
-{
- int r;
- uint64_t id;
- char * doctype;
- nestegg * ctx;
-
- if (!(io.read && io.seek && io.tell))
- return -1;
-
- ctx = ne_alloc(sizeof(*ctx));
-
- ctx->io = ne_alloc(sizeof(*ctx->io));
- *ctx->io = io;
- ctx->alloc_pool = ne_pool_init();
- 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))
- return -1;
-
- ctx = ne_alloc(sizeof(*ctx));
-
- ctx->io = ne_alloc(sizeof(*ctx->io));
- *ctx->io = io;
- ctx->log = callback;
- ctx->alloc_pool = ne_pool_init();
-
- if (!ctx->log)
- ctx->log = ne_null_log_callback;
-
- r = ne_peek_element(ctx, &id, NULL);
- if (r != 1) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- if (id != ID_EBML) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "ctx %p", ctx);
-
- ne_ctx_push(ctx, ne_top_level_elements, ctx);
-
- r = ne_parse(ctx, NULL, max_offset);
-
- if (r != 1) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- if (ne_get_uint(ctx->ebml.ebml_read_version, &version) != 0)
- version = 1;
- if (version != 1) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- if (ne_get_string(ctx->ebml.doctype, &doctype) != 0)
- doctype = "matroska";
- if (strcmp(doctype, "webm") != 0) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- if (ne_get_uint(ctx->ebml.doctype_read_version, &docversion) != 0)
- docversion = 1;
- if (docversion < 1 || docversion > 2) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- if (!ctx->segment.tracks.track_entry.head) {
- nestegg_destroy(ctx);
- return -1;
- }
-
- track = ctx->segment.tracks.track_entry.head;
- ctx->track_count = 0;
-
- while (track) {
- ctx->track_count += 1;
- track = track->next;
- }
-
- *context = ctx;
-
- return 0;
-}
-
-void
-nestegg_destroy(nestegg * ctx)
-{
- while (ctx->ancestor)
- ne_ctx_pop(ctx);
- ne_pool_destroy(ctx->alloc_pool);
- free(ctx->io);
- free(ctx);
-}
-
-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;
-
- tc_scale = ne_get_timecode_scale(ctx);
-
- *duration = (uint64_t) (unscaled_duration * tc_scale);
- return 0;
-}
-
-int
-nestegg_tstamp_scale(nestegg * ctx, uint64_t * scale)
-{
- *scale = ne_get_timecode_scale(ctx);
- return 0;
-}
-
-int
-nestegg_track_count(nestegg * ctx, unsigned int * tracks)
-{
- *tracks = ctx->track_count;
- return 0;
-}
-
-int
-nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offset,
- int64_t * start_pos, int64_t * end_pos, uint64_t * tstamp)
-{
- int range_obtained = 0;
- unsigned int cluster_count = 0;
- struct cue_point * cue_point;
- struct cue_track_positions * pos;
- uint64_t seek_pos, track_number, tc_scale, time;
- struct ebml_list_node * cues_node = ctx->segment.cues.cue_point.head;
- struct ebml_list_node * cue_pos_node = NULL;
- unsigned int track = 0, track_count = 0, track_index;
-
- if (!start_pos || !end_pos || !tstamp)
- return -1;
-
- /* Initialise return values */
- *start_pos = -1;
- *end_pos = -1;
- *tstamp = 0;
-
- if (!cues_node) {
- ne_init_cue_points(ctx, max_offset);
- cues_node = ctx->segment.cues.cue_point.head;
- /* Verify cues have been added to context. */
- if (!cues_node)
- return -1;
- }
-
- nestegg_track_count(ctx, &track_count);
-
- tc_scale = ne_get_timecode_scale(ctx);
-
- while (cues_node && !range_obtained) {
- assert(cues_node->id == ID_CUE_POINT);
- cue_point = cues_node->data;
- cue_pos_node = cue_point->cue_track_positions.head;
- 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++) {
- if (ne_get_uint(pos->track, &track_number) != 0)
- return -1;
-
- if (ne_map_track_number_to_index(ctx, track_number, &track_index) != 0)
- return -1;
-
- if (track_index == track) {
- if (ne_get_uint(pos->cluster_position, &seek_pos) != 0)
- return -1;
- if (cluster_count == cluster_num) {
- *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;
- range_obtained = 1;
- break;
- }
- cluster_count++;
- }
- }
- cue_pos_node = cue_pos_node->next;
- }
- cues_node = cues_node->next;
- }
-
- return 0;
-}
-
-int
-nestegg_offset_seek(nestegg * ctx, uint64_t offset)
-{
- int r;
-
- /* 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);
-
- return 0;
-}
-
-int
-nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp)
-{
- int r;
- struct cue_point * cue_point;
- struct cue_track_positions * pos;
- uint64_t seek_pos, tc_scale;
-
- /* If there are no cues loaded, check for cues element in the seek head
- and load it. */
- if (!ctx->segment.cues.cue_point.head) {
- r = ne_init_cue_points(ctx, -1);
- if (r != 0)
- return -1;
- }
-
- tc_scale = ne_get_timecode_scale(ctx);
-
- cue_point = ne_find_cue_point_for_tstamp(ctx, ctx->segment.cues.cue_point.head,
- track, tc_scale, tstamp);
- if (!cue_point)
- return -1;
-
- pos = ne_find_cue_position_for_track(ctx, cue_point->cue_track_positions.head, track);
- if (pos == NULL)
- return -1;
-
- if (ne_get_uint(pos->cluster_position, &seek_pos) != 0)
- return -1;
-
- /* Seek and set up parser state for segment-level element (Cluster). */
- r = nestegg_offset_seek(ctx, ctx->segment_offset + seek_pos);
- ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cluster elements");
- r = ne_parse(ctx, NULL, -1);
- if (r != 1)
- return -1;
-
- if (!ne_is_suspend_element(ctx->last_id))
- return -1;
-
- return 0;
-}
-
-int
-nestegg_track_type(nestegg * ctx, unsigned int track)
-{
- struct track_entry * entry;
- uint64_t type;
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- return -1;
-
- if (ne_get_uint(entry->type, &type) != 0)
- return -1;
-
- if (type & TRACK_TYPE_VIDEO)
- return NESTEGG_TRACK_VIDEO;
-
- if (type & TRACK_TYPE_AUDIO)
- return NESTEGG_TRACK_AUDIO;
-
- return -1;
-}
-
-int
-nestegg_track_codec_id(nestegg * ctx, unsigned int track)
-{
- char * codec_id;
- struct track_entry * entry;
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- return -1;
-
- if (ne_get_string(entry->codec_id, &codec_id) != 0)
- return -1;
-
- if (strcmp(codec_id, TRACK_ID_VP8) == 0)
- return NESTEGG_CODEC_VP8;
-
- if (strcmp(codec_id, TRACK_ID_VP9) == 0)
- return NESTEGG_CODEC_VP9;
-
- if (strcmp(codec_id, TRACK_ID_VORBIS) == 0)
- return NESTEGG_CODEC_VORBIS;
-
- if (strcmp(codec_id, TRACK_ID_OPUS) == 0)
- return NESTEGG_CODEC_OPUS;
-
- return -1;
-}
-
-int
-nestegg_track_codec_data_count(nestegg * ctx, unsigned int track,
- unsigned int * count)
-{
- struct track_entry * entry;
- struct ebml_binary codec_private;
- unsigned char * p;
-
- *count = 0;
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- return -1;
-
- if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS)
- return -1;
-
- if (ne_get_binary(entry->codec_private, &codec_private) != 0)
- return -1;
-
- if (codec_private.length < 1)
- return -1;
-
- p = codec_private.data;
- *count = *p + 1;
-
- if (*count > 3)
- return -1;
-
- return 0;
-}
-
-int
-nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item,
- unsigned char ** data, size_t * length)
-{
- 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;
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- return -1;
-
- 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;
-
- if (count > 3)
- return -1;
-
- 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);
-
- for (i = 0; i < item; ++i) {
- if (sizes[i] > LIMIT_FRAME)
- return -1;
- p += sizes[i];
- }
- *data = p;
- *length = sizes[item];
- } else {
- *data = codec_private.data;
- *length = codec_private.length;
- }
-
- return 0;
-}
-
-int
-nestegg_track_video_params(nestegg * ctx, unsigned int track,
- nestegg_video_params * params)
-{
- struct track_entry * entry;
- uint64_t value;
-
- memset(params, 0, sizeof(*params));
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- return -1;
-
- if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_VIDEO)
- return -1;
-
- value = 0;
- ne_get_uint(entry->video.stereo_mode, &value);
- if (value <= NESTEGG_VIDEO_STEREO_TOP_BOTTOM ||
- value == NESTEGG_VIDEO_STEREO_RIGHT_LEFT)
- params->stereo_mode = value;
-
- if (ne_get_uint(entry->video.pixel_width, &value) != 0)
- return -1;
- params->width = value;
-
- if (ne_get_uint(entry->video.pixel_height, &value) != 0)
- return -1;
- params->height = value;
-
- value = 0;
- ne_get_uint(entry->video.pixel_crop_bottom, &value);
- params->crop_bottom = value;
-
- value = 0;
- ne_get_uint(entry->video.pixel_crop_top, &value);
- params->crop_top = value;
-
- value = 0;
- ne_get_uint(entry->video.pixel_crop_left, &value);
- params->crop_left = value;
-
- value = 0;
- ne_get_uint(entry->video.pixel_crop_right, &value);
- params->crop_right = value;
-
- value = params->width;
- ne_get_uint(entry->video.display_width, &value);
- params->display_width = value;
-
- value = params->height;
- ne_get_uint(entry->video.display_height, &value);
- params->display_height = value;
-
- return 0;
-}
-
-int
-nestegg_track_audio_params(nestegg * ctx, unsigned int track,
- nestegg_audio_params * params)
-{
- struct track_entry * entry;
- uint64_t value;
-
- memset(params, 0, sizeof(*params));
-
- entry = ne_find_track_entry(ctx, track);
- if (!entry)
- return -1;
-
- if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_AUDIO)
- return -1;
-
- params->rate = 8000;
- ne_get_float(entry->audio.sampling_frequency, ¶ms->rate);
-
- value = 1;
- ne_get_uint(entry->audio.channels, &value);
- params->channels = value;
-
- value = 16;
- ne_get_uint(entry->audio.bit_depth, &value);
- params->depth = value;
-
- value = 0;
- ne_get_uint(entry->codec_delay, &value);
- params->codec_delay = value;
-
- value = 0;
- ne_get_uint(entry->seek_preroll, &value);
- params->seek_preroll = value;
-
- return 0;
-}
-
-int
-nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt)
-{
- int r;
- uint64_t id, size;
-
- *pkt = NULL;
-
- for (;;) {
- r = ne_peek_element(ctx, &id, &size);
- if (r != 1)
- 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
- handle directly. */
- r = ne_read_block(ctx, id, size, pkt);
- if (r != 1)
- return r;
-
- r = ne_read_discard_padding(ctx, *pkt);
- if (r != 1)
- return r;
-
- return r;
- }
-
- r = ne_parse(ctx, NULL, -1);
- if (r != 1)
- return r;
- }
-
- return 1;
-}
-
-void
-nestegg_free_packet(nestegg_packet * pkt)
-{
- struct frame * frame;
-
- while (pkt->frame) {
- frame = pkt->frame;
- pkt->frame = frame->next;
- free(frame->data);
- free(frame);
- }
-
- free(pkt);
-}
-
-int
-nestegg_packet_track(nestegg_packet * pkt, unsigned int * track)
-{
- *track = pkt->track;
- return 0;
-}
-
-int
-nestegg_packet_tstamp(nestegg_packet * pkt, uint64_t * tstamp)
-{
- *tstamp = pkt->timecode;
- return 0;
-}
-
-int
-nestegg_packet_discard_padding(nestegg_packet * pkt, int64_t * discard_padding)
-{
- *discard_padding = pkt->discard_padding;
- return 0;
-}
-
-int
-nestegg_packet_count(nestegg_packet * pkt, unsigned int * count)
-{
- struct frame * f = pkt->frame;
-
- *count = 0;
-
- while (f) {
- *count += 1;
- f = f->next;
- }
-
- return 0;
-}
-
-int
-nestegg_packet_data(nestegg_packet * pkt, unsigned int item,
- unsigned char ** data, size_t * length)
-{
- struct frame * f = pkt->frame;
- unsigned int count = 0;
-
- *data = NULL;
- *length = 0;
-
- while (f) {
- if (count == item) {
- *data = f->data;
- *length = f->length;
- return 0;
- }
- count += 1;
- f = f->next;
- }
-
- return -1;
-}
-
-int
-nestegg_has_cues(nestegg * ctx)
-{
- return ctx->segment.cues.cue_point.head ||
- ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES);
-}
-
-int
-nestegg_sniff(unsigned char const * buffer, size_t length)
-{
- nestegg_io io;
- struct sniff_buffer user_data;
-
- user_data.buffer = buffer;
- user_data.length = length;
- user_data.offset = 0;
-
- io.read = ne_buffer_read;
- io.seek = ne_buffer_seek;
- io.tell = ne_buffer_tell;
- io.userdata = &user_data;
- return ne_match_webm(io, length);
-}
-
--- a/nestegg/test/test.c
+++ /dev/null
@@ -1,250 +1,0 @@
-/*
- * Copyright © 2010 Mozilla Foundation
- *
- * This program is made available under an ISC-style license. See the
- * accompanying file LICENSE for details.
- */
-#include <assert.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include "nestegg/nestegg.h"
-
-#undef DEBUG
-#define SEEK_TEST
-
-static int
-stdio_read(void * p, size_t length, void * file)
-{
- size_t r;
- FILE * fp = file;
-
- r = fread(p, length, 1, fp);
- if (r == 0 && feof(fp))
- return 0;
- return r == 0 ? -1 : 1;
-}
-
-static int
-stdio_seek(int64_t offset, int whence, void * file)
-{
- FILE * fp = file;
- return fseek(fp, offset, whence);
-}
-
-static int64_t
-stdio_tell(void * fp)
-{
- return ftell(fp);
-}
-
-static void
-log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...)
-{
- va_list ap;
- char const * sev = NULL;
-
-#if !defined(DEBUG)
- if (severity < NESTEGG_LOG_WARNING)
- return;
-#endif
-
- switch (severity) {
- case NESTEGG_LOG_DEBUG:
- sev = "debug: ";
- break;
- case NESTEGG_LOG_WARNING:
- sev = "warning: ";
- break;
- case NESTEGG_LOG_CRITICAL:
- sev = "critical:";
- break;
- default:
- sev = "unknown: ";
- }
-
- fprintf(stderr, "%p %s ", (void *) ctx, sev);
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fprintf(stderr, "\n");
-}
-
-int
-main(int argc, char * argv[])
-{
- FILE * fp;
- int r, type;
- nestegg * ctx;
- nestegg_audio_params aparams;
- nestegg_packet * pkt;
- nestegg_video_params vparams;
- size_t length, size;
- uint64_t duration, tstamp, pkt_tstamp;
- unsigned char * codec_data, * ptr;
- unsigned int cnt, i, j, track, tracks, pkt_cnt, pkt_track;
- unsigned int data_items = 0;
- nestegg_io io = {
- stdio_read,
- stdio_seek,
- stdio_tell,
- NULL
- };
-
- if (argc != 2)
- return EXIT_FAILURE;
-
- fp = fopen(argv[1], "rb");
- if (!fp)
- return EXIT_FAILURE;
-
- io.userdata = fp;
-
- ctx = NULL;
- r = nestegg_init(&ctx, io, log_callback, -1);
- if (r != 0)
- return EXIT_FAILURE;
-
- nestegg_track_count(ctx, &tracks);
- nestegg_duration(ctx, &duration);
-#if defined(DEBUG)
- fprintf(stderr, "media has %u tracks and duration %fs\n", tracks, duration / 1e9);
-#endif
-
- for (i = 0; i < tracks; ++i) {
- type = nestegg_track_type(ctx, i);
-#if defined(DEBUG)
- fprintf(stderr, "track %u: type: %d codec: %d", i,
- type, nestegg_track_codec_id(ctx, i));
-#endif
- nestegg_track_codec_data_count(ctx, i, &data_items);
- for (j = 0; j < data_items; ++j) {
- nestegg_track_codec_data(ctx, i, j, &codec_data, &length);
-#if defined(DEBUG)
- fprintf(stderr, " (%p, %u)", codec_data, (unsigned int) length);
-#endif
- }
- if (type == NESTEGG_TRACK_VIDEO) {
- nestegg_track_video_params(ctx, i, &vparams);
-#if defined(DEBUG)
- fprintf(stderr, " video: %ux%u (d: %ux%u %ux%ux%ux%u)",
- vparams.width, vparams.height,
- vparams.display_width, vparams.display_height,
- vparams.crop_top, vparams.crop_left, vparams.crop_bottom, vparams.crop_right);
-#endif
- } else if (type == NESTEGG_TRACK_AUDIO) {
- nestegg_track_audio_params(ctx, i, &aparams);
-#if defined(DEBUG)
- fprintf(stderr, " audio: %.2fhz %u bit %u channels",
- aparams.rate, aparams.depth, aparams.channels);
-#endif
- }
-#if defined(DEBUG)
- fprintf(stderr, "\n");
-#endif
- }
-
-#if defined(SEEK_TEST)
-#if defined(DEBUG)
- fprintf(stderr, "seek to middle\n");
-#endif
- r = nestegg_track_seek(ctx, 0, duration / 2);
- if (r == 0) {
-#if defined(DEBUG)
- fprintf(stderr, "middle ");
-#endif
- r = nestegg_read_packet(ctx, &pkt);
- if (r == 1) {
- nestegg_packet_track(pkt, &track);
- nestegg_packet_count(pkt, &cnt);
- nestegg_packet_tstamp(pkt, &tstamp);
-#if defined(DEBUG)
- fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
-#endif
- nestegg_free_packet(pkt);
- } else {
-#if defined(DEBUG)
- fprintf(stderr, "middle seek failed\n");
-#endif
- }
- }
-
-#if defined(DEBUG)
- fprintf(stderr, "seek to ~end\n");
-#endif
- r = nestegg_track_seek(ctx, 0, duration - (duration / 10));
- if (r == 0) {
-#if defined(DEBUG)
- fprintf(stderr, "end ");
-#endif
- r = nestegg_read_packet(ctx, &pkt);
- if (r == 1) {
- nestegg_packet_track(pkt, &track);
- nestegg_packet_count(pkt, &cnt);
- nestegg_packet_tstamp(pkt, &tstamp);
-#if defined(DEBUG)
- fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
-#endif
- nestegg_free_packet(pkt);
- } else {
-#if defined(DEBUG)
- fprintf(stderr, "end seek failed\n");
-#endif
- }
- }
-
-#if defined(DEBUG)
- fprintf(stderr, "seek to ~start\n");
-#endif
- r = nestegg_track_seek(ctx, 0, duration / 10);
- if (r == 0) {
-#if defined(DEBUG)
- fprintf(stderr, "start ");
-#endif
- r = nestegg_read_packet(ctx, &pkt);
- if (r == 1) {
- nestegg_packet_track(pkt, &track);
- nestegg_packet_count(pkt, &cnt);
- nestegg_packet_tstamp(pkt, &tstamp);
-#if defined(DEBUG)
- fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
-#endif
- nestegg_free_packet(pkt);
- } else {
-#if defined(DEBUG)
- fprintf(stderr, "start seek failed\n");
-#endif
- }
- }
-#endif
-
- while (nestegg_read_packet(ctx, &pkt) > 0) {
- nestegg_packet_track(pkt, &pkt_track);
- nestegg_packet_count(pkt, &pkt_cnt);
- nestegg_packet_tstamp(pkt, &pkt_tstamp);
-
-#if defined(DEBUG)
- fprintf(stderr, "t %u pts %f frames %u: ", pkt_track, pkt_tstamp / 1e9, pkt_cnt);
-#endif
-
- for (i = 0; i < pkt_cnt; ++i) {
- nestegg_packet_data(pkt, i, &ptr, &size);
-#if defined(DEBUG)
- fprintf(stderr, "%u ", (unsigned int) size);
-#endif
- }
-#if defined(DEBUG)
- fprintf(stderr, "\n");
-#endif
-
- nestegg_free_packet(pkt);
- }
-
- nestegg_destroy(ctx);
- fclose(fp);
-
- return EXIT_SUCCESS;
-}
--- a/test/test.mk
+++ b/test/test.mk
@@ -39,12 +39,12 @@
LIBVPX_TEST_SRCS-$(CONFIG_VP9_DECODER) += external_frame_buffer_test.cc
## WebM Parsing
-NESTEGG_SRCS += ../nestegg/halloc/halloc.h
-NESTEGG_SRCS += ../nestegg/halloc/src/align.h
-NESTEGG_SRCS += ../nestegg/halloc/src/halloc.c
-NESTEGG_SRCS += ../nestegg/halloc/src/hlist.h
-NESTEGG_SRCS += ../nestegg/include/nestegg/nestegg.h
-NESTEGG_SRCS += ../nestegg/src/nestegg.c
+NESTEGG_SRCS += ../third_party/nestegg/halloc/halloc.h
+NESTEGG_SRCS += ../third_party/nestegg/halloc/src/align.h
+NESTEGG_SRCS += ../third_party/nestegg/halloc/src/halloc.c
+NESTEGG_SRCS += ../third_party/nestegg/halloc/src/hlist.h
+NESTEGG_SRCS += ../third_party/nestegg/include/nestegg/nestegg.h
+NESTEGG_SRCS += ../third_party/nestegg/src/nestegg.c
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += $(NESTEGG_SRCS)
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += webm_video_source.h
--- a/test/webm_video_source.h
+++ b/test/webm_video_source.h
@@ -14,7 +14,7 @@
#include <cstdlib>
#include <new>
#include <string>
-#include "nestegg/include/nestegg/nestegg.h"
+#include "third_party/nestegg/include/nestegg/nestegg.h"
#include "test/video_source.h"
namespace libvpx_test {
--- /dev/null
+++ b/third_party/nestegg/0001-include-paths.diff
@@ -1,0 +1,41 @@
+diff --git a/nestegg/halloc/src/halloc.c b/nestegg/halloc/src/halloc.c
+index 5758fc0..837b3ff 100644
+--- a/nestegg/halloc/src/halloc.c
++++ b/nestegg/halloc/src/halloc.c
+@@ -15,7 +15,7 @@
+ #include <stdlib.h> /* realloc */
+ #include <string.h> /* memset & co */
+
+-#include "halloc.h"
++#include "third_party/nestegg/halloc/halloc.h"
+ #include "align.h"
+ #include "hlist.h"
+
+diff --git a/nestegg/include/nestegg/nestegg.h b/nestegg/include/nestegg/nestegg.h
+index ff13728..c18d1d3 100644
+--- a/nestegg/include/nestegg/nestegg.h
++++ b/nestegg/include/nestegg/nestegg.h
+@@ -7,7 +7,7 @@
+ #if !defined(NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79)
+ #define NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79
+
+-#include <nestegg/nestegg-stdint.h>
++#include "vpx/vpx_integer.h"
+
+ #if defined(__cplusplus)
+ extern "C" {
+diff --git a/nestegg/src/nestegg.c b/nestegg/src/nestegg.c
+index daf1eed..4fb10e7 100644
+--- a/nestegg/src/nestegg.c
++++ b/nestegg/src/nestegg.c
+@@ -8,8 +8,8 @@
+ #include <stdlib.h>
+ #include <string.h>
+
+-#include "halloc.h"
+-#include "nestegg/nestegg.h"
++#include "third_party/nestegg/halloc/halloc.h"
++#include "third_party/nestegg/include/nestegg/nestegg.h"
+
+ /* EBML Elements */
+ #define ID_EBML 0x1a45dfa3
--- /dev/null
+++ b/third_party/nestegg/0002-ne_read_simple-uninitialized_variable.diff
@@ -1,0 +1,21 @@
+diff --git a/nestegg/src/nestegg.c b/nestegg/src/nestegg.c
+index 4fb10e7..b6bc460 100644
+--- a/nestegg/src/nestegg.c
++++ b/nestegg/src/nestegg.c
+@@ -934,7 +934,7 @@ static int
+ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
+ {
+ struct ebml_type * storage;
+- int r;
++ int r = 0;
+
+ storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset);
+
+@@ -968,7 +968,6 @@ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
+ case TYPE_MASTER:
+ case TYPE_UNKNOWN:
+ assert(0);
+- r = 0;
+ break;
+ }
+
--- /dev/null
+++ b/third_party/nestegg/AUTHORS
@@ -1,0 +1,3 @@
+Matthew Gregan <kinetik@flim.org>
+Steve Workman <sjhworkman@gmail.com>
+Paul Adenot <paul@paul.cx>
--- /dev/null
+++ b/third_party/nestegg/INSTALL
@@ -1,0 +1,8 @@
+Build instructions for libnestegg
+=================================
+
+0. Change directory into the source directory.
+1. Run |autoreconf --install| to generate configure.
+2. Run |./configure| to configure the build.
+3. Run |make| to build.
+4. Run |make check| to run the test suite.
--- /dev/null
+++ b/third_party/nestegg/LICENSE
@@ -1,0 +1,13 @@
+Copyright © 2010 Mozilla Foundation
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null
+++ b/third_party/nestegg/README
@@ -1,0 +1,6 @@
+See INSTALL for build instructions.
+
+Licensed under an ISC-style license. See LICENSE for details.
+
+The source under the halloc/ directory is licensed under a BSD license. See
+halloc/halloc.h for details.
--- /dev/null
+++ b/third_party/nestegg/README.webm
@@ -1,0 +1,16 @@
+URL: https://github.com/kinetiknz/nestegg.git
+Version: f46223191d8116a36bf299b5b9793fcb798417b1
+License: ISC-style
+License File: LICENSE
+
+Description:
+The source under the halloc/ directory is licensed under a BSD license. See
+halloc/halloc.h for details.
+
+Local Modifications:
+- delete unnecessary docs and build files
+- nestegg/0001-include-paths.diff
+ include path modifications for the libvpx build system
+- 0002-ne_read_simple-uninitialized_variable.diff
+ fixes:
+nestegg.c|975 col 6| warning: ‘r’ may be used uninitialized in this function [-Wuninitialized]
--- /dev/null
+++ b/third_party/nestegg/TODO
@@ -1,0 +1,21 @@
+- Document when read, seek, tell callbacks are used.
+- Add an automated testsuite.
+- Test (and fix, if necessary) support for unknown sizes.
+- Test (and fix, if necessary) support for large files.
+- Read past unknown elements rather than seeking.
+- Try to handle unknown elements with unknown sizes.
+- Formalize handling of default element values.
+- Try to resynchronize stream when read_block fails so that failure to parse
+ a single block can be treated as non-fatal.
+- Make logging more useful to API users.
+- Avoid reparsing Cues and ignore any SeekHead at end of file.
+- Optionally build a Cue index as Clusters are parsed.
+- Support seeking without Cues.
+- Avoid building a list of Clusters as they are parsed and retain only the
+ last one parsed.
+- Add an asynchronous error code to struct nestegg and ensure that API calls
+ continue to fail safely one a fatal error has been returned.
+- Modify parser/data structures to provide a clean separation. Perhaps the
+ parser should return a generic tree of nodes that a second pass uses to
+ initialize the main data structures.
+- Use pool allocator for all allocations.
--- /dev/null
+++ b/third_party/nestegg/halloc/README
@@ -1,0 +1,45 @@
+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 <string.h> 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.
+
--- /dev/null
+++ b/third_party/nestegg/halloc/halloc.h
@@ -1,0 +1,43 @@
+/*
+ * 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 <stddef.h> /* 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
+
--- /dev/null
+++ b/third_party/nestegg/halloc/src/align.h
@@ -1,0 +1,36 @@
+/*
+ * 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_
+
+/*
+ * 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
+
--- /dev/null
+++ b/third_party/nestegg/halloc/src/halloc.c
@@ -1,0 +1,254 @@
+/*
+ * 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 <stdlib.h> /* realloc */
+#include <string.h> /* memset & co */
+
+#include "third_party/nestegg/halloc/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
+ */
+static void _set_allocator(void);
+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)
+ {
+ _set_allocator();
+ 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
+ */
+static void _set_allocator(void)
+{
+ 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.
+ */
+ allocator = realloc;
+ if (! (p = malloc(1)))
+ /* hmm */
+ return;
+
+ if ((p = realloc(p, 0)))
+ {
+ /* realloc cannot be used as free() */
+ allocator = _realloc;
+ free(p);
+ }
+}
+
+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);
+ }
+}
+
--- /dev/null
+++ b/third_party/nestegg/halloc/src/hlist.h
@@ -1,0 +1,136 @@
+/*
+ * 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 <assert.h>
+#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
+
--- /dev/null
+++ b/third_party/nestegg/halloc/src/macros.h
@@ -1,0 +1,36 @@
+/*
+ * 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 <stddef.h> /* 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
+
--- /dev/null
+++ b/third_party/nestegg/include/nestegg/nestegg.h
@@ -1,0 +1,353 @@
+/*
+ * Copyright © 2010 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#if !defined(NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79)
+#define NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79
+
+#include "vpx/vpx_integer.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/** @mainpage
+
+ @section intro Introduction
+
+ This is the documentation for the <tt>libnestegg</tt> C API.
+ <tt>libnestegg</tt> is a demultiplexing library for <a
+ href="http://www.webmproject.org/code/specs/container/">WebM</a>
+ media files.
+
+ @section example Example code
+
+ @code
+ nestegg * demux_ctx;
+ nestegg_init(&demux_ctx, io, NULL);
+
+ nestegg_packet * pkt;
+ while ((r = nestegg_read_packet(demux_ctx, &pkt)) > 0) {
+ unsigned int track;
+
+ nestegg_packet_track(pkt, &track);
+
+ // This example decodes the first track only.
+ if (track == 0) {
+ unsigned int chunk, chunks;
+
+ nestegg_packet_count(pkt, &chunks);
+
+ // Decode each chunk of data.
+ for (chunk = 0; chunk < chunks; ++chunk) {
+ unsigned char * data;
+ size_t data_size;
+
+ nestegg_packet_data(pkt, chunk, &data, &data_size);
+
+ example_codec_decode(codec_ctx, data, data_size);
+ }
+ }
+
+ nestegg_free_packet(pkt);
+ }
+
+ nestegg_destroy(demux_ctx);
+ @endcode
+*/
+
+
+/** @file
+ The <tt>libnestegg</tt> C API. */
+
+#define NESTEGG_TRACK_VIDEO 0 /**< Track is of type video. */
+#define NESTEGG_TRACK_AUDIO 1 /**< Track is of type audio. */
+
+#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_VIDEO_MONO 0 /**< Track is mono video. */
+#define NESTEGG_VIDEO_STEREO_LEFT_RIGHT 1 /**< Track is side-by-side stereo video. Left first. */
+#define NESTEGG_VIDEO_STEREO_BOTTOM_TOP 2 /**< Track is top-bottom stereo video. Right first. */
+#define NESTEGG_VIDEO_STEREO_TOP_BOTTOM 3 /**< Track is top-bottom stereo video. Left first. */
+#define NESTEGG_VIDEO_STEREO_RIGHT_LEFT 11 /**< Track is side-by-side stereo video. Right first. */
+
+#define NESTEGG_SEEK_SET 0 /**< Seek offset relative to beginning of stream. */
+#define NESTEGG_SEEK_CUR 1 /**< Seek offset relative to current position in stream. */
+#define NESTEGG_SEEK_END 2 /**< Seek offset relative to end of stream. */
+
+#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. */
+
+/** 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.
+ @retval 0 End of stream.
+ @retval -1 Error. */
+ int (* read)(void * buffer, size_t length, void * userdata);
+
+ /** User supplied seek callback.
+ @param offset Offset within the stream to seek to.
+ @param whence Seek direction. One of #NESTEGG_SEEK_SET,
+ #NESTEGG_SEEK_CUR, or #NESTEGG_SEEK_END.
+ @param userdata The #userdata supplied by the user.
+ @retval 0 Seek succeeded.
+ @retval -1 Error. */
+ int (* seek)(int64_t offset, int whence, void * userdata);
+
+ /** User supplied tell callback.
+ @param userdata The #userdata supplied by the user.
+ @returns Current position within the stream.
+ @retval -1 Error. */
+ int64_t (* tell)(void * userdata);
+
+ /** User supplied pointer to be passed to the IO callbacks. */
+ void * userdata;
+} nestegg_io;
+
+/** Parameters specific to a video track. */
+typedef struct {
+ unsigned int stereo_mode; /**< Video mode. One of #NESTEGG_VIDEO_MONO,
+ #NESTEGG_VIDEO_STEREO_LEFT_RIGHT,
+ #NESTEGG_VIDEO_STEREO_BOTTOM_TOP, or
+ #NESTEGG_VIDEO_STEREO_TOP_BOTTOM. */
+ unsigned int width; /**< Width of the video frame in pixels. */
+ unsigned int height; /**< Height of the video frame in pixels. */
+ unsigned int display_width; /**< Display width of the video frame in pixels. */
+ unsigned int display_height; /**< Display height of the video frame in pixels. */
+ unsigned int crop_bottom; /**< Pixels to crop from the bottom of the frame. */
+ unsigned int crop_top; /**< Pixels to crop from the top of the frame. */
+ unsigned int crop_left; /**< Pixels to crop from the left of the frame. */
+ unsigned int crop_right; /**< Pixels to crop from the right of the frame. */
+} nestegg_video_params;
+
+/** Parameters specific to an audio track. */
+typedef struct {
+ double rate; /**< Sampling rate in Hz. */
+ unsigned int channels; /**< Number of audio channels. */
+ unsigned int depth; /**< Bits per sample. */
+ uint64_t codec_delay; /**< Nanoseconds that must be discarded from the start. */
+ uint64_t seek_preroll;/**< Nanoseconds that must be discarded after a seek. */
+} nestegg_audio_params;
+
+/** Logging callback function pointer. */
+typedef void (* nestegg_log)(nestegg * context, unsigned int severity, char const * format, ...);
+
+/** Initialize a nestegg context. During initialization the parser will
+ read forward in the stream processing all elements until the first
+ block of media is reached. All track metadata has been processed at this point.
+ @param context Storage for the new nestegg context. @see nestegg_destroy
+ @param io User supplied IO context.
+ @param callback Optional logging callback function pointer. May be NULL.
+ @param max_offset Optional maximum offset to be read. Set -1 to ignore.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t max_offset);
+
+/** Destroy a nestegg context and free associated memory.
+ @param context #nestegg context to be freed. @see nestegg_init */
+void nestegg_destroy(nestegg * context);
+
+/** Query the duration of the media stream in nanoseconds.
+ @param context Stream context initialized by #nestegg_init.
+ @param duration Storage for the queried duration.
+ @retval 0 Success.
+ @retval -1 Error. */
+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.
+ @param context Stream context initialized by #nestegg_init.
+ @param scale Storage for the queried scale factor.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_tstamp_scale(nestegg * context, uint64_t * scale);
+
+/** Query the number of tracks in the media stream.
+ @param context Stream context initialized by #nestegg_init.
+ @param tracks Storage for the queried track count.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_track_count(nestegg * context, unsigned int * tracks);
+
+/** Query the start and end offset for a particular cluster.
+ @param context Stream context initialized by #nestegg_init.
+ @param cluster_num Zero-based cluster number; order they appear in cues.
+ @param max_offset Optional maximum offset to be read. Set -1 to ignore.
+ @param start_pos Starting offset of the cluster. -1 means non-existant.
+ @param end_pos Starting offset of the cluster. -1 means non-existant or
+ final cluster.
+ @param tstamp Starting timestamp of the cluster.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_get_cue_point(nestegg * context, unsigned int cluster_num,
+ int64_t max_offset, int64_t * start_pos,
+ 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.
+ @param context Stream context initialized by #nestegg_init.
+ @param offset Absolute offset in bytes.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_offset_seek(nestegg * context, uint64_t offset);
+
+/** Seek @a track to @a tstamp. Stream seek will terminate at the earliest
+ key point in the stream at or before @a tstamp. Other tracks in the
+ stream will output packets with unspecified but nearby timestamps.
+ @param context Stream context initialized by #nestegg_init.
+ @param track Zero based track number.
+ @param tstamp Absolute timestamp in nanoseconds.
+ @retval 0 Success.
+ @retval -1 Error. */
+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 -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 -1 Error. */
+int nestegg_track_codec_id(nestegg * context, unsigned int track);
+
+/** Query the number of codec initialization chunks for @a track. Each
+ chunk of data should be passed to the codec initialization functions in
+ the order returned.
+ @param context Stream context initialized by #nestegg_init.
+ @param track Zero based track number.
+ @param count Storage for the queried chunk count.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_track_codec_data_count(nestegg * context, unsigned int track,
+ unsigned int * count);
+
+/** Get a pointer to chunk number @a item of codec initialization data for
+ @a track.
+ @param context Stream context initialized by #nestegg_init.
+ @param track Zero based track number.
+ @param item Zero based chunk item number.
+ @param data Storage for the queried data pointer.
+ The data is owned by the #nestegg context.
+ @param length Storage for the queried data size.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_track_codec_data(nestegg * context, unsigned int track, unsigned int item,
+ unsigned char ** data, size_t * length);
+
+/** Query the video parameters specified by @a track.
+ @param context Stream context initialized by #nestegg_init.
+ @param track Zero based track number.
+ @param params Storage for the queried video parameters.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_track_video_params(nestegg * context, unsigned int track,
+ nestegg_video_params * params);
+
+/** Query the audio parameters specified by @a track.
+ @param context Stream context initialized by #nestegg_init.
+ @param track Zero based track number.
+ @param params Storage for the queried audio parameters.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_track_audio_params(nestegg * context, unsigned int track,
+ nestegg_audio_params * params);
+
+/** 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
+ forward. @see nestegg_free_packet
+ @param context Context returned by #nestegg_init.
+ @param packet Storage for the returned nestegg_packet.
+ @retval 1 Additional packets may be read in subsequent calls.
+ @retval 0 End of stream.
+ @retval -1 Error. */
+int nestegg_read_packet(nestegg * context, nestegg_packet ** packet);
+
+/** Destroy a nestegg_packet and free associated memory.
+ @param packet #nestegg_packet to be freed. @see nestegg_read_packet */
+void nestegg_free_packet(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.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_packet_track(nestegg_packet * packet, unsigned int * track);
+
+/** Query the time stamp in nanoseconds of @a packet.
+ @param packet Packet initialized by #nestegg_read_packet.
+ @param tstamp Storage for the queried timestamp in nanoseconds.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_packet_tstamp(nestegg_packet * packet, uint64_t * tstamp);
+
+/** 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.
+ @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.
+ The data is owned by the #nestegg_packet packet.
+ @param length Storage for the queried data size.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_packet_data(nestegg_packet * packet, unsigned int item,
+ unsigned char ** data, size_t * length);
+
+/** Returns discard_padding for given packet
+ @param packet Packet initialized by #nestegg_read_packet.
+ @param discard_padding pointer to store discard padding in.
+ @retval 0 Success.
+ @retval -1 Error. */
+int nestegg_packet_discard_padding(nestegg_packet * packet,
+ int64_t * discard_padding);
+
+/** 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. */
+int nestegg_sniff(unsigned char const * buffer, size_t length);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79 */
--- /dev/null
+++ b/third_party/nestegg/src/nestegg.c
@@ -1,0 +1,2306 @@
+/*
+ * Copyright © 2010 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "third_party/nestegg/halloc/halloc.h"
+#include "third_party/nestegg/include/nestegg/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
+
+/* Global Elements */
+#define ID_VOID 0xec
+#define ID_CRC32 0xbf
+
+/* WebM Elements */
+#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
+
+/* Info Elements */
+#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
+
+/* BlockGroup Elements */
+#define ID_BLOCK 0xa1
+#define ID_BLOCK_DURATION 0x9b
+#define ID_REFERENCE_BLOCK 0xfb
+#define ID_DISCARD_PADDING 0x75a2
+
+/* 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
+
+/* Video Elements */
+#define ID_VIDEO 0xe0
+#define ID_STEREO_MODE 0x53b8
+#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
+
+/* 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
+
+/* 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)
+
+/* 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)
+
+/* Block Header Flags */
+#define BLOCK_FLAGS_LACING 6
+
+/* Lacing Constants */
+#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
+
+/* 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"
+
+enum vint_mask {
+ MASK_NONE,
+ MASK_FIRST_BIT
+};
+
+struct ebml_binary {
+ unsigned char * data;
+ size_t length;
+};
+
+struct ebml_list_node {
+ struct ebml_list_node * next;
+ uint64_t id;
+ void * data;
+};
+
+struct ebml_list {
+ struct ebml_list_node * head;
+ struct ebml_list_node * tail;
+};
+
+struct ebml_type {
+ union ebml_value {
+ uint64_t u;
+ double f;
+ int64_t i;
+ char * s;
+ struct ebml_binary b;
+ } v;
+ enum ebml_type_enum type;
+ int read;
+};
+
+/* 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;
+ struct ebml_type doctype;
+ struct ebml_type doctype_version;
+ struct ebml_type doctype_read_version;
+};
+
+/* Matroksa Definitions */
+struct seek {
+ struct ebml_type id;
+ struct ebml_type position;
+};
+
+struct seek_head {
+ struct ebml_list seek;
+};
+
+struct info {
+ struct ebml_type timecode_scale;
+ struct ebml_type duration;
+};
+
+struct block_group {
+ 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 pixel_width;
+ struct ebml_type pixel_height;
+ struct ebml_type pixel_crop_bottom;
+ struct ebml_type pixel_crop_top;
+ struct ebml_type pixel_crop_left;
+ struct ebml_type pixel_crop_right;
+ struct ebml_type display_width;
+ struct ebml_type display_height;
+};
+
+struct audio {
+ struct ebml_type sampling_frequency;
+ struct ebml_type channels;
+ struct ebml_type bit_depth;
+};
+
+struct track_entry {
+ struct ebml_type number;
+ struct ebml_type uid;
+ struct ebml_type type;
+ struct ebml_type flag_enabled;
+ struct ebml_type flag_default;
+ struct ebml_type flag_lacing;
+ struct ebml_type track_timecode_scale;
+ struct ebml_type language;
+ struct ebml_type codec_id;
+ struct ebml_type codec_private;
+ struct ebml_type codec_delay;
+ struct ebml_type seek_preroll;
+ struct video video;
+ struct audio audio;
+};
+
+struct tracks {
+ struct ebml_list track_entry;
+};
+
+struct cue_track_positions {
+ struct ebml_type track;
+ struct ebml_type cluster_position;
+ struct ebml_type block_number;
+};
+
+struct cue_point {
+ struct ebml_type time;
+ struct ebml_list cue_track_positions;
+};
+
+struct cues {
+ struct ebml_list cue_point;
+};
+
+struct segment {
+ struct ebml_list seek_head;
+ struct info info;
+ struct ebml_list cluster;
+ struct tracks tracks;
+ struct cues cues;
+};
+
+/* Misc. */
+struct pool_ctx {
+ char dummy;
+};
+
+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;
+};
+
+/* Public (opaque) Structures */
+struct nestegg {
+ nestegg_io * io;
+ nestegg_log log;
+ struct pool_ctx * alloc_pool;
+ uint64_t last_id;
+ uint64_t last_size;
+ int last_valid;
+ struct list_node * ancestor;
+ struct ebml ebml;
+ struct segment segment;
+ int64_t segment_offset;
+ unsigned int track_count;
+};
+
+struct nestegg_packet {
+ uint64_t track;
+ uint64_t timecode;
+ struct frame * frame;
+ int64_t discard_padding;
+};
+
+/* Element Descriptor */
+struct ebml_element_desc {
+ char const * name;
+ uint64_t id;
+ enum ebml_type_enum type;
+ size_t offset;
+ unsigned int flags;
+ struct ebml_element_desc * children;
+ size_t size;
+ size_t data_offset;
+};
+
+#define E_FIELD(ID, TYPE, STRUCT, FIELD) \
+ { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, NULL, 0, 0 }
+#define E_MASTER(ID, TYPE, STRUCT, FIELD) \
+ { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_MULTI, ne_ ## FIELD ## _elements, \
+ sizeof(struct FIELD), 0 }
+#define E_SINGLE_MASTER_O(ID, TYPE, STRUCT, FIELD) \
+ { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_OFFSET, ne_ ## FIELD ## _elements, 0, \
+ offsetof(STRUCT, FIELD ## _offset) }
+#define E_SINGLE_MASTER(ID, TYPE, STRUCT, FIELD) \
+ { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, ne_ ## FIELD ## _elements, 0, 0 }
+#define E_SUSPEND(ID, TYPE) \
+ { #ID, ID, TYPE, 0, DESC_FLAG_SUSPEND, NULL, 0, 0 }
+#define E_LAST \
+ { NULL, 0, 0, 0, DESC_FLAG_NONE, NULL, 0, 0 }
+
+/* EBML Element Lists */
+static struct ebml_element_desc ne_ebml_elements[] = {
+ E_FIELD(ID_EBML_VERSION, TYPE_UINT, struct ebml, ebml_version),
+ E_FIELD(ID_EBML_READ_VERSION, TYPE_UINT, struct ebml, ebml_read_version),
+ E_FIELD(ID_EBML_MAX_ID_LENGTH, TYPE_UINT, struct ebml, ebml_max_id_length),
+ E_FIELD(ID_EBML_MAX_SIZE_LENGTH, TYPE_UINT, struct ebml, ebml_max_size_length),
+ E_FIELD(ID_DOCTYPE, TYPE_STRING, struct ebml, doctype),
+ E_FIELD(ID_DOCTYPE_VERSION, TYPE_UINT, struct ebml, doctype_version),
+ E_FIELD(ID_DOCTYPE_READ_VERSION, TYPE_UINT, struct ebml, doctype_read_version),
+ E_LAST
+};
+
+/* WebM Element Lists */
+static struct ebml_element_desc ne_seek_elements[] = {
+ E_FIELD(ID_SEEK_ID, TYPE_BINARY, struct seek, id),
+ E_FIELD(ID_SEEK_POSITION, TYPE_UINT, struct seek, position),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_seek_head_elements[] = {
+ E_MASTER(ID_SEEK, TYPE_MASTER, struct seek_head, seek),
+ E_LAST
+};
+
+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_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_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_PIXEL_WIDTH, TYPE_UINT, struct video, pixel_width),
+ E_FIELD(ID_PIXEL_HEIGHT, TYPE_UINT, struct video, pixel_height),
+ E_FIELD(ID_PIXEL_CROP_BOTTOM, TYPE_UINT, struct video, pixel_crop_bottom),
+ E_FIELD(ID_PIXEL_CROP_TOP, TYPE_UINT, struct video, pixel_crop_top),
+ E_FIELD(ID_PIXEL_CROP_LEFT, TYPE_UINT, struct video, pixel_crop_left),
+ E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right),
+ E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width),
+ E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_audio_elements[] = {
+ E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency),
+ E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels),
+ E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth),
+ 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),
+ E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type),
+ E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled),
+ E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default),
+ E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing),
+ E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale),
+ E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language),
+ E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id),
+ E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private),
+ E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay),
+ E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll),
+ E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video),
+ E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_tracks_elements[] = {
+ E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_cue_track_positions_elements[] = {
+ E_FIELD(ID_CUE_TRACK, TYPE_UINT, struct cue_track_positions, track),
+ E_FIELD(ID_CUE_CLUSTER_POSITION, TYPE_UINT, struct cue_track_positions, cluster_position),
+ E_FIELD(ID_CUE_BLOCK_NUMBER, TYPE_UINT, struct cue_track_positions, block_number),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_cue_point_elements[] = {
+ E_FIELD(ID_CUE_TIME, TYPE_UINT, struct cue_point, time),
+ E_MASTER(ID_CUE_TRACK_POSITIONS, TYPE_MASTER, struct cue_point, cue_track_positions),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_cues_elements[] = {
+ E_MASTER(ID_CUE_POINT, TYPE_MASTER, struct cues, cue_point),
+ E_LAST
+};
+
+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_SINGLE_MASTER(ID_TRACKS, TYPE_MASTER, struct segment, tracks),
+ E_SINGLE_MASTER(ID_CUES, TYPE_MASTER, struct segment, cues),
+ E_LAST
+};
+
+static struct ebml_element_desc ne_top_level_elements[] = {
+ E_SINGLE_MASTER(ID_EBML, TYPE_MASTER, nestegg, ebml),
+ E_SINGLE_MASTER_O(ID_SEGMENT, TYPE_MASTER, nestegg, segment),
+ E_LAST
+};
+
+#undef E_FIELD
+#undef E_MASTER
+#undef E_SINGLE_MASTER_O
+#undef E_SINGLE_MASTER
+#undef E_SUSPEND
+#undef E_LAST
+
+static struct pool_ctx *
+ne_pool_init(void)
+{
+ struct pool_ctx * pool;
+
+ pool = h_malloc(sizeof(*pool));
+ if (!pool)
+ abort();
+ return pool;
+}
+
+static void
+ne_pool_destroy(struct pool_ctx * pool)
+{
+ h_free(pool);
+}
+
+static void *
+ne_pool_alloc(size_t size, struct pool_ctx * pool)
+{
+ void * p;
+
+ p = h_malloc(size);
+ if (!p)
+ abort();
+ hattach(p, pool);
+ memset(p, 0, size);
+ return p;
+}
+
+static void *
+ne_alloc(size_t size)
+{
+ void * p;
+
+ p = calloc(1, size);
+ if (!p)
+ abort();
+ return p;
+}
+
+static int
+ne_io_read(nestegg_io * io, void * buffer, size_t length)
+{
+ return io->read(buffer, length, io->userdata);
+}
+
+static int
+ne_io_seek(nestegg_io * io, int64_t offset, int whence)
+{
+ return io->seek(offset, whence, io->userdata);
+}
+
+static int
+ne_io_read_skip(nestegg_io * io, size_t length)
+{
+ size_t get;
+ unsigned char buf[8192];
+ int r = 1;
+
+ while (length > 0) {
+ get = length < sizeof(buf) ? length : sizeof(buf);
+ r = ne_io_read(io, buf, get);
+ if (r != 1)
+ break;
+ length -= get;
+ }
+
+ return r;
+}
+
+static int64_t
+ne_io_tell(nestegg_io * io)
+{
+ return io->tell(io->userdata);
+}
+
+static int
+ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag)
+{
+ int r;
+ unsigned char b;
+ size_t maxlen = 8;
+ unsigned int count = 1, mask = 1 << 7;
+
+ r = ne_io_read(io, &b, 1);
+ if (r != 1)
+ return r;
+
+ while (count < maxlen) {
+ if ((b & mask) != 0)
+ break;
+ mask >>= 1;
+ count += 1;
+ }
+
+ if (length)
+ *length = count;
+ *value = b;
+
+ if (maskflag == MASK_FIRST_BIT)
+ *value = b & ~mask;
+
+ while (--count) {
+ r = ne_io_read(io, &b, 1);
+ if (r != 1)
+ return r;
+ *value <<= 8;
+ *value |= b;
+ }
+
+ return 1;
+}
+
+static int
+ne_read_id(nestegg_io * io, uint64_t * value, uint64_t * length)
+{
+ return ne_bare_read_vint(io, value, length, MASK_NONE);
+}
+
+static int
+ne_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length)
+{
+ return ne_bare_read_vint(io, value, length, MASK_FIRST_BIT);
+}
+
+static int
+ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length)
+{
+ int r;
+ uint64_t uvalue;
+ uint64_t ulength;
+ int64_t svint_subtr[] = {
+ 0x3f, 0x1fff,
+ 0xfffff, 0x7ffffff,
+ 0x3ffffffffLL, 0x1ffffffffffLL,
+ 0xffffffffffffLL, 0x7fffffffffffffLL
+ };
+
+ r = ne_bare_read_vint(io, &uvalue, &ulength, MASK_FIRST_BIT);
+ if (r != 1)
+ return r;
+ *value = uvalue - svint_subtr[ulength - 1];
+ if (length)
+ *length = ulength;
+ return r;
+}
+
+static int
+ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length)
+{
+ unsigned char b;
+ int r;
+
+ if (length == 0 || length > 8)
+ return -1;
+ r = ne_io_read(io, &b, 1);
+ if (r != 1)
+ return r;
+ *val = b;
+ while (--length) {
+ r = ne_io_read(io, &b, 1);
+ if (r != 1)
+ return r;
+ *val <<= 8;
+ *val |= b;
+ }
+ return 1;
+}
+
+static int
+ne_read_int(nestegg_io * io, int64_t * val, uint64_t length)
+{
+ int r;
+ uint64_t uval, base;
+
+ r = ne_read_uint(io, &uval, length);
+ if (r != 1)
+ return r;
+
+ if (length < sizeof(int64_t)) {
+ base = 1;
+ base <<= length * 8 - 1;
+ if (uval >= base) {
+ base = 1;
+ base <<= length * 8;
+ } else {
+ base = 0;
+ }
+ *val = uval - base;
+ } else {
+ *val = (int64_t) uval;
+ }
+
+ return 1;
+}
+
+static int
+ne_read_float(nestegg_io * io, double * val, uint64_t length)
+{
+ union {
+ uint64_t u;
+ float f;
+ double d;
+ } value;
+ int r;
+
+ /* Length == 10 not implemented. */
+ if (length != 4 && length != 8)
+ return -1;
+ r = ne_read_uint(io, &value.u, length);
+ if (r != 1)
+ return r;
+ if (length == 4)
+ *val = value.f;
+ else
+ *val = value.d;
+ return 1;
+}
+
+static int
+ne_read_string(nestegg * ctx, char ** val, uint64_t length)
+{
+ char * str;
+ int r;
+
+ if (length == 0 || length > LIMIT_STRING)
+ return -1;
+ str = ne_pool_alloc(length + 1, ctx->alloc_pool);
+ r = ne_io_read(ctx->io, (unsigned char *) str, length);
+ if (r != 1)
+ return r;
+ str[length] = '\0';
+ *val = str;
+ return 1;
+}
+
+static int
+ne_read_binary(nestegg * ctx, struct ebml_binary * val, uint64_t length)
+{
+ if (length == 0 || length > LIMIT_BINARY)
+ return -1;
+ val->data = ne_pool_alloc(length, ctx->alloc_pool);
+ val->length = length;
+ return ne_io_read(ctx->io, val->data, length);
+}
+
+static int
+ne_get_uint(struct ebml_type type, uint64_t * value)
+{
+ if (!type.read)
+ return -1;
+
+ assert(type.type == TYPE_UINT);
+
+ *value = type.v.u;
+
+ return 0;
+}
+
+static int
+ne_get_float(struct ebml_type type, double * value)
+{
+ if (!type.read)
+ return -1;
+
+ assert(type.type == TYPE_FLOAT);
+
+ *value = type.v.f;
+
+ return 0;
+}
+
+static int
+ne_get_string(struct ebml_type type, char ** value)
+{
+ if (!type.read)
+ return -1;
+
+ assert(type.type == TYPE_STRING);
+
+ *value = type.v.s;
+
+ return 0;
+}
+
+static int
+ne_get_binary(struct ebml_type type, struct ebml_binary * value)
+{
+ if (!type.read)
+ return -1;
+
+ assert(type.type == TYPE_BINARY);
+
+ *value = type.v.b;
+
+ return 0;
+}
+
+static int
+ne_is_ancestor_element(uint64_t id, struct list_node * ancestor)
+{
+ struct ebml_element_desc * element;
+
+ for (; ancestor; ancestor = ancestor->previous)
+ for (element = ancestor->node; element->id; ++element)
+ if (element->id == id)
+ return 1;
+
+ return 0;
+}
+
+static struct ebml_element_desc *
+ne_find_element(uint64_t id, struct ebml_element_desc * elements)
+{
+ struct ebml_element_desc * element;
+
+ for (element = elements; element->id; ++element)
+ if (element->id == id)
+ return element;
+
+ return NULL;
+}
+
+static void
+ne_ctx_push(nestegg * ctx, struct ebml_element_desc * ancestor, void * data)
+{
+ struct list_node * item;
+
+ item = ne_alloc(sizeof(*item));
+ item->previous = ctx->ancestor;
+ item->node = ancestor;
+ item->data = data;
+ ctx->ancestor = item;
+}
+
+static void
+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)
+{
+ 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)
+{
+ 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;
+ ctx->last_size = s->last_size;
+ ctx->last_valid = s->last_valid;
+ return 0;
+}
+
+static int
+ne_peek_element(nestegg * ctx, uint64_t * id, uint64_t * size)
+{
+ int r;
+
+ if (ctx->last_valid) {
+ if (id)
+ *id = ctx->last_id;
+ if (size)
+ *size = ctx->last_size;
+ return 1;
+ }
+
+ r = ne_read_id(ctx->io, &ctx->last_id, NULL);
+ if (r != 1)
+ return r;
+
+ r = ne_read_vint(ctx->io, &ctx->last_size, NULL);
+ if (r != 1)
+ return r;
+
+ if (id)
+ *id = ctx->last_id;
+ if (size)
+ *size = ctx->last_size;
+
+ ctx->last_valid = 1;
+
+ return 1;
+}
+
+static int
+ne_read_element(nestegg * ctx, uint64_t * id, uint64_t * size)
+{
+ int r;
+
+ r = ne_peek_element(ctx, id, size);
+ if (r != 1)
+ return r;
+
+ ctx->last_valid = 0;
+
+ return 1;
+}
+
+static void
+ne_read_master(nestegg * ctx, struct ebml_element_desc * desc)
+{
+ struct ebml_list * list;
+ struct ebml_list_node * node, * oldtail;
+
+ assert(desc->type == TYPE_MASTER && desc->flags & DESC_FLAG_MULTI);
+
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "multi master element %llx (%s)",
+ desc->id, desc->name);
+
+ list = (struct ebml_list *) (ctx->ancestor->data + desc->offset);
+
+ node = ne_pool_alloc(sizeof(*node), ctx->alloc_pool);
+ node->id = desc->id;
+ node->data = ne_pool_alloc(desc->size, ctx->alloc_pool);
+
+ oldtail = list->tail;
+ if (oldtail)
+ oldtail->next = node;
+ list->tail = node;
+ if (!list->head)
+ list->head = node;
+
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p", node->data);
+
+ ne_ctx_push(ctx, desc->children, node->data);
+}
+
+static void
+ne_read_single_master(nestegg * ctx, struct ebml_element_desc * desc)
+{
+ assert(desc->type == TYPE_MASTER && !(desc->flags & DESC_FLAG_MULTI));
+
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "single master element %llx (%s)",
+ desc->id, desc->name);
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p (%u)",
+ ctx->ancestor->data + desc->offset, desc->offset);
+
+ ne_ctx_push(ctx, desc->children, ctx->ancestor->data + desc->offset);
+}
+
+static int
+ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length)
+{
+ struct ebml_type * storage;
+ int r = 0;
+
+ storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset);
+
+ if (storage->read) {
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) already read, skipping",
+ desc->id, desc->name);
+ 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:
+ assert(0);
+ break;
+ }
+
+ if (r == 1)
+ storage->read = 1;
+
+ return r;
+}
+
+static int
+ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset)
+{
+ int r;
+ int64_t * data_offset;
+ uint64_t id, size, peeked_id;
+ struct ebml_element_desc * element;
+
+ if (!ctx->ancestor)
+ return -1;
+
+ for (;;) {
+ if (max_offset > 0 && ne_io_tell(ctx->io) >= max_offset) {
+ /* Reached end of offset allowed for parsing - return gracefully */
+ r = 1;
+ break;
+ }
+ 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);
+ 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;
+ assert(id == peeked_id);
+
+ if (element->flags & DESC_FLAG_OFFSET) {
+ data_offset = (int64_t *) (ctx->ancestor->data + element->data_offset);
+ *data_offset = ne_io_tell(ctx->io);
+ if (*data_offset < 0) {
+ r = -1;
+ break;
+ }
+ }
+
+ if (element->type == TYPE_MASTER) {
+ if (element->flags & DESC_FLAG_MULTI)
+ ne_read_master(ctx, element);
+ else
+ ne_read_single_master(ctx, element);
+ continue;
+ } else {
+ r = ne_read_simple(ctx, element, size);
+ if (r < 0)
+ break;
+ }
+ } else if (ne_is_ancestor_element(id, ctx->ancestor->previous)) {
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "parent element %llx", id);
+ if (top_level && ctx->ancestor->node == top_level) {
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "*** parse about to back up past top_level");
+ r = 1;
+ break;
+ }
+ ne_ctx_pop(ctx);
+ } else {
+ r = ne_read_element(ctx, &id, &size);
+ if (r != 1)
+ break;
+
+ if (id != ID_VOID && id != ID_CRC32)
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "unknown element %llx", id);
+ r = ne_io_read_skip(ctx->io, size);
+ if (r != 1)
+ break;
+ }
+ }
+
+ if (r != 1)
+ while (ctx->ancestor)
+ ne_ctx_pop(ctx);
+
+ return r;
+}
+
+static uint64_t
+ne_xiph_lace_value(unsigned char ** np)
+{
+ uint64_t lace;
+ uint64_t value;
+ unsigned char * p = *np;
+
+ lace = *p++;
+ value = lace;
+ while (lace == 255) {
+ lace = *p++;
+ value += lace;
+ }
+
+ *np = p;
+
+ return value;
+}
+
+static int
+ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed)
+{
+ int r;
+ uint64_t lace;
+
+ r = ne_read_uint(io, &lace, 1);
+ if (r != 1)
+ return r;
+ *consumed += 1;
+
+ *value = lace;
+ while (lace == 255) {
+ r = ne_read_uint(io, &lace, 1);
+ if (r != 1)
+ return r;
+ *consumed += 1;
+ *value += lace;
+ }
+
+ return 1;
+}
+
+static int
+ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes)
+{
+ int r;
+ size_t i = 0;
+ uint64_t sum = 0;
+
+ while (--n) {
+ r = ne_read_xiph_lace_value(io, &sizes[i], read);
+ if (r != 1)
+ return r;
+ sum += sizes[i];
+ i += 1;
+ }
+
+ if (*read + sum > block)
+ return -1;
+
+ /* Last frame is the remainder of the block. */
+ sizes[i] = block - *read - sum;
+ return 1;
+}
+
+static int
+ne_read_ebml_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes)
+{
+ int r;
+ uint64_t lace, sum, length;
+ int64_t slace;
+ size_t i = 0;
+
+ r = ne_read_vint(io, &lace, &length);
+ if (r != 1)
+ return r;
+ *read += length;
+
+ sizes[i] = lace;
+ sum = sizes[i];
+
+ i += 1;
+ n -= 1;
+
+ while (--n) {
+ r = ne_read_svint(io, &slace, &length);
+ if (r != 1)
+ return r;
+ *read += length;
+ sizes[i] = sizes[i - 1] + slace;
+ sum += sizes[i];
+ i += 1;
+ }
+
+ if (*read + sum > block)
+ return -1;
+
+ /* Last frame is the remainder of the block. */
+ sizes[i] = block - *read - sum;
+ return 1;
+}
+
+static uint64_t
+ne_get_timecode_scale(nestegg * ctx)
+{
+ uint64_t scale;
+
+ if (ne_get_uint(ctx->segment.info.timecode_scale, &scale) != 0)
+ scale = 1000000;
+
+ return scale;
+}
+
+static int
+ne_map_track_number_to_index(nestegg * ctx,
+ unsigned int track_number,
+ unsigned int * track_index)
+{
+ struct ebml_list_node * node;
+ struct track_entry * t_entry;
+ uint64_t t_number = 0;
+
+ if (!track_index)
+ return -1;
+ *track_index = 0;
+
+ if (track_number == 0)
+ return -1;
+
+ node = ctx->segment.tracks.track_entry.head;
+ while (node) {
+ assert(node->id == ID_TRACK_ENTRY);
+ t_entry = node->data;
+ if (ne_get_uint(t_entry->number, &t_number) != 0)
+ return -1;
+ if (t_number == track_number)
+ return 0;
+ *track_index += 1;
+ node = node->next;
+ }
+
+ return -1;
+}
+
+static struct track_entry *
+ne_find_track_entry(nestegg * ctx, unsigned int track)
+{
+ struct ebml_list_node * node;
+ unsigned int tracks = 0;
+
+ node = ctx->segment.tracks.track_entry.head;
+ while (node) {
+ assert(node->id == ID_TRACK_ENTRY);
+ if (track == tracks)
+ return node->data;
+ tracks += 1;
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+static int
+ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data)
+{
+ 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;
+ unsigned int i, lacing, track;
+ size_t consumed = 0;
+
+ *data = NULL;
+
+ if (block_size > LIMIT_BLOCK)
+ return -1;
+
+ r = ne_read_vint(ctx->io, &track_number, &length);
+ if (r != 1)
+ return r;
+
+ if (track_number == 0)
+ return -1;
+
+ consumed += length;
+
+ r = ne_read_int(ctx->io, &timecode, 2);
+ if (r != 1)
+ return r;
+
+ consumed += 2;
+
+ r = ne_read_uint(ctx->io, &flags, 1);
+ if (r != 1)
+ return r;
+
+ consumed += 1;
+
+ frames = 0;
+
+ /* Flags are different between Block and SimpleBlock, but lacing is
+ encoded the same way. */
+ lacing = (flags & BLOCK_FLAGS_LACING) >> 1;
+
+ switch (lacing) {
+ case LACING_NONE:
+ frames = 1;
+ break;
+ case LACING_XIPH:
+ case LACING_FIXED:
+ case LACING_EBML:
+ r = ne_read_uint(ctx->io, &frames, 1);
+ if (r != 1)
+ return r;
+ consumed += 1;
+ frames += 1;
+ }
+
+ if (frames > 256)
+ return -1;
+
+ switch (lacing) {
+ case LACING_NONE:
+ frame_sizes[0] = block_size - consumed;
+ break;
+ case LACING_XIPH:
+ if (frames == 1)
+ return -1;
+ r = ne_read_xiph_lacing(ctx->io, block_size, &consumed, frames, frame_sizes);
+ if (r != 1)
+ return r;
+ break;
+ case LACING_FIXED:
+ if ((block_size - consumed) % frames)
+ return -1;
+ for (i = 0; i < frames; ++i)
+ frame_sizes[i] = (block_size - consumed) / frames;
+ break;
+ case LACING_EBML:
+ if (frames == 1)
+ return -1;
+ r = ne_read_ebml_lacing(ctx->io, block_size, &consumed, frames, frame_sizes);
+ if (r != 1)
+ return r;
+ break;
+ }
+
+ /* Sanity check unlaced frame sizes against total block size. */
+ total = consumed;
+ for (i = 0; i < frames; ++i)
+ total += frame_sizes[i];
+ if (total > block_size)
+ return -1;
+
+ if (ne_map_track_number_to_index(ctx, track_number, &track) != 0)
+ return -1;
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ 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)
+ return -1;
+
+ abs_timecode = timecode + cluster_tc;
+ if (abs_timecode < 0)
+ return -1;
+
+ pkt = ne_alloc(sizeof(*pkt));
+ pkt->track = track;
+ pkt->timecode = abs_timecode * tc_scale * track_scale;
+
+ 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);
+
+ last = NULL;
+ for (i = 0; i < frames; ++i) {
+ if (frame_sizes[i] > LIMIT_FRAME) {
+ nestegg_free_packet(pkt);
+ return -1;
+ }
+ f = ne_alloc(sizeof(*f));
+ f->data = ne_alloc(frame_sizes[i]);
+ f->length = frame_sizes[i];
+ r = ne_io_read(ctx->io, f->data, frame_sizes[i]);
+ if (r != 1) {
+ free(f->data);
+ free(f);
+ nestegg_free_packet(pkt);
+ return -1;
+ }
+
+ if (!last)
+ pkt->frame = f;
+ else
+ last->next = f;
+ last = f;
+ }
+
+ *data = pkt;
+
+ 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 uint64_t
+ne_buf_read_id(unsigned char const * p, size_t length)
+{
+ uint64_t id = 0;
+
+ while (length--) {
+ id <<= 8;
+ id |= *p++;
+ }
+
+ return id;
+}
+
+static struct seek *
+ne_find_seek_for_id(struct ebml_list_node * seek_head, uint64_t id)
+{
+ struct ebml_list * head;
+ struct ebml_list_node * seek;
+ struct ebml_binary binary_id;
+ struct seek * s;
+
+ while (seek_head) {
+ assert(seek_head->id == ID_SEEK_HEAD);
+ head = seek_head->data;
+ seek = head->head;
+
+ while (seek) {
+ assert(seek->id == ID_SEEK);
+ s = seek->data;
+
+ if (ne_get_binary(s->id, &binary_id) == 0 &&
+ ne_buf_read_id(binary_id.data, binary_id.length) == id)
+ return s;
+
+ seek = seek->next;
+ }
+
+ seek_head = seek_head->next;
+ }
+
+ return NULL;
+}
+
+static struct cue_track_positions *
+ne_find_cue_position_for_track(nestegg * ctx, struct ebml_list_node * node, unsigned int track)
+{
+ struct cue_track_positions * pos = NULL;
+ uint64_t track_number;
+ unsigned int t;
+
+ while (node) {
+ assert(node->id == ID_CUE_TRACK_POSITIONS);
+ pos = node->data;
+ if (ne_get_uint(pos->track, &track_number) != 0)
+ return NULL;
+
+ if (ne_map_track_number_to_index(ctx, track_number, &t) != 0)
+ return NULL;
+
+ if (t == track)
+ return pos;
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+static struct cue_point *
+ne_find_cue_point_for_tstamp(nestegg * ctx, struct ebml_list_node * cue_point, unsigned int track, uint64_t scale, uint64_t tstamp)
+{
+ uint64_t time;
+ struct cue_point * c, * prev = NULL;
+
+ while (cue_point) {
+ assert(cue_point->id == ID_CUE_POINT);
+ c = cue_point->data;
+
+ if (!prev)
+ prev = c;
+
+ if (ne_get_uint(c->time, &time) == 0 && time * scale > tstamp)
+ break;
+
+ if (ne_find_cue_position_for_track(ctx, c->cue_track_positions.head, track) != NULL)
+ prev = c;
+
+ cue_point = cue_point->next;
+ }
+
+ 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, ...)
+{
+ if (ctx && severity && fmt)
+ return;
+}
+
+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;
+
+ /* 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;
+
+ if (ne_get_uint(found->position, &seek_pos) != 0)
+ return -1;
+
+ /* Save old parser state. */
+ r = ne_ctx_save(ctx, &state);
+ if (r != 0)
+ return -1;
+
+ /* Seek and set up parser state for segment-level element (Cues). */
+ r = ne_io_seek(ctx->io, ctx->segment_offset + seek_pos, NESTEGG_SEEK_SET);
+ if (r != 0)
+ return -1;
+ ctx->last_valid = 0;
+
+ r = ne_read_element(ctx, &id, NULL);
+ if (r != 1)
+ return -1;
+
+ if (id != ID_CUES)
+ return -1;
+
+ ctx->ancestor = NULL;
+ ne_ctx_push(ctx, ne_top_level_elements, ctx);
+ ne_ctx_push(ctx, ne_segment_elements, &ctx->segment);
+ ne_ctx_push(ctx, ne_cues_elements, &ctx->segment.cues);
+ /* parser will run until end of cues element. */
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cue elements");
+ r = ne_parse(ctx, ne_cues_elements, max_offset);
+ while (ctx->ancestor)
+ ne_ctx_pop(ctx);
+
+ /* Reset parser state to original state and seek back to old position. */
+ if (ne_ctx_restore(ctx, &state) != 0)
+ return -1;
+
+ if (r < 0)
+ return -1;
+
+ node = ctx->segment.cues.cue_point.head;
+ if (!node)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Three functions that implement the nestegg_io interface, operating on a
+ * sniff_buffer. */
+struct sniff_buffer {
+ unsigned char const * buffer;
+ size_t length;
+ int64_t offset;
+};
+
+static int
+ne_buffer_read(void * buffer, size_t length, void * user_data)
+{
+ struct sniff_buffer * sb = user_data;
+
+ int rv = 1;
+ size_t available = sb->length - sb->offset;
+
+ if (available < length)
+ return 0;
+
+ memcpy(buffer, sb->buffer + sb->offset, length);
+ sb->offset += length;
+
+ return rv;
+}
+
+static int
+ne_buffer_seek(int64_t offset, int whence, void * user_data)
+{
+ struct sniff_buffer * sb = user_data;
+ int64_t o = sb->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;
+ }
+
+ if (o < 0 || o > (int64_t) sb->length)
+ return -1;
+
+ sb->offset = o;
+ return 0;
+}
+
+static int64_t
+ne_buffer_tell(void * user_data)
+{
+ struct sniff_buffer * sb = user_data;
+ return sb->offset;
+}
+
+static int
+ne_match_webm(nestegg_io io, int64_t max_offset)
+{
+ int r;
+ uint64_t id;
+ char * doctype;
+ nestegg * ctx;
+
+ if (!(io.read && io.seek && io.tell))
+ return -1;
+
+ ctx = ne_alloc(sizeof(*ctx));
+
+ ctx->io = ne_alloc(sizeof(*ctx->io));
+ *ctx->io = io;
+ ctx->alloc_pool = ne_pool_init();
+ 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))
+ return -1;
+
+ ctx = ne_alloc(sizeof(*ctx));
+
+ ctx->io = ne_alloc(sizeof(*ctx->io));
+ *ctx->io = io;
+ ctx->log = callback;
+ ctx->alloc_pool = ne_pool_init();
+
+ if (!ctx->log)
+ ctx->log = ne_null_log_callback;
+
+ r = ne_peek_element(ctx, &id, NULL);
+ if (r != 1) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ if (id != ID_EBML) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "ctx %p", ctx);
+
+ ne_ctx_push(ctx, ne_top_level_elements, ctx);
+
+ r = ne_parse(ctx, NULL, max_offset);
+
+ if (r != 1) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ if (ne_get_uint(ctx->ebml.ebml_read_version, &version) != 0)
+ version = 1;
+ if (version != 1) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ if (ne_get_string(ctx->ebml.doctype, &doctype) != 0)
+ doctype = "matroska";
+ if (strcmp(doctype, "webm") != 0) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ if (ne_get_uint(ctx->ebml.doctype_read_version, &docversion) != 0)
+ docversion = 1;
+ if (docversion < 1 || docversion > 2) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ if (!ctx->segment.tracks.track_entry.head) {
+ nestegg_destroy(ctx);
+ return -1;
+ }
+
+ track = ctx->segment.tracks.track_entry.head;
+ ctx->track_count = 0;
+
+ while (track) {
+ ctx->track_count += 1;
+ track = track->next;
+ }
+
+ *context = ctx;
+
+ return 0;
+}
+
+void
+nestegg_destroy(nestegg * ctx)
+{
+ while (ctx->ancestor)
+ ne_ctx_pop(ctx);
+ ne_pool_destroy(ctx->alloc_pool);
+ free(ctx->io);
+ free(ctx);
+}
+
+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;
+
+ tc_scale = ne_get_timecode_scale(ctx);
+
+ *duration = (uint64_t) (unscaled_duration * tc_scale);
+ return 0;
+}
+
+int
+nestegg_tstamp_scale(nestegg * ctx, uint64_t * scale)
+{
+ *scale = ne_get_timecode_scale(ctx);
+ return 0;
+}
+
+int
+nestegg_track_count(nestegg * ctx, unsigned int * tracks)
+{
+ *tracks = ctx->track_count;
+ return 0;
+}
+
+int
+nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offset,
+ int64_t * start_pos, int64_t * end_pos, uint64_t * tstamp)
+{
+ int range_obtained = 0;
+ unsigned int cluster_count = 0;
+ struct cue_point * cue_point;
+ struct cue_track_positions * pos;
+ uint64_t seek_pos, track_number, tc_scale, time;
+ struct ebml_list_node * cues_node = ctx->segment.cues.cue_point.head;
+ struct ebml_list_node * cue_pos_node = NULL;
+ unsigned int track = 0, track_count = 0, track_index;
+
+ if (!start_pos || !end_pos || !tstamp)
+ return -1;
+
+ /* Initialise return values */
+ *start_pos = -1;
+ *end_pos = -1;
+ *tstamp = 0;
+
+ if (!cues_node) {
+ ne_init_cue_points(ctx, max_offset);
+ cues_node = ctx->segment.cues.cue_point.head;
+ /* Verify cues have been added to context. */
+ if (!cues_node)
+ return -1;
+ }
+
+ nestegg_track_count(ctx, &track_count);
+
+ tc_scale = ne_get_timecode_scale(ctx);
+
+ while (cues_node && !range_obtained) {
+ assert(cues_node->id == ID_CUE_POINT);
+ cue_point = cues_node->data;
+ cue_pos_node = cue_point->cue_track_positions.head;
+ 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++) {
+ if (ne_get_uint(pos->track, &track_number) != 0)
+ return -1;
+
+ if (ne_map_track_number_to_index(ctx, track_number, &track_index) != 0)
+ return -1;
+
+ if (track_index == track) {
+ if (ne_get_uint(pos->cluster_position, &seek_pos) != 0)
+ return -1;
+ if (cluster_count == cluster_num) {
+ *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;
+ range_obtained = 1;
+ break;
+ }
+ cluster_count++;
+ }
+ }
+ cue_pos_node = cue_pos_node->next;
+ }
+ cues_node = cues_node->next;
+ }
+
+ return 0;
+}
+
+int
+nestegg_offset_seek(nestegg * ctx, uint64_t offset)
+{
+ int r;
+
+ /* 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);
+
+ return 0;
+}
+
+int
+nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp)
+{
+ int r;
+ struct cue_point * cue_point;
+ struct cue_track_positions * pos;
+ uint64_t seek_pos, tc_scale;
+
+ /* If there are no cues loaded, check for cues element in the seek head
+ and load it. */
+ if (!ctx->segment.cues.cue_point.head) {
+ r = ne_init_cue_points(ctx, -1);
+ if (r != 0)
+ return -1;
+ }
+
+ tc_scale = ne_get_timecode_scale(ctx);
+
+ cue_point = ne_find_cue_point_for_tstamp(ctx, ctx->segment.cues.cue_point.head,
+ track, tc_scale, tstamp);
+ if (!cue_point)
+ return -1;
+
+ pos = ne_find_cue_position_for_track(ctx, cue_point->cue_track_positions.head, track);
+ if (pos == NULL)
+ return -1;
+
+ if (ne_get_uint(pos->cluster_position, &seek_pos) != 0)
+ return -1;
+
+ /* Seek and set up parser state for segment-level element (Cluster). */
+ r = nestegg_offset_seek(ctx, ctx->segment_offset + seek_pos);
+ ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cluster elements");
+ r = ne_parse(ctx, NULL, -1);
+ if (r != 1)
+ return -1;
+
+ if (!ne_is_suspend_element(ctx->last_id))
+ return -1;
+
+ return 0;
+}
+
+int
+nestegg_track_type(nestegg * ctx, unsigned int track)
+{
+ struct track_entry * entry;
+ uint64_t type;
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ return -1;
+
+ if (ne_get_uint(entry->type, &type) != 0)
+ return -1;
+
+ if (type & TRACK_TYPE_VIDEO)
+ return NESTEGG_TRACK_VIDEO;
+
+ if (type & TRACK_TYPE_AUDIO)
+ return NESTEGG_TRACK_AUDIO;
+
+ return -1;
+}
+
+int
+nestegg_track_codec_id(nestegg * ctx, unsigned int track)
+{
+ char * codec_id;
+ struct track_entry * entry;
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ return -1;
+
+ if (ne_get_string(entry->codec_id, &codec_id) != 0)
+ return -1;
+
+ if (strcmp(codec_id, TRACK_ID_VP8) == 0)
+ return NESTEGG_CODEC_VP8;
+
+ if (strcmp(codec_id, TRACK_ID_VP9) == 0)
+ return NESTEGG_CODEC_VP9;
+
+ if (strcmp(codec_id, TRACK_ID_VORBIS) == 0)
+ return NESTEGG_CODEC_VORBIS;
+
+ if (strcmp(codec_id, TRACK_ID_OPUS) == 0)
+ return NESTEGG_CODEC_OPUS;
+
+ return -1;
+}
+
+int
+nestegg_track_codec_data_count(nestegg * ctx, unsigned int track,
+ unsigned int * count)
+{
+ struct track_entry * entry;
+ struct ebml_binary codec_private;
+ unsigned char * p;
+
+ *count = 0;
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ return -1;
+
+ if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS)
+ return -1;
+
+ if (ne_get_binary(entry->codec_private, &codec_private) != 0)
+ return -1;
+
+ if (codec_private.length < 1)
+ return -1;
+
+ p = codec_private.data;
+ *count = *p + 1;
+
+ if (*count > 3)
+ return -1;
+
+ return 0;
+}
+
+int
+nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item,
+ unsigned char ** data, size_t * length)
+{
+ 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;
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ return -1;
+
+ 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;
+
+ if (count > 3)
+ return -1;
+
+ 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);
+
+ for (i = 0; i < item; ++i) {
+ if (sizes[i] > LIMIT_FRAME)
+ return -1;
+ p += sizes[i];
+ }
+ *data = p;
+ *length = sizes[item];
+ } else {
+ *data = codec_private.data;
+ *length = codec_private.length;
+ }
+
+ return 0;
+}
+
+int
+nestegg_track_video_params(nestegg * ctx, unsigned int track,
+ nestegg_video_params * params)
+{
+ struct track_entry * entry;
+ uint64_t value;
+
+ memset(params, 0, sizeof(*params));
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ return -1;
+
+ if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_VIDEO)
+ return -1;
+
+ value = 0;
+ ne_get_uint(entry->video.stereo_mode, &value);
+ if (value <= NESTEGG_VIDEO_STEREO_TOP_BOTTOM ||
+ value == NESTEGG_VIDEO_STEREO_RIGHT_LEFT)
+ params->stereo_mode = value;
+
+ if (ne_get_uint(entry->video.pixel_width, &value) != 0)
+ return -1;
+ params->width = value;
+
+ if (ne_get_uint(entry->video.pixel_height, &value) != 0)
+ return -1;
+ params->height = value;
+
+ value = 0;
+ ne_get_uint(entry->video.pixel_crop_bottom, &value);
+ params->crop_bottom = value;
+
+ value = 0;
+ ne_get_uint(entry->video.pixel_crop_top, &value);
+ params->crop_top = value;
+
+ value = 0;
+ ne_get_uint(entry->video.pixel_crop_left, &value);
+ params->crop_left = value;
+
+ value = 0;
+ ne_get_uint(entry->video.pixel_crop_right, &value);
+ params->crop_right = value;
+
+ value = params->width;
+ ne_get_uint(entry->video.display_width, &value);
+ params->display_width = value;
+
+ value = params->height;
+ ne_get_uint(entry->video.display_height, &value);
+ params->display_height = value;
+
+ return 0;
+}
+
+int
+nestegg_track_audio_params(nestegg * ctx, unsigned int track,
+ nestegg_audio_params * params)
+{
+ struct track_entry * entry;
+ uint64_t value;
+
+ memset(params, 0, sizeof(*params));
+
+ entry = ne_find_track_entry(ctx, track);
+ if (!entry)
+ return -1;
+
+ if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_AUDIO)
+ return -1;
+
+ params->rate = 8000;
+ ne_get_float(entry->audio.sampling_frequency, ¶ms->rate);
+
+ value = 1;
+ ne_get_uint(entry->audio.channels, &value);
+ params->channels = value;
+
+ value = 16;
+ ne_get_uint(entry->audio.bit_depth, &value);
+ params->depth = value;
+
+ value = 0;
+ ne_get_uint(entry->codec_delay, &value);
+ params->codec_delay = value;
+
+ value = 0;
+ ne_get_uint(entry->seek_preroll, &value);
+ params->seek_preroll = value;
+
+ return 0;
+}
+
+int
+nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt)
+{
+ int r;
+ uint64_t id, size;
+
+ *pkt = NULL;
+
+ for (;;) {
+ r = ne_peek_element(ctx, &id, &size);
+ if (r != 1)
+ 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
+ handle directly. */
+ r = ne_read_block(ctx, id, size, pkt);
+ if (r != 1)
+ return r;
+
+ r = ne_read_discard_padding(ctx, *pkt);
+ if (r != 1)
+ return r;
+
+ return r;
+ }
+
+ r = ne_parse(ctx, NULL, -1);
+ if (r != 1)
+ return r;
+ }
+
+ return 1;
+}
+
+void
+nestegg_free_packet(nestegg_packet * pkt)
+{
+ struct frame * frame;
+
+ while (pkt->frame) {
+ frame = pkt->frame;
+ pkt->frame = frame->next;
+ free(frame->data);
+ free(frame);
+ }
+
+ free(pkt);
+}
+
+int
+nestegg_packet_track(nestegg_packet * pkt, unsigned int * track)
+{
+ *track = pkt->track;
+ return 0;
+}
+
+int
+nestegg_packet_tstamp(nestegg_packet * pkt, uint64_t * tstamp)
+{
+ *tstamp = pkt->timecode;
+ return 0;
+}
+
+int
+nestegg_packet_discard_padding(nestegg_packet * pkt, int64_t * discard_padding)
+{
+ *discard_padding = pkt->discard_padding;
+ return 0;
+}
+
+int
+nestegg_packet_count(nestegg_packet * pkt, unsigned int * count)
+{
+ struct frame * f = pkt->frame;
+
+ *count = 0;
+
+ while (f) {
+ *count += 1;
+ f = f->next;
+ }
+
+ return 0;
+}
+
+int
+nestegg_packet_data(nestegg_packet * pkt, unsigned int item,
+ unsigned char ** data, size_t * length)
+{
+ struct frame * f = pkt->frame;
+ unsigned int count = 0;
+
+ *data = NULL;
+ *length = 0;
+
+ while (f) {
+ if (count == item) {
+ *data = f->data;
+ *length = f->length;
+ return 0;
+ }
+ count += 1;
+ f = f->next;
+ }
+
+ return -1;
+}
+
+int
+nestegg_has_cues(nestegg * ctx)
+{
+ return ctx->segment.cues.cue_point.head ||
+ ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES);
+}
+
+int
+nestegg_sniff(unsigned char const * buffer, size_t length)
+{
+ nestegg_io io;
+ struct sniff_buffer user_data;
+
+ user_data.buffer = buffer;
+ user_data.length = length;
+ user_data.offset = 0;
+
+ io.read = ne_buffer_read;
+ io.seek = ne_buffer_seek;
+ io.tell = ne_buffer_tell;
+ io.userdata = &user_data;
+ return ne_match_webm(io, length);
+}
+
--- /dev/null
+++ b/third_party/nestegg/test/test.c
@@ -1,0 +1,250 @@
+/*
+ * Copyright © 2010 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "nestegg/nestegg.h"
+
+#undef DEBUG
+#define SEEK_TEST
+
+static int
+stdio_read(void * p, size_t length, void * file)
+{
+ size_t r;
+ FILE * fp = file;
+
+ r = fread(p, length, 1, fp);
+ if (r == 0 && feof(fp))
+ return 0;
+ return r == 0 ? -1 : 1;
+}
+
+static int
+stdio_seek(int64_t offset, int whence, void * file)
+{
+ FILE * fp = file;
+ return fseek(fp, offset, whence);
+}
+
+static int64_t
+stdio_tell(void * fp)
+{
+ return ftell(fp);
+}
+
+static void
+log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...)
+{
+ va_list ap;
+ char const * sev = NULL;
+
+#if !defined(DEBUG)
+ if (severity < NESTEGG_LOG_WARNING)
+ return;
+#endif
+
+ switch (severity) {
+ case NESTEGG_LOG_DEBUG:
+ sev = "debug: ";
+ break;
+ case NESTEGG_LOG_WARNING:
+ sev = "warning: ";
+ break;
+ case NESTEGG_LOG_CRITICAL:
+ sev = "critical:";
+ break;
+ default:
+ sev = "unknown: ";
+ }
+
+ fprintf(stderr, "%p %s ", (void *) ctx, sev);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+}
+
+int
+main(int argc, char * argv[])
+{
+ FILE * fp;
+ int r, type;
+ nestegg * ctx;
+ nestegg_audio_params aparams;
+ nestegg_packet * pkt;
+ nestegg_video_params vparams;
+ size_t length, size;
+ uint64_t duration, tstamp, pkt_tstamp;
+ unsigned char * codec_data, * ptr;
+ unsigned int cnt, i, j, track, tracks, pkt_cnt, pkt_track;
+ unsigned int data_items = 0;
+ nestegg_io io = {
+ stdio_read,
+ stdio_seek,
+ stdio_tell,
+ NULL
+ };
+
+ if (argc != 2)
+ return EXIT_FAILURE;
+
+ fp = fopen(argv[1], "rb");
+ if (!fp)
+ return EXIT_FAILURE;
+
+ io.userdata = fp;
+
+ ctx = NULL;
+ r = nestegg_init(&ctx, io, log_callback, -1);
+ if (r != 0)
+ return EXIT_FAILURE;
+
+ nestegg_track_count(ctx, &tracks);
+ nestegg_duration(ctx, &duration);
+#if defined(DEBUG)
+ fprintf(stderr, "media has %u tracks and duration %fs\n", tracks, duration / 1e9);
+#endif
+
+ for (i = 0; i < tracks; ++i) {
+ type = nestegg_track_type(ctx, i);
+#if defined(DEBUG)
+ fprintf(stderr, "track %u: type: %d codec: %d", i,
+ type, nestegg_track_codec_id(ctx, i));
+#endif
+ nestegg_track_codec_data_count(ctx, i, &data_items);
+ for (j = 0; j < data_items; ++j) {
+ nestegg_track_codec_data(ctx, i, j, &codec_data, &length);
+#if defined(DEBUG)
+ fprintf(stderr, " (%p, %u)", codec_data, (unsigned int) length);
+#endif
+ }
+ if (type == NESTEGG_TRACK_VIDEO) {
+ nestegg_track_video_params(ctx, i, &vparams);
+#if defined(DEBUG)
+ fprintf(stderr, " video: %ux%u (d: %ux%u %ux%ux%ux%u)",
+ vparams.width, vparams.height,
+ vparams.display_width, vparams.display_height,
+ vparams.crop_top, vparams.crop_left, vparams.crop_bottom, vparams.crop_right);
+#endif
+ } else if (type == NESTEGG_TRACK_AUDIO) {
+ nestegg_track_audio_params(ctx, i, &aparams);
+#if defined(DEBUG)
+ fprintf(stderr, " audio: %.2fhz %u bit %u channels",
+ aparams.rate, aparams.depth, aparams.channels);
+#endif
+ }
+#if defined(DEBUG)
+ fprintf(stderr, "\n");
+#endif
+ }
+
+#if defined(SEEK_TEST)
+#if defined(DEBUG)
+ fprintf(stderr, "seek to middle\n");
+#endif
+ r = nestegg_track_seek(ctx, 0, duration / 2);
+ if (r == 0) {
+#if defined(DEBUG)
+ fprintf(stderr, "middle ");
+#endif
+ r = nestegg_read_packet(ctx, &pkt);
+ if (r == 1) {
+ nestegg_packet_track(pkt, &track);
+ nestegg_packet_count(pkt, &cnt);
+ nestegg_packet_tstamp(pkt, &tstamp);
+#if defined(DEBUG)
+ fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
+#endif
+ nestegg_free_packet(pkt);
+ } else {
+#if defined(DEBUG)
+ fprintf(stderr, "middle seek failed\n");
+#endif
+ }
+ }
+
+#if defined(DEBUG)
+ fprintf(stderr, "seek to ~end\n");
+#endif
+ r = nestegg_track_seek(ctx, 0, duration - (duration / 10));
+ if (r == 0) {
+#if defined(DEBUG)
+ fprintf(stderr, "end ");
+#endif
+ r = nestegg_read_packet(ctx, &pkt);
+ if (r == 1) {
+ nestegg_packet_track(pkt, &track);
+ nestegg_packet_count(pkt, &cnt);
+ nestegg_packet_tstamp(pkt, &tstamp);
+#if defined(DEBUG)
+ fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
+#endif
+ nestegg_free_packet(pkt);
+ } else {
+#if defined(DEBUG)
+ fprintf(stderr, "end seek failed\n");
+#endif
+ }
+ }
+
+#if defined(DEBUG)
+ fprintf(stderr, "seek to ~start\n");
+#endif
+ r = nestegg_track_seek(ctx, 0, duration / 10);
+ if (r == 0) {
+#if defined(DEBUG)
+ fprintf(stderr, "start ");
+#endif
+ r = nestegg_read_packet(ctx, &pkt);
+ if (r == 1) {
+ nestegg_packet_track(pkt, &track);
+ nestegg_packet_count(pkt, &cnt);
+ nestegg_packet_tstamp(pkt, &tstamp);
+#if defined(DEBUG)
+ fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
+#endif
+ nestegg_free_packet(pkt);
+ } else {
+#if defined(DEBUG)
+ fprintf(stderr, "start seek failed\n");
+#endif
+ }
+ }
+#endif
+
+ while (nestegg_read_packet(ctx, &pkt) > 0) {
+ nestegg_packet_track(pkt, &pkt_track);
+ nestegg_packet_count(pkt, &pkt_cnt);
+ nestegg_packet_tstamp(pkt, &pkt_tstamp);
+
+#if defined(DEBUG)
+ fprintf(stderr, "t %u pts %f frames %u: ", pkt_track, pkt_tstamp / 1e9, pkt_cnt);
+#endif
+
+ for (i = 0; i < pkt_cnt; ++i) {
+ nestegg_packet_data(pkt, i, &ptr, &size);
+#if defined(DEBUG)
+ fprintf(stderr, "%u ", (unsigned int) size);
+#endif
+ }
+#if defined(DEBUG)
+ fprintf(stderr, "\n");
+#endif
+
+ nestegg_free_packet(pkt);
+ }
+
+ nestegg_destroy(ctx);
+ fclose(fp);
+
+ return EXIT_SUCCESS;
+}
--- a/webmdec.c
+++ b/webmdec.c
@@ -12,7 +12,7 @@
#include <stdarg.h>
-#include "nestegg/include/nestegg/nestegg.h"
+#include "third_party/nestegg/include/nestegg/nestegg.h"
static int nestegg_read_cb(void *buffer, size_t length, void *userdata) {
FILE *f = userdata;
--
⑨