diff --git a/Makefile b/Makefile
index a47c1be..8a7edbd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
PROG = oxframe
-SRC = oxframe.c
+SRC = src/oxframe.c
+NETEGGS = src/nestegg.o src/halloc/src/halloc.o
PREFIX ?= /usr/local
BINDIR ?= ${PREFIX}/bin
@@ -8,6 +9,7 @@ MAN1DIR ?= ${PREFIX}/man/man1
CC ?= gcc
CFLAGS ?= -D_FILE_OFFSET_BITS=64
CFLAGS += -Wall -ffast-math -fsigned-char
+CFLAGS += -I. -Isrc -Isrc/halloc
INSTALL = install
@@ -17,19 +19,22 @@ LINKFLAGS ?= -L${PREFIX}/lib
LINKFLAGS += `imlib2-config --libs`
LINKFLAGS += -L/usr/local/lib /usr/local/lib/liboggplay.a
LINKFLAGS += -loggz -lfishsound -ltheora -lvorbisenc -lvorbis -lm -logg -lkate -lpthread
+LINKFLAGS += -lvpx
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}
- ${CC} -Wall -Wno-parentheses -O3 -fforce-addr -fomit-frame-pointer -finline-functions -funroll-loops ${CFLAGS} ${INCLUDEFLAGS} -o ${PROG} $< ${LINKFLAGS}
+${PROG}: ${SRC} ${NETEGGS}
+ ${CC} -Wall -Wno-parentheses -O3 -fforce-addr -fomit-frame-pointer -finline-functions -funroll-loops ${CFLAGS} ${INCLUDEFLAGS} -o ${PROG} $< ${NETEGGS} ${LINKFLAGS}
install: ${PROG}
${INSTALL} -c -m 555 -o root -g bin ${PROG} ${BINDIR}
${INSTALL} -c -m 555 -o root -g bin oxposterframe ${BINDIR}
clean:
- -@rm -f ${PROG} *~ core *.core
+ -@rm -f ${PROG} *~ core *.core src/*.o src/halloc/src/*.o
diff --git a/oxframe.c b/src/oxframe.c
similarity index 54%
rename from oxframe.c
rename to src/oxframe.c
index d2f7852..c975cbc 100644
--- a/oxframe.c
+++ b/src/oxframe.c
@@ -17,10 +17,19 @@
* along with This program. If not, see .
*/
-#include
+#include
+#include
#include
#include
#include
+#include
+#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
@@ -185,16 +194,282 @@ void init_state(oxstate *state) {
state->format = oxImageNotSet;
}
-int main (int argc, char * argv[]) {
- int c,long_option_index;
- OggPlay * player;
- OggPlayReader * reader = NULL;
+//WebM
+static int
+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 fps_num = 25;
int fps_denom = 1;
int granuleshift = 6;
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; yd_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;
const char *optstring = "h:x:y:p:i:o:f:";
@@ -232,7 +507,6 @@ int main (int argc, char * argv[]) {
break;
case 'i':
state.input = optarg;
- reader = oggplay_file_reader_new(state.input);
break;
case 'o':
state.output = optarg;
@@ -256,47 +530,17 @@ int main (int argc, char * argv[]) {
state.format = oxJPG;
}
- player = oggplay_open_with_reader(reader);
-
if (state.input == NULL) {
fprintf (stderr, "please provide input file\n");
exit (1);
}
- if (player == NULL) {
- fprintf (stderr, "could not initialise oggplay with this file\n");
- exit (1);
+
+ if (strstr(&(state.input[strlen(state.input)-5]), ".webm") == NULL) { //ogv
+ extract_frame_ogv(&state);
}
-
- 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);
+ 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;
}