merge vp8 branch
This commit is contained in:
commit
76147b7f2f
11 changed files with 3084 additions and 45 deletions
13
Makefile
13
Makefile
|
@ -1,5 +1,6 @@
|
||||||
PROG = oxframe
|
PROG = oxframe
|
||||||
SRC = oxframe.c
|
SRC = src/oxframe.c
|
||||||
|
NETEGGS = src/nestegg.o src/halloc/src/halloc.o
|
||||||
|
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BINDIR ?= ${PREFIX}/bin
|
BINDIR ?= ${PREFIX}/bin
|
||||||
|
@ -8,6 +9,7 @@ MAN1DIR ?= ${PREFIX}/man/man1
|
||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
CFLAGS ?= -D_FILE_OFFSET_BITS=64
|
CFLAGS ?= -D_FILE_OFFSET_BITS=64
|
||||||
CFLAGS += -Wall -ffast-math -fsigned-char
|
CFLAGS += -Wall -ffast-math -fsigned-char
|
||||||
|
CFLAGS += -I. -Isrc -Isrc/halloc
|
||||||
|
|
||||||
INSTALL = install
|
INSTALL = install
|
||||||
|
|
||||||
|
@ -17,19 +19,22 @@ LINKFLAGS ?= -L${PREFIX}/lib
|
||||||
LINKFLAGS += `imlib2-config --libs`
|
LINKFLAGS += `imlib2-config --libs`
|
||||||
LINKFLAGS += -L/usr/local/lib /usr/local/lib/liboggplay.a
|
LINKFLAGS += -L/usr/local/lib /usr/local/lib/liboggplay.a
|
||||||
LINKFLAGS += -loggz -lfishsound -ltheora -lvorbisenc -lvorbis -lm -logg -lkate -lpthread
|
LINKFLAGS += -loggz -lfishsound -ltheora -lvorbisenc -lvorbis -lm -logg -lkate -lpthread
|
||||||
|
LINKFLAGS += -lvpx
|
||||||
|
|
||||||
|
|
||||||
all: ${PROG}
|
all: ${PROG}
|
||||||
|
|
||||||
|
src/nestegg.o: src/nestegg.h src/nestegg.c src/halloc/halloc.h
|
||||||
|
src/halloc/src/halloc.o: src/halloc/src/halloc.c src/halloc/halloc.h src/halloc/src/align.h src/halloc/src/hlist.h src/halloc/src/macros.h
|
||||||
|
|
||||||
|
|
||||||
${PROG}: ${SRC}
|
${PROG}: ${SRC} ${NETEGGS}
|
||||||
${CC} -Wall -Wno-parentheses -O3 -fforce-addr -fomit-frame-pointer -finline-functions -funroll-loops ${CFLAGS} ${INCLUDEFLAGS} -o ${PROG} $< ${LINKFLAGS}
|
${CC} -Wall -Wno-parentheses -O3 -fforce-addr -fomit-frame-pointer -finline-functions -funroll-loops ${CFLAGS} ${INCLUDEFLAGS} -o ${PROG} $< ${NETEGGS} ${LINKFLAGS}
|
||||||
|
|
||||||
install: ${PROG}
|
install: ${PROG}
|
||||||
${INSTALL} -c -m 555 -o root -g bin ${PROG} ${BINDIR}
|
${INSTALL} -c -m 555 -o root -g bin ${PROG} ${BINDIR}
|
||||||
${INSTALL} -c -m 555 -o root -g bin oxposterframe ${BINDIR}
|
${INSTALL} -c -m 555 -o root -g bin oxposterframe ${BINDIR}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-@rm -f ${PROG} *~ core *.core
|
-@rm -f ${PROG} *~ core *.core src/*.o src/halloc/src/*.o
|
||||||
|
|
||||||
|
|
14
src/halloc/Makefile
Normal file
14
src/halloc/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
CFLAGS = -I. -ansi -Wall -pedantic
|
||||||
|
|
||||||
|
LIBNAME = libhalloc.a
|
||||||
|
OBJS = src/halloc.o
|
||||||
|
|
||||||
|
$(LIBNAME): $(OBJS)
|
||||||
|
ar rcs $(LIBNAME) $(OBJS)
|
||||||
|
|
||||||
|
install: $(LIBNAME)
|
||||||
|
cp halloc.h /usr/include
|
||||||
|
cp $(LIBNAME) /usr/lib
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(LIBNAME) $(OBJS)
|
45
src/halloc/README
Normal file
45
src/halloc/README
Normal file
|
@ -0,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.
|
||||||
|
|
43
src/halloc/halloc.h
Normal file
43
src/halloc/halloc.h
Normal file
|
@ -0,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
|
||||||
|
|
36
src/halloc/src/align.h
Normal file
36
src/halloc/src/align.h
Normal file
|
@ -0,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
|
||||||
|
|
254
src/halloc/src/halloc.c
Normal file
254
src/halloc/src/halloc.c
Normal file
|
@ -0,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 "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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
136
src/halloc/src/hlist.h
Normal file
136
src/halloc/src/hlist.h
Normal file
|
@ -0,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
|
||||||
|
|
36
src/halloc/src/macros.h
Normal file
36
src/halloc/src/macros.h
Normal file
|
@ -0,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*)(- 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
|
||||||
|
|
1938
src/nestegg.c
Normal file
1938
src/nestegg.c
Normal file
File diff suppressed because it is too large
Load diff
288
src/nestegg.h
Normal file
288
src/nestegg.h
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2010 Matthew Gregan <kinetik@flim.org>
|
||||||
|
*
|
||||||
|
* This program is made available under an ISC-style license. See the
|
||||||
|
* accompanying file LICENSE for details.
|
||||||
|
*/
|
||||||
|
#ifndef NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79
|
||||||
|
#define NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
typedef __int64 int64_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @mainpage
|
||||||
|
|
||||||
|
@section intro Introduction
|
||||||
|
|
||||||
|
This is the documentation fot the <tt>libnestegg</tt> C API.
|
||||||
|
<tt>libnestegg</tt> is a demultiplexing library for <a
|
||||||
|
href="http://www.matroska.org/">Matroska</a> and <a
|
||||||
|
href="http://www.webmproject.org/">WebMedia</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_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 userptr 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 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. */
|
||||||
|
} 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.
|
||||||
|
@retval 0 Success.
|
||||||
|
@retval -1 Error. */
|
||||||
|
int nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback);
|
||||||
|
|
||||||
|
/** 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 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);
|
||||||
|
|
||||||
|
/** 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_VIDEO 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);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NESTEGG_671cac2a_365d_ed69_d7a3_4491d3538d79 */
|
|
@ -17,10 +17,19 @@
|
||||||
* along with This program. If not, see <http://www.gnu.org/licenses/>.
|
* along with This program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <oggplay/oggplay.h>
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <oggplay/oggplay.h>
|
||||||
|
#include "nestegg.h"
|
||||||
|
|
||||||
|
#define VPX_CODEC_DISABLE_COMPAT 1
|
||||||
|
#include "vpx/vpx_decoder.h"
|
||||||
|
#include "vpx/vp8dx.h"
|
||||||
|
|
||||||
|
#define interface (&vpx_codec_vp8_dx_algo)
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -185,16 +194,282 @@ void init_state(oxstate *state) {
|
||||||
state->format = oxImageNotSet;
|
state->format = oxImageNotSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main (int argc, char * argv[]) {
|
|
||||||
|
|
||||||
int c,long_option_index;
|
//WebM
|
||||||
OggPlay * player;
|
static int
|
||||||
OggPlayReader * reader = NULL;
|
stdio_read(void * p, size_t length, void * fp)
|
||||||
|
{
|
||||||
|
size_t r;
|
||||||
|
|
||||||
|
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 * fp)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
#ifndef 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");
|
||||||
|
}
|
||||||
|
static void die_codec(vpx_codec_ctx_t *ctx, const char *s) {
|
||||||
|
const char *detail = vpx_codec_error_detail(ctx);
|
||||||
|
|
||||||
|
printf("%s: %s\n", s, vpx_codec_error(ctx));
|
||||||
|
if(detail)
|
||||||
|
printf(" %s\n",detail);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int extract_frame_ogv(oxstate *state) {
|
||||||
int i;
|
int i;
|
||||||
int fps_num = 25;
|
int fps_num = 25;
|
||||||
int fps_denom = 1;
|
int fps_denom = 1;
|
||||||
int granuleshift = 6;
|
int granuleshift = 6;
|
||||||
long max_num, offset;
|
long max_num, offset;
|
||||||
|
|
||||||
|
OggPlay * player;
|
||||||
|
OggPlayReader * reader = NULL;
|
||||||
|
|
||||||
|
reader = oggplay_file_reader_new(state->input);
|
||||||
|
player = oggplay_open_with_reader(reader);
|
||||||
|
|
||||||
|
if (player == NULL) {
|
||||||
|
fprintf (stderr, "could not initialise oggplay with this file\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < oggplay_get_num_tracks (player); i++) {
|
||||||
|
if (oggplay_get_track_type (player, i) == OGGZ_CONTENT_THEORA) {
|
||||||
|
oggplay_set_callback_num_frames (player, i, 1);
|
||||||
|
oggplay_get_video_fps(player, i, &fps_denom, &fps_num);
|
||||||
|
}
|
||||||
|
oggplay_set_track_active(player, i);
|
||||||
|
}
|
||||||
|
oggplay_set_data_callback(player, dump_frame_callback, state);
|
||||||
|
|
||||||
|
max_num = 1 << granuleshift;
|
||||||
|
offset = (1000 * max_num * fps_denom) / fps_num;
|
||||||
|
|
||||||
|
state->duration = oggplay_get_duration(player);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (frame_pos > state->duration) {
|
||||||
|
fprintf (stderr, "can not seek to frame later than duration\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(state->frame_pos - offset > 0) {
|
||||||
|
if (oggplay_seek(player, state->frame_pos - offset) == E_OGGPLAY_CANT_SEEK) {
|
||||||
|
fprintf (stderr, "failed to seeek to %ld\n", state->frame_pos);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oggplay_start_decoding(player);
|
||||||
|
|
||||||
|
oggplay_close (player);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int extract_frame_webm(oxstate *state) {
|
||||||
|
FILE * fp;
|
||||||
|
int r, type, codec_id;
|
||||||
|
nestegg * ctx;
|
||||||
|
nestegg_packet * pkt;
|
||||||
|
nestegg_video_params vparams;
|
||||||
|
uint64_t duration, pkt_tstamp;
|
||||||
|
unsigned int i, tracks;
|
||||||
|
int flags = 0;
|
||||||
|
int done = 0;
|
||||||
|
vpx_codec_ctx_t codec;
|
||||||
|
vpx_image_t *img;
|
||||||
|
|
||||||
|
//in nanoseconds
|
||||||
|
uint64_t seek_tstamp = (uint64_t)state->frame_pos*1000000;
|
||||||
|
|
||||||
|
nestegg_io io = {
|
||||||
|
stdio_read,
|
||||||
|
stdio_seek,
|
||||||
|
stdio_tell,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
fp = fopen(state->input, "rb");
|
||||||
|
if (!fp) {
|
||||||
|
fprintf (stderr, "could not open input file\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
io.userdata = fp;
|
||||||
|
ctx = NULL;
|
||||||
|
r = nestegg_init(&ctx, io, log_callback);
|
||||||
|
if (r != 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
nestegg_track_count(ctx, &tracks);
|
||||||
|
nestegg_duration(ctx, &duration);
|
||||||
|
|
||||||
|
if (seek_tstamp > duration) {
|
||||||
|
fprintf (stderr, "can not seek to frame later than duration\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize codec */
|
||||||
|
if(vpx_codec_dec_init(&codec, interface, NULL, flags))
|
||||||
|
die_codec(&codec, "Failed to initialize decoder");
|
||||||
|
|
||||||
|
for (i = 0; i < tracks; ++i) {
|
||||||
|
type = nestegg_track_type(ctx, i);
|
||||||
|
codec_id = nestegg_track_codec_id(ctx, i);
|
||||||
|
|
||||||
|
if (type == NESTEGG_TRACK_VIDEO && codec_id == NESTEGG_CODEC_VP8) {
|
||||||
|
nestegg_track_video_params(ctx, i, &vparams);
|
||||||
|
|
||||||
|
if(!nestegg_track_seek(ctx, i, seek_tstamp)) {
|
||||||
|
|
||||||
|
while (!done && (r = nestegg_read_packet(ctx, &pkt)) > 0) {
|
||||||
|
unsigned int track;
|
||||||
|
|
||||||
|
nestegg_packet_track(pkt, &track);
|
||||||
|
if(nestegg_packet_tstamp(pkt, &pkt_tstamp) < 0) {
|
||||||
|
fprintf (stderr, "faild to get timestamp\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// only look for video track
|
||||||
|
if (track == i) {
|
||||||
|
unsigned int chunk, chunks;
|
||||||
|
|
||||||
|
nestegg_packet_count(pkt, &chunks);
|
||||||
|
|
||||||
|
// Decode each chunk of data.
|
||||||
|
for (chunk = 0; chunk < chunks; ++chunk) {
|
||||||
|
vpx_codec_iter_t iter = NULL;
|
||||||
|
unsigned char * data;
|
||||||
|
size_t data_size;
|
||||||
|
|
||||||
|
nestegg_packet_data(pkt, chunk, &data, &data_size);
|
||||||
|
|
||||||
|
/* Decode the frame */
|
||||||
|
if(vpx_codec_decode(&codec, data, data_size, NULL, 0))
|
||||||
|
die_codec(&codec, "Failed to decode frame");
|
||||||
|
|
||||||
|
while((img = vpx_codec_get_frame(&codec, &iter))) {
|
||||||
|
//we got a frame...
|
||||||
|
if(seek_tstamp-pkt_tstamp<=0) {
|
||||||
|
unsigned int y;
|
||||||
|
unsigned char *q, *p, *q2, *p2;
|
||||||
|
OggPlayYUVChannels from;
|
||||||
|
OggPlayRGBChannels to;
|
||||||
|
|
||||||
|
done = 1;
|
||||||
|
|
||||||
|
from.y_width = img->d_w;
|
||||||
|
from.y_height = img->d_h;
|
||||||
|
from.uv_width = (1 + img->d_w) / 2;
|
||||||
|
from.uv_height = (1 + img->d_h) / 2;
|
||||||
|
|
||||||
|
from.ptry = malloc(from.y_width * from.y_height);
|
||||||
|
from.ptru = malloc(from.uv_width * from.uv_height);
|
||||||
|
from.ptrv = malloc(from.uv_width * from.uv_height);
|
||||||
|
|
||||||
|
q =img->planes[PLANE_Y];
|
||||||
|
p = from.ptry;
|
||||||
|
for(y=0; y<img->d_h; y++) {
|
||||||
|
memcpy(p, q, img->d_w);
|
||||||
|
p += img->d_w;
|
||||||
|
q += img->stride[PLANE_Y];
|
||||||
|
}
|
||||||
|
q =img->planes[PLANE_U];
|
||||||
|
p = from.ptru;
|
||||||
|
q2 =img->planes[PLANE_V];
|
||||||
|
p2 = from.ptrv;
|
||||||
|
for(y=0; y<(1 + img->d_h) / 2; y++) {
|
||||||
|
memcpy(p, q, (1 + img->d_w) / 2);
|
||||||
|
memcpy(p2, q2, (1 + img->d_w) / 2);
|
||||||
|
p += (1 + img->d_w) / 2;
|
||||||
|
q += img->stride[PLANE_U];
|
||||||
|
p2 += (1 + img->d_w) / 2;
|
||||||
|
q2 += img->stride[PLANE_V];
|
||||||
|
}
|
||||||
|
|
||||||
|
to.ptro = malloc(from.y_width * from.y_height * 4);
|
||||||
|
to.rgb_width = from.y_width;
|
||||||
|
to.rgb_height = from.y_height;
|
||||||
|
|
||||||
|
oggplay_yuv2bgra (&from, &to);
|
||||||
|
|
||||||
|
write_image_file(&to, state);
|
||||||
|
free(from.ptry);
|
||||||
|
free(from.ptru);
|
||||||
|
free(from.ptrv);
|
||||||
|
free(to.ptro);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nestegg_free_packet(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vpx_codec_destroy(&codec))
|
||||||
|
die_codec(&codec, "Failed to destroy codec");
|
||||||
|
|
||||||
|
nestegg_destroy(ctx);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fprintf (stderr, "failed to seek\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char * argv[]) {
|
||||||
|
|
||||||
|
int c,long_option_index;
|
||||||
oxstate state;
|
oxstate state;
|
||||||
|
|
||||||
const char *optstring = "h:x:y:p:i:o:f:";
|
const char *optstring = "h:x:y:p:i:o:f:";
|
||||||
|
@ -232,7 +507,6 @@ int main (int argc, char * argv[]) {
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
state.input = optarg;
|
state.input = optarg;
|
||||||
reader = oggplay_file_reader_new(state.input);
|
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
state.output = optarg;
|
state.output = optarg;
|
||||||
|
@ -256,47 +530,17 @@ int main (int argc, char * argv[]) {
|
||||||
state.format = oxJPG;
|
state.format = oxJPG;
|
||||||
}
|
}
|
||||||
|
|
||||||
player = oggplay_open_with_reader(reader);
|
|
||||||
|
|
||||||
if (state.input == NULL) {
|
if (state.input == NULL) {
|
||||||
fprintf (stderr, "please provide input file\n");
|
fprintf (stderr, "please provide input file\n");
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
if (player == NULL) {
|
|
||||||
fprintf (stderr, "could not initialise oggplay with this file\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < oggplay_get_num_tracks (player); i++) {
|
if (strstr(&(state.input[strlen(state.input)-5]), ".webm") == NULL) { //ogv
|
||||||
if (oggplay_get_track_type (player, i) == OGGZ_CONTENT_THEORA) {
|
extract_frame_ogv(&state);
|
||||||
oggplay_set_callback_num_frames (player, i, 1);
|
|
||||||
oggplay_get_video_fps(player, i, &fps_denom, &fps_num);
|
|
||||||
}
|
}
|
||||||
oggplay_set_track_active(player, i);
|
else { // .webm using nestegg + libvpx
|
||||||
|
extract_frame_webm(&state);
|
||||||
}
|
}
|
||||||
oggplay_set_data_callback(player, dump_frame_callback, &state);
|
|
||||||
|
|
||||||
max_num = 1 << granuleshift;
|
|
||||||
offset = (1000 * max_num * fps_denom) / fps_num;
|
|
||||||
|
|
||||||
state.duration = oggplay_get_duration(player);
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (frame_pos > duration) {
|
|
||||||
fprintf (stderr, "can not seek to frame later than duration\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(state.frame_pos - offset > 0) {
|
|
||||||
if (oggplay_seek(player, state.frame_pos - offset) == E_OGGPLAY_CANT_SEEK) {
|
|
||||||
fprintf (stderr, "failed to seeek to %ld\n", state.frame_pos);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oggplay_start_decoding(player);
|
|
||||||
|
|
||||||
oggplay_close (player);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
Loading…
Reference in a new issue