diff --git a/examples.mk b/examples.mk index 3324fd92968596bfd52ba3b4442e81d714c73362..36d20df4d06bb66fc242be2db740c7d68b4d027b 100644 --- a/examples.mk +++ b/examples.mk @@ -25,6 +25,7 @@ vpxdec.SRCS += vpx/vpx_integer.h vpxdec.SRCS += args.c args.h vpxdec.SRCS += ivfdec.c ivfdec.h vpxdec.SRCS += tools_common.c tools_common.h +vpxdec.SRCS += webmdec.c webmdec.h vpxdec.SRCS += nestegg/halloc/halloc.h vpxdec.SRCS += nestegg/halloc/src/align.h vpxdec.SRCS += nestegg/halloc/src/halloc.c diff --git a/tools_common.h b/tools_common.h index 353f90aaa7d53864fa4c329d3d099ecf636018e1..7500523e8c4a0fe828a92120fb50a4c2c9b00a10 100644 --- a/tools_common.h +++ b/tools_common.h @@ -92,11 +92,11 @@ struct VpxInputContext { off_t length; struct FileTypeDetectionBuffer detect; enum VideoFileType file_type; - unsigned int width; - unsigned int height; + uint32_t width; + uint32_t height; int use_i420; int only_i420; - unsigned int fourcc; + uint32_t fourcc; struct VpxRational framerate; #if CONFIG_ENCODERS y4m_input y4m; diff --git a/vpxdec.c b/vpxdec.c index 650899268afa293d33233c28179dcbe842f8d51d..dc2eec826cbd793c56a5cd5d9d138c18eeb78deb 100644 --- a/vpxdec.c +++ b/vpxdec.c @@ -15,6 +15,9 @@ #include <string.h> #include <limits.h> +#include "third_party/libyuv/include/libyuv/scale.h" + +#include "./args.h" #include "./ivfdec.h" #define VPX_CODEC_DISABLE_COMPAT 1 @@ -27,20 +30,19 @@ #endif #if CONFIG_MD5 -#include "md5_utils.h" +#include "./md5_utils.h" #endif #include "./tools_common.h" -#include "nestegg/include/nestegg/nestegg.h" -#include "third_party/libyuv/include/libyuv/scale.h" +#include "./webmdec.h" static const char *exec_name; static const struct { char const *name; const vpx_codec_iface_t *(*iface)(void); - unsigned int fourcc; - unsigned int fourcc_mask; + uint32_t fourcc; + uint32_t fourcc_mask; } ifaces[] = { #if CONFIG_VP8_DECODER {"vp8", vpx_codec_vp8_dx, VP8_FOURCC_MASK, 0x00FFFFFF}, @@ -50,7 +52,11 @@ static const struct { #endif }; -#include "args.h" +struct VpxDecInputContext { + struct VpxInputContext *vpx_input_ctx; + struct WebmInputContext *webm_ctx; +}; + static const arg_def_t looparg = ARG_DEF(NULL, "loops", 1, "Number of times to decode the file"); static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, @@ -162,15 +168,6 @@ void usage_exit() { exit(EXIT_FAILURE); } -struct VpxDecInputContext { - nestegg *nestegg_ctx; - nestegg_packet *pkt; - unsigned int chunk; - unsigned int chunks; - unsigned int video_track; - struct VpxInputContext *vpx_input_ctx; -}; - static int read_frame(struct VpxDecInputContext *input, uint8_t **buf, size_t *bytes_in_buffer, @@ -180,30 +177,8 @@ static int read_frame(struct VpxDecInputContext *input, FILE *infile = input->vpx_input_ctx->file; enum VideoFileType kind = input->vpx_input_ctx->file_type; if (kind == FILE_TYPE_WEBM) { - if (input->chunk >= input->chunks) { - unsigned int track; - - do { - /* End of this packet, get another. */ - if (input->pkt) - nestegg_free_packet(input->pkt); - - if (nestegg_read_packet(input->nestegg_ctx, &input->pkt) <= 0 - || nestegg_packet_track(input->pkt, &track)) - return 1; - - } while (track != input->video_track); - - if (nestegg_packet_count(input->pkt, &input->chunks)) - return 1; - input->chunk = 0; - } - - if (nestegg_packet_data(input->pkt, input->chunk, buf, bytes_in_buffer)) - return 1; - input->chunk++; - - return 0; + return webm_read_frame(input->webm_ctx, + buf, bytes_in_buffer, buffer_size); } else if (kind == FILE_TYPE_RAW) { if (fread(raw_hdr, RAW_FRAME_HDR_SZ, 1, infile) != 1) { if (!feof(infile)) @@ -333,161 +308,12 @@ int file_is_raw(struct VpxInputContext *input) { return is_raw; } - -static int -nestegg_read_cb(void *buffer, size_t length, void *userdata) { - FILE *f = userdata; - - if (fread(buffer, 1, length, f) < length) { - if (ferror(f)) - return -1; - if (feof(f)) - return 0; - } - return 1; -} - - -static int -nestegg_seek_cb(int64_t offset, int whence, void *userdata) { - switch (whence) { - case NESTEGG_SEEK_SET: - whence = SEEK_SET; - break; - case NESTEGG_SEEK_CUR: - whence = SEEK_CUR; - break; - case NESTEGG_SEEK_END: - whence = SEEK_END; - break; - }; - return fseek(userdata, (long)offset, whence) ? -1 : 0; -} - - -static int64_t -nestegg_tell_cb(void *userdata) { - return ftell(userdata); -} - - -static void -nestegg_log_cb(nestegg *context, unsigned int severity, char const *format, - ...) { - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - - -static int -webm_guess_framerate(struct VpxDecInputContext *input) { - unsigned int i; - uint64_t tstamp = 0; - - /* Check to see if we can seek before we parse any data. */ - if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0)) { - warn("WARNING: Failed to guess framerate (no Cues), set to 30fps.\n"); - input->vpx_input_ctx->framerate.numerator = 30; - input->vpx_input_ctx->framerate.denominator = 1; - return 0; - } - - /* Guess the framerate. Read up to 1 second, or 50 video packets, - * whichever comes first. - */ - for (i = 0; tstamp < 1000000000 && i < 50;) { - nestegg_packet *pkt; - unsigned int track; - - if (nestegg_read_packet(input->nestegg_ctx, &pkt) <= 0) - break; - - nestegg_packet_track(pkt, &track); - if (track == input->video_track) { - nestegg_packet_tstamp(pkt, &tstamp); - i++; - } - - nestegg_free_packet(pkt); - } - - if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0)) - goto fail; - - input->vpx_input_ctx->framerate.numerator = (i - 1) * 1000000; - input->vpx_input_ctx->framerate.denominator = (int)(tstamp / 1000); - return 0; -fail: - nestegg_destroy(input->nestegg_ctx); - input->nestegg_ctx = NULL; - rewind(input->vpx_input_ctx->file); - return 1; -} - - -static int -file_is_webm(struct VpxDecInputContext *input) { - unsigned int i, n; - int track_type = -1; - int codec_id; - - nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0}; - nestegg_video_params params; - - io.userdata = input->vpx_input_ctx->file; - if (nestegg_init(&input->nestegg_ctx, io, NULL)) - goto fail; - - if (nestegg_track_count(input->nestegg_ctx, &n)) - goto fail; - - for (i = 0; i < n; i++) { - track_type = nestegg_track_type(input->nestegg_ctx, i); - - if (track_type == NESTEGG_TRACK_VIDEO) - break; - else if (track_type < 0) - goto fail; - } - - codec_id = nestegg_track_codec_id(input->nestegg_ctx, i); - if (codec_id == NESTEGG_CODEC_VP8) { - input->vpx_input_ctx->fourcc = VP8_FOURCC_MASK; - } else if (codec_id == NESTEGG_CODEC_VP9) { - input->vpx_input_ctx->fourcc = VP9_FOURCC_MASK; - } else { - fprintf(stderr, "Not VPx video, quitting.\n"); - exit(1); - } - - input->video_track = i; - - if (nestegg_track_video_params(input->nestegg_ctx, i, ¶ms)) - goto fail; - - input->vpx_input_ctx->framerate.denominator = 0; - input->vpx_input_ctx->framerate.numerator = 0; - input->vpx_input_ctx->width = params.width; - input->vpx_input_ctx->height = params.height; - return 1; -fail: - input->nestegg_ctx = NULL; - rewind(input->vpx_input_ctx->file); - return 0; -} - - void show_progress(int frame_in, int frame_out, unsigned long dx_time) { fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\r", frame_in, frame_out, dx_time, (float)frame_out * 1000000.0 / (float)dx_time); } - void generate_filename(const char *pattern, char *out, size_t q_len, unsigned int d_w, unsigned int d_h, unsigned int frame_in) { @@ -606,7 +432,9 @@ int main_loop(int argc, const char **argv_) { struct VpxDecInputContext input = {0}; struct VpxInputContext vpx_input_ctx = {0}; + struct WebmInputContext webm_ctx = {0}; input.vpx_input_ctx = &vpx_input_ctx; + input.webm_ctx = &webm_ctx; /* Parse command line */ exec_name = argv_[0]; @@ -748,7 +576,7 @@ int main_loop(int argc, const char **argv_) { input.vpx_input_ctx->file = infile; if (file_is_ivf(input.vpx_input_ctx)) input.vpx_input_ctx->file_type = FILE_TYPE_IVF; - else if (file_is_webm(&input)) + else if (file_is_webm(input.webm_ctx, input.vpx_input_ctx)) input.vpx_input_ctx->file_type = FILE_TYPE_WEBM; else if (file_is_raw(input.vpx_input_ctx)) input.vpx_input_ctx->file_type = FILE_TYPE_RAW; @@ -792,7 +620,7 @@ int main_loop(int argc, const char **argv_) { } if (vpx_input_ctx.file_type == FILE_TYPE_WEBM) - if (webm_guess_framerate(&input)) { + if (webm_guess_framerate(input.webm_ctx, input.vpx_input_ctx)) { fprintf(stderr, "Failed to guess framerate -- error parsing " "webm file?\n"); return EXIT_FAILURE; @@ -1036,10 +864,11 @@ fail: if (single_file && !noblit) out_close(out, outfile, do_md5); - if (input.nestegg_ctx) - nestegg_destroy(input.nestegg_ctx); - if (input.vpx_input_ctx->file_type != FILE_TYPE_WEBM) + if (input.vpx_input_ctx->file_type == FILE_TYPE_WEBM) + webm_free(input.webm_ctx); + else free(buf); + fclose(infile); free(argv); diff --git a/webmdec.c b/webmdec.c new file mode 100644 index 0000000000000000000000000000000000000000..4bf7c7e2b56d3f202c6d35dce5fdd4f8fde6b763 --- /dev/null +++ b/webmdec.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2013 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "./webmdec.h" + +#include <stdarg.h> + +#include "nestegg/include/nestegg/nestegg.h" + +static int nestegg_read_cb(void *buffer, size_t length, void *userdata) { + FILE *f = userdata; + + if (fread(buffer, 1, length, f) < length) { + if (ferror(f)) + return -1; + if (feof(f)) + return 0; + } + return 1; +} + +static int nestegg_seek_cb(int64_t offset, int whence, void *userdata) { + switch (whence) { + case NESTEGG_SEEK_SET: + whence = SEEK_SET; + break; + case NESTEGG_SEEK_CUR: + whence = SEEK_CUR; + break; + case NESTEGG_SEEK_END: + whence = SEEK_END; + break; + }; + return fseek(userdata, (int32_t)offset, whence) ? -1 : 0; +} + +static int64_t nestegg_tell_cb(void *userdata) { + return ftell(userdata); +} + +static void nestegg_log_cb(nestegg *context, + unsigned int severity, + char const *format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +int file_is_webm(struct WebmInputContext *webm_ctx, + struct VpxInputContext *vpx_ctx) { + uint32_t i, n; + int track_type = -1; + int codec_id; + + nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0}; + nestegg_video_params params; + + io.userdata = vpx_ctx->file; + if (nestegg_init(&webm_ctx->nestegg_ctx, io, NULL)) + goto fail; + + if (nestegg_track_count(webm_ctx->nestegg_ctx, &n)) + goto fail; + + for (i = 0; i < n; i++) { + track_type = nestegg_track_type(webm_ctx->nestegg_ctx, i); + + if (track_type == NESTEGG_TRACK_VIDEO) + break; + else if (track_type < 0) + goto fail; + } + + codec_id = nestegg_track_codec_id(webm_ctx->nestegg_ctx, i); + if (codec_id == NESTEGG_CODEC_VP8) { + vpx_ctx->fourcc = VP8_FOURCC_MASK; + } else if (codec_id == NESTEGG_CODEC_VP9) { + vpx_ctx->fourcc = VP9_FOURCC_MASK; + } else { + fatal("Not VPx video, quitting.\n"); + } + + webm_ctx->video_track = i; + + if (nestegg_track_video_params(webm_ctx->nestegg_ctx, i, ¶ms)) + goto fail; + + vpx_ctx->framerate.denominator = 0; + vpx_ctx->framerate.numerator = 0; + vpx_ctx->width = params.width; + vpx_ctx->height = params.height; + + return 1; + + fail: + webm_ctx->nestegg_ctx = NULL; + rewind(vpx_ctx->file); + + return 0; +} + +int webm_read_frame(struct WebmInputContext *webm_ctx, + uint8_t **buffer, + size_t *bytes_in_buffer, + size_t *buffer_size) { + if (webm_ctx->chunk >= webm_ctx->chunks) { + uint32_t track; + + do { + /* End of this packet, get another. */ + if (webm_ctx->pkt) + nestegg_free_packet(webm_ctx->pkt); + + if (nestegg_read_packet(webm_ctx->nestegg_ctx, &webm_ctx->pkt) <= 0 || + nestegg_packet_track(webm_ctx->pkt, &track)) { + return 1; + } + } while (track != webm_ctx->video_track); + + if (nestegg_packet_count(webm_ctx->pkt, &webm_ctx->chunks)) + return 1; + + webm_ctx->chunk = 0; + } + + if (nestegg_packet_data(webm_ctx->pkt, webm_ctx->chunk, + buffer, bytes_in_buffer)) { + return 1; + } + + webm_ctx->chunk++; + return 0; +} + +int webm_guess_framerate(struct WebmInputContext *webm_ctx, + struct VpxInputContext *vpx_ctx) { + uint32_t i; + uint64_t tstamp = 0; + + /* Check to see if we can seek before we parse any data. */ + if (nestegg_track_seek(webm_ctx->nestegg_ctx, webm_ctx->video_track, 0)) { + warn("Failed to guess framerate (no Cues), set to 30fps.\n"); + vpx_ctx->framerate.numerator = 30; + vpx_ctx->framerate.denominator = 1; + return 0; + } + + /* Guess the framerate. Read up to 1 second, or 50 video packets, + * whichever comes first. + */ + for (i = 0; tstamp < 1000000000 && i < 50;) { + nestegg_packet *pkt; + uint32_t track; + + if (nestegg_read_packet(webm_ctx->nestegg_ctx, &pkt) <= 0) + break; + + nestegg_packet_track(pkt, &track); + if (track == webm_ctx->video_track) { + nestegg_packet_tstamp(pkt, &tstamp); + ++i; + } + + nestegg_free_packet(pkt); + } + + if (nestegg_track_seek(webm_ctx->nestegg_ctx, webm_ctx->video_track, 0)) + goto fail; + + vpx_ctx->framerate.numerator = (i - 1) * 1000000; + vpx_ctx->framerate.denominator = (int)(tstamp / 1000); + return 0; + + fail: + nestegg_destroy(webm_ctx->nestegg_ctx); + webm_ctx->nestegg_ctx = NULL; + rewind(vpx_ctx->file); + return 1; +} + +void webm_free(struct WebmInputContext *webm_ctx) { + if (webm_ctx && webm_ctx->nestegg_ctx) + nestegg_destroy(webm_ctx->nestegg_ctx); +} diff --git a/webmdec.h b/webmdec.h new file mode 100644 index 0000000000000000000000000000000000000000..002fbe645a3e862002109eab2e90b01ddfa9e356 --- /dev/null +++ b/webmdec.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBMDEC_H_ +#define WEBMDEC_H_ + +#include "./tools_common.h" + +struct nestegg; +struct nestegg_packet; +struct VpxInputContext; + +struct WebmInputContext { + uint32_t chunk; + uint32_t chunks; + uint32_t video_track; + struct nestegg *nestegg_ctx; + struct nestegg_packet *pkt; +}; + +int file_is_webm(struct WebmInputContext *webm_ctx, + struct VpxInputContext *vpx_ctx); + +int webm_read_frame(struct WebmInputContext *webm_ctx, + uint8_t **buffer, + size_t *bytes_in_buffer, + size_t *buffer_size); + +int webm_guess_framerate(struct WebmInputContext *webm_ctx, + struct VpxInputContext *vpx_ctx); + +void webm_free(struct WebmInputContext *webm_ctx); + +#endif // WEBMDEC_H_