merge vp8 branch

This commit is contained in:
j 2010-05-23 00:18:00 +02:00
commit 76147b7f2f
11 changed files with 3084 additions and 45 deletions

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

288
src/nestegg.h Normal file
View 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 */

View file

@ -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"); if (strstr(&(state.input[strlen(state.input)-5]), ".webm") == NULL) { //ogv
exit (1); extract_frame_ogv(&state);
} }
else { // .webm using nestegg + libvpx
for (i = 0; i < oggplay_get_num_tracks (player); i++) { extract_frame_webm(&state);
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 > 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;
} }