shithub: libvpx

Download patch

ref: 8e3ef6c0e13098b8fc76a27dc60d0239ff18bc71
parent: dd18536563a98c78b3fad23b3eced9e589669d1e
parent: bb3b9aa93f5f699c986d8ac5e6d6f3ceffab41c9
author: James Zern <jzern@google.com>
date: Sat Feb 22 06:43:37 EST 2014

Merge "move nestegg to third_party"

diff: cannot open a/nestegg/halloc/src//null: file does not exist: 'a/nestegg/halloc/src//null' diff: cannot open a/nestegg/halloc//null: file does not exist: 'a/nestegg/halloc//null' diff: cannot open a/nestegg/include/nestegg//null: file does not exist: 'a/nestegg/include/nestegg//null' diff: cannot open a/nestegg/include//null: file does not exist: 'a/nestegg/include//null' diff: cannot open a/nestegg/src//null: file does not exist: 'a/nestegg/src//null' diff: cannot open a/nestegg/test//null: file does not exist: 'a/nestegg/test//null' diff: cannot open a/nestegg//null: file does not exist: 'a/nestegg//null' diff: cannot open b/third_party/nestegg/halloc/src//null: file does not exist: 'b/third_party/nestegg/halloc/src//null' diff: cannot open b/third_party/nestegg/halloc//null: file does not exist: 'b/third_party/nestegg/halloc//null' diff: cannot open b/third_party/nestegg/include/nestegg//null: file does not exist: 'b/third_party/nestegg/include/nestegg//null' diff: cannot open b/third_party/nestegg/include//null: file does not exist: 'b/third_party/nestegg/include//null' diff: cannot open b/third_party/nestegg/src//null: file does not exist: 'b/third_party/nestegg/src//null' diff: cannot open b/third_party/nestegg/test//null: file does not exist: 'b/third_party/nestegg/test//null' diff: cannot open b/third_party/nestegg//null: file does not exist: 'b/third_party/nestegg//null'
--- 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, &params->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, &params->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;