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; }