diff options
author | Vignesh Venkatasubramanian <vigneshv@google.com> | 2014-03-10 09:50:31 -0700 |
---|---|---|
committer | Vignesh Venkatasubramanian <vigneshv@google.com> | 2014-03-11 10:04:38 -0700 |
commit | b08e2e23eec181e9951df33cd704ac294c5407b6 (patch) | |
tree | 8efc1ca8af18169b44e505ea3ac1d224c1d777ab /libvpx/vpxdec.c | |
parent | f0ceed9d146017d218d64ca946d5c2b31356389b (diff) | |
download | android_external_libvpx-b08e2e23eec181e9951df33cd704ac294c5407b6.tar.gz android_external_libvpx-b08e2e23eec181e9951df33cd704ac294c5407b6.tar.bz2 android_external_libvpx-b08e2e23eec181e9951df33cd704ac294c5407b6.zip |
libvpx: Roll latest libvpx and enable VP9 Encoder
Rolling latest libvpx from upstream and generating configurations with VP9
Encoder enabled. Updating a mistake in UPDATING file.
Upstream Hash: 1f08824d6db735e4cacb0419785a789395e42b2b
Change-Id: Ie2c45e2253ade4a9ce88ca640cae39a7ece2d8f4
Diffstat (limited to 'libvpx/vpxdec.c')
-rw-r--r-- | libvpx/vpxdec.c | 938 |
1 files changed, 378 insertions, 560 deletions
diff --git a/libvpx/vpxdec.c b/libvpx/vpxdec.c index 110e4ac..b69e55e 100644 --- a/libvpx/vpxdec.c +++ b/libvpx/vpxdec.c @@ -8,10 +8,6 @@ * be found in the AUTHORS file in the root of the source tree. */ - -/* This is a simple program that reads ivf files and decodes them - * using the new interface. Decoded frames are output as YV12 raw. - */ #include <assert.h> #include <stdio.h> #include <stdlib.h> @@ -19,37 +15,34 @@ #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 -#include "vpx_config.h" +#include "./vpx_config.h" #include "vpx/vpx_decoder.h" +#include "vpx_ports/mem_ops.h" #include "vpx_ports/vpx_timer.h" + #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER #include "vpx/vp8dx.h" #endif -#if CONFIG_MD5 -#include "md5_utils.h" -#endif -#include "tools_common.h" -#include "nestegg/include/nestegg/nestegg.h" -#include "third_party/libyuv/include/libyuv/scale.h" + +#include "./md5_utils.h" + +#include "./tools_common.h" +#include "./webmdec.h" +#include "./y4menc.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; -} ifaces[] = { -#if CONFIG_VP8_DECODER - {"vp8", vpx_codec_vp8_dx, VP8_FOURCC_MASK, 0x00FFFFFF}, -#endif -#if CONFIG_VP9_DECODER - {"vp9", vpx_codec_vp9_dx, VP9_FOURCC_MASK, 0x00FFFFFF}, -#endif +struct VpxDecInputContext { + struct VpxInputContext *vpx_input_ctx; + struct WebmInputContext *webm_ctx; }; -#include "args.h" 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, @@ -83,18 +76,17 @@ static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0, static const arg_def_t scalearg = ARG_DEF("S", "scale", 0, "Scale output frames uniformly"); +static const arg_def_t fb_arg = + ARG_DEF(NULL, "frame-buffers", 1, "Number of frame buffers to use"); -#if CONFIG_MD5 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0, "Compute the MD5 sum of the decoded frame"); -#endif + static const arg_def_t *all_args[] = { &codecarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg, &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile, - &threadsarg, &verbosearg, &scalearg, -#if CONFIG_MD5 + &threadsarg, &verbosearg, &scalearg, &fb_arg, &md5arg, -#endif &error_concealment, NULL }; @@ -126,6 +118,21 @@ static const arg_def_t *vp8_pp_args[] = { }; #endif +static int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst, + FilterMode mode) { + assert(src->fmt == VPX_IMG_FMT_I420); + assert(dst->fmt == VPX_IMG_FMT_I420); + return I420Scale(src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y], + src->planes[VPX_PLANE_U], src->stride[VPX_PLANE_U], + src->planes[VPX_PLANE_V], src->stride[VPX_PLANE_V], + src->d_w, src->d_h, + dst->planes[VPX_PLANE_Y], dst->stride[VPX_PLANE_Y], + dst->planes[VPX_PLANE_U], dst->stride[VPX_PLANE_U], + dst->planes[VPX_PLANE_V], dst->stride[VPX_PLANE_V], + dst->d_w, dst->d_h, + mode); +} + void usage_exit() { int i; @@ -153,435 +160,214 @@ void usage_exit() { ); fprintf(stderr, "\nIncluded decoders:\n\n"); - for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) + for (i = 0; i < get_vpx_decoder_count(); ++i) { + const VpxInterface *const decoder = get_vpx_decoder_by_index(i); fprintf(stderr, " %-6s - %s\n", - ifaces[i].name, - vpx_codec_iface_name(ifaces[i].iface())); + decoder->name, vpx_codec_iface_name(decoder->interface())); + } exit(EXIT_FAILURE); } -static unsigned int mem_get_le16(const void *vmem) { - unsigned int val; - const unsigned char *mem = (const unsigned char *)vmem; - - val = mem[1] << 8; - val |= mem[0]; - return val; -} - -static unsigned int mem_get_le32(const void *vmem) { - unsigned int val; - const unsigned char *mem = (const unsigned char *)vmem; +static int raw_read_frame(FILE *infile, uint8_t **buffer, + size_t *bytes_read, size_t *buffer_size) { + char raw_hdr[RAW_FRAME_HDR_SZ]; + size_t frame_size = 0; - val = mem[3] << 24; - val |= mem[2] << 16; - val |= mem[1] << 8; - val |= mem[0]; - return val; -} - -enum file_kind { - RAW_FILE, - IVF_FILE, - WEBM_FILE -}; - -struct input_ctx { - enum file_kind kind; - FILE *infile; - nestegg *nestegg_ctx; - nestegg_packet *pkt; - unsigned int chunk; - unsigned int chunks; - unsigned int video_track; -}; - -#define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t)) -#define RAW_FRAME_HDR_SZ (sizeof(uint32_t)) -static int read_frame(struct input_ctx *input, - uint8_t **buf, - size_t *buf_sz, - size_t *buf_alloc_sz) { - char raw_hdr[IVF_FRAME_HDR_SZ]; - size_t new_buf_sz; - FILE *infile = input->infile; - enum file_kind kind = input->kind; - if (kind == WEBM_FILE) { - 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, buf_sz)) - return 1; - input->chunk++; - - return 0; - } - /* For both the raw and ivf formats, the frame size is the first 4 bytes - * of the frame header. We just need to special case on the header - * size. - */ - else if (fread(raw_hdr, kind == IVF_FILE - ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1, infile) != 1) { + if (fread(raw_hdr, RAW_FRAME_HDR_SZ, 1, infile) != 1) { if (!feof(infile)) - fprintf(stderr, "Failed to read frame size\n"); - - new_buf_sz = 0; + warn("Failed to read RAW frame size\n"); } else { - new_buf_sz = mem_get_le32(raw_hdr); + const size_t kCorruptFrameThreshold = 256 * 1024 * 1024; + const size_t kFrameTooSmallThreshold = 256 * 1024; + frame_size = mem_get_le32(raw_hdr); - if (new_buf_sz > 256 * 1024 * 1024) { - fprintf(stderr, "Error: Read invalid frame size (%u)\n", - (unsigned int)new_buf_sz); - new_buf_sz = 0; + if (frame_size > kCorruptFrameThreshold) { + warn("Read invalid frame size (%u)\n", (unsigned int)frame_size); + frame_size = 0; } - if (kind == RAW_FILE && new_buf_sz > 256 * 1024) - fprintf(stderr, "Warning: Read invalid frame size (%u)" - " - not a raw file?\n", (unsigned int)new_buf_sz); - - if (new_buf_sz > *buf_alloc_sz) { - uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz); + if (frame_size < kFrameTooSmallThreshold) { + warn("Warning: Read invalid frame size (%u) - not a raw file?\n", + (unsigned int)frame_size); + } + if (frame_size > *buffer_size) { + uint8_t *new_buf = realloc(*buffer, 2 * frame_size); if (new_buf) { - *buf = new_buf; - *buf_alloc_sz = 2 * new_buf_sz; + *buffer = new_buf; + *buffer_size = 2 * frame_size; } else { - fprintf(stderr, "Failed to allocate compressed data buffer\n"); - new_buf_sz = 0; + warn("Failed to allocate compressed data buffer\n"); + frame_size = 0; } } } - *buf_sz = new_buf_sz; - if (!feof(infile)) { - if (fread(*buf, 1, *buf_sz, infile) != *buf_sz) { - fprintf(stderr, "Failed to read full frame\n"); + if (fread(*buffer, 1, frame_size, infile) != frame_size) { + warn("Failed to read full frame\n"); return 1; } - - return 0; - } - - return 1; -} - -void *out_open(const char *out_fn, int do_md5) { - void *out = NULL; - - if (do_md5) { -#if CONFIG_MD5 - MD5Context *md5_ctx = out = malloc(sizeof(MD5Context)); - (void)out_fn; - MD5Init(md5_ctx); -#endif - } else { - FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb") - : set_binary_mode(stdout); - - if (!outfile) { - fprintf(stderr, "Failed to output file"); - exit(EXIT_FAILURE); - } + *bytes_read = frame_size; } - return out; + return 0; } -void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5) { - if (do_md5) { -#if CONFIG_MD5 - MD5Update(out, buf, len); -#endif - } else { - (void) fwrite(buf, 1, len, out); +static int read_frame(struct VpxDecInputContext *input, uint8_t **buf, + size_t *bytes_in_buffer, size_t *buffer_size) { + switch (input->vpx_input_ctx->file_type) { + case FILE_TYPE_WEBM: + return webm_read_frame(input->webm_ctx, + buf, bytes_in_buffer, buffer_size); + case FILE_TYPE_RAW: + return raw_read_frame(input->vpx_input_ctx->file, + buf, bytes_in_buffer, buffer_size); + case FILE_TYPE_IVF: + return ivf_read_frame(input->vpx_input_ctx->file, + buf, bytes_in_buffer, buffer_size); + default: + return 1; } } -void out_close(void *out, const char *out_fn, int do_md5) { - if (do_md5) { -#if CONFIG_MD5 - uint8_t md5[16]; - int i; - - MD5Final(md5, out); - free(out); +static void update_image_md5(const vpx_image_t *img, const int planes[3], + MD5Context *md5) { + int i, y; - for (i = 0; i < 16; i++) - printf("%02x", md5[i]); + for (i = 0; i < 3; ++i) { + const int plane = planes[i]; + const unsigned char *buf = img->planes[plane]; + const int stride = img->stride[plane]; + const int w = vpx_img_plane_width(img, plane); + const int h = vpx_img_plane_height(img, plane); - printf(" %s\n", out_fn); -#endif - } else { - fclose(out); + for (y = 0; y < h; ++y) { + MD5Update(md5, buf, w); + buf += stride; + } } } -unsigned int file_is_ivf(FILE *infile, - unsigned int *fourcc, - unsigned int *width, - unsigned int *height, - unsigned int *fps_den, - unsigned int *fps_num) { - char raw_hdr[32]; - int is_ivf = 0; - - if (fread(raw_hdr, 1, 32, infile) == 32) { - if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K' - && raw_hdr[2] == 'I' && raw_hdr[3] == 'F') { - is_ivf = 1; - - if (mem_get_le16(raw_hdr + 4) != 0) - fprintf(stderr, "Error: Unrecognized IVF version! This file may not" - " decode properly."); - - *fourcc = mem_get_le32(raw_hdr + 8); - *width = mem_get_le16(raw_hdr + 12); - *height = mem_get_le16(raw_hdr + 14); - *fps_num = mem_get_le32(raw_hdr + 16); - *fps_den = mem_get_le32(raw_hdr + 20); - - /* Some versions of vpxenc used 1/(2*fps) for the timebase, so - * we can guess the framerate using only the timebase in this - * case. Other files would require reading ahead to guess the - * timebase, like we do for webm. - */ - if (*fps_num < 1000) { - /* Correct for the factor of 2 applied to the timebase in the - * encoder. - */ - if (*fps_num & 1)*fps_den <<= 1; - else *fps_num >>= 1; - } else { - /* Don't know FPS for sure, and don't have readahead code - * (yet?), so just default to 30fps. - */ - *fps_num = 30; - *fps_den = 1; - } - } - } +static void write_image_file(const vpx_image_t *img, const int planes[3], + FILE *file) { + int i, y; - if (!is_ivf) - rewind(infile); + for (i = 0; i < 3; ++i) { + const int plane = planes[i]; + const unsigned char *buf = img->planes[plane]; + const int stride = img->stride[plane]; + const int w = vpx_img_plane_width(img, plane); + const int h = vpx_img_plane_height(img, plane); - return is_ivf; + for (y = 0; y < h; ++y) { + fwrite(buf, 1, w, file); + buf += stride; + } + } } - -unsigned int file_is_raw(FILE *infile, - unsigned int *fourcc, - unsigned int *width, - unsigned int *height, - unsigned int *fps_den, - unsigned int *fps_num) { - unsigned char buf[32]; +int file_is_raw(struct VpxInputContext *input) { + uint8_t buf[32]; int is_raw = 0; vpx_codec_stream_info_t si; si.sz = sizeof(si); - if (fread(buf, 1, 32, infile) == 32) { + if (fread(buf, 1, 32, input->file) == 32) { int i; - if (mem_get_le32(buf) < 256 * 1024 * 1024) - for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) - if (!vpx_codec_peek_stream_info(ifaces[i].iface(), + if (mem_get_le32(buf) < 256 * 1024 * 1024) { + for (i = 0; i < get_vpx_decoder_count(); ++i) { + const VpxInterface *const decoder = get_vpx_decoder_by_index(i); + if (!vpx_codec_peek_stream_info(decoder->interface(), buf + 4, 32 - 4, &si)) { is_raw = 1; - *fourcc = ifaces[i].fourcc; - *width = si.w; - *height = si.h; - *fps_num = 30; - *fps_den = 1; + input->fourcc = decoder->fourcc; + input->width = si.w; + input->height = si.h; + input->framerate.numerator = 30; + input->framerate.denominator = 1; break; } + } + } } - rewind(infile); + rewind(input->file); 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); +void show_progress(int frame_in, int frame_out, uint64_t dx_time) { + fprintf(stderr, + "%d decoded frames/%d showed frames in %"PRId64" us (%.2f fps)\r", + frame_in, frame_out, dx_time, + (double)frame_out * 1000000.0 / (double)dx_time); } +struct ExternalFrameBuffer { + uint8_t* data; + size_t size; + int in_use; +}; -static int -webm_guess_framerate(struct input_ctx *input, - unsigned int *fps_den, - unsigned int *fps_num) { - 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)) { - fprintf(stderr, - "WARNING: Failed to guess framerate (no Cues), set to 30fps.\n"); - *fps_num = 30; - *fps_den = 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; +struct ExternalFrameBufferList { + int num_external_frame_buffers; + struct ExternalFrameBuffer *ext_fb; +}; - if (nestegg_read_packet(input->nestegg_ctx, &pkt) <= 0) +// Callback used by libvpx to request an external frame buffer. |cb_priv| +// Application private data passed into the set function. |min_size| is the +// minimum size in bytes needed to decode the next frame. |fb| pointer to the +// frame buffer. +int get_vp9_frame_buffer(void *cb_priv, size_t min_size, + vpx_codec_frame_buffer_t *fb) { + int i; + struct ExternalFrameBufferList *const ext_fb_list = + (struct ExternalFrameBufferList *)cb_priv; + if (ext_fb_list == NULL) + return -1; + + // Find a free frame buffer. + for (i = 0; i < ext_fb_list->num_external_frame_buffers; ++i) { + if (!ext_fb_list->ext_fb[i].in_use) 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; - - *fps_num = (i - 1) * 1000000; - *fps_den = (unsigned int)(tstamp / 1000); - return 0; -fail: - nestegg_destroy(input->nestegg_ctx); - input->nestegg_ctx = NULL; - rewind(input->infile); - return 1; -} - - -static int -file_is_webm(struct input_ctx *input, - unsigned int *fourcc, - unsigned int *width, - unsigned int *height, - unsigned int *fps_den, - unsigned int *fps_num) { - unsigned int i, n; - int track_type = -1; - int codec_id; + if (i == ext_fb_list->num_external_frame_buffers) + return -1; - nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0}; - nestegg_video_params params; - - io.userdata = input->infile; - 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; - } + if (ext_fb_list->ext_fb[i].size < min_size) { + free(ext_fb_list->ext_fb[i].data); + ext_fb_list->ext_fb[i].data = (uint8_t *)malloc(min_size); + if (!ext_fb_list->ext_fb[i].data) + return -1; - codec_id = nestegg_track_codec_id(input->nestegg_ctx, i); - if (codec_id == NESTEGG_CODEC_VP8) { - *fourcc = VP8_FOURCC_MASK; - } else if (codec_id == NESTEGG_CODEC_VP9) { - *fourcc = VP9_FOURCC_MASK; - } else { - fprintf(stderr, "Not VPx video, quitting.\n"); - exit(1); + ext_fb_list->ext_fb[i].size = min_size; } - input->video_track = i; - - if (nestegg_track_video_params(input->nestegg_ctx, i, ¶ms)) - goto fail; + fb->data = ext_fb_list->ext_fb[i].data; + fb->size = ext_fb_list->ext_fb[i].size; + ext_fb_list->ext_fb[i].in_use = 1; - *fps_den = 0; - *fps_num = 0; - *width = params.width; - *height = params.height; - return 1; -fail: - input->nestegg_ctx = NULL; - rewind(input->infile); + // Set the frame buffer's private data to point at the external frame buffer. + fb->priv = &ext_fb_list->ext_fb[i]; 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); +// Callback used by libvpx when there are no references to the frame buffer. +// |cb_priv| user private data passed into the set function. |fb| pointer +// to the frame buffer. +int release_vp9_frame_buffer(void *cb_priv, + vpx_codec_frame_buffer_t *fb) { + struct ExternalFrameBuffer *const ext_fb = + (struct ExternalFrameBuffer *)fb->priv; + (void)cb_priv; + ext_fb->in_use = 0; + return 0; } - void generate_filename(const char *pattern, char *out, size_t q_len, unsigned int d_w, unsigned int d_h, unsigned int frame_in) { @@ -661,32 +447,60 @@ void generate_filename(const char *pattern, char *out, size_t q_len, } while (*p); } +static int is_single_file(const char *outfile_pattern) { + const char *p = outfile_pattern; + + do { + p = strchr(p, '%'); + if (p && p[1] >= '1' && p[1] <= '9') + return 0; // pattern contains sequence number, so it's not unique + if (p) + p++; + } while (p); + + return 1; +} + +static void print_md5(unsigned char digest[16], const char *filename) { + int i; + + for (i = 0; i < 16; ++i) + printf("%02x", digest[i]); + printf(" %s\n", filename); +} + +static FILE *open_outfile(const char *name) { + if (strcmp("-", name) == 0) { + set_binary_mode(stdout); + return stdout; + } else { + FILE *file = fopen(name, "wb"); + if (!file) + fatal("Failed to output file %s", name); + return file; + } +} int main_loop(int argc, const char **argv_) { - vpx_codec_ctx_t decoder; + vpx_codec_ctx_t decoder; char *fn = NULL; int i; uint8_t *buf = NULL; - size_t buf_sz = 0, buf_alloc_sz = 0; + size_t bytes_in_buffer = 0, buffer_size = 0; FILE *infile; - int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0; + int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0; + int do_md5 = 0, progress = 0; int stop_after = 0, postproc = 0, summary = 0, quiet = 1; int arg_skip = 0; int ec_enabled = 0; - vpx_codec_iface_t *iface = NULL; - unsigned int fourcc; - unsigned long dx_time = 0; + const VpxInterface *interface = NULL; + const VpxInterface *fourcc_interface = NULL; + uint64_t dx_time = 0; struct arg arg; char **argv, **argi, **argj; - const char *outfile_pattern = 0; - char outfile[PATH_MAX]; + int single_file; int use_y4m = 1; - unsigned int width; - unsigned int height; - unsigned int fps_den; - unsigned int fps_num; - void *out = NULL; vpx_codec_dec_cfg_t cfg = {0}; #if CONFIG_VP8_DECODER vp8_postproc_cfg_t vp8_pp_cfg = {0}; @@ -695,13 +509,26 @@ int main_loop(int argc, const char **argv_) { int vp8_dbg_color_b_modes = 0; int vp8_dbg_display_mv = 0; #endif - struct input_ctx input = {0}; int frames_corrupted = 0; int dec_flags = 0; int do_scale = 0; - int stream_w = 0, stream_h = 0; vpx_image_t *scaled_img = NULL; int frame_avail, got_data; + int num_external_frame_buffers = 0; + struct ExternalFrameBufferList ext_fb_list = {0}; + + const char *outfile_pattern = NULL; + char outfile_name[PATH_MAX] = {0}; + FILE *outfile = NULL; + + MD5Context md5_ctx; + unsigned char md5_digest[16]; + + 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]; @@ -712,17 +539,9 @@ int main_loop(int argc, const char **argv_) { arg.argv_step = 1; if (arg_match(&arg, &codecarg, argi)) { - int j, k = -1; - - for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++) - if (!strcmp(ifaces[j].name, arg.val)) - k = j; - - if (k >= 0) - iface = ifaces[k].iface(); - else - die("Error: Unrecognized argument (%s) to --codec\n", - arg.val); + interface = get_vpx_decoder_by_name(arg.val); + if (!interface) + die("Error: Unrecognized argument (%s) to --codec\n", arg.val); } else if (arg_match(&arg, &looparg, argi)) { // no-op } else if (arg_match(&arg, &outputfile, argi)) @@ -755,6 +574,8 @@ int main_loop(int argc, const char **argv_) { quiet = 0; else if (arg_match(&arg, &scalearg, argi)) do_scale = 1; + else if (arg_match(&arg, &fb_arg, argi)) + num_external_frame_buffers = arg_parse_uint(&arg); #if CONFIG_VP8_DECODER else if (arg_match(&arg, &addnoise_level, argi)) { @@ -827,8 +648,7 @@ int main_loop(int argc, const char **argv_) { infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin); if (!infile) { - fprintf(stderr, "Failed to open file '%s'", - strcmp(fn, "-") ? fn : "stdin"); + fprintf(stderr, "Failed to open file '%s'", strcmp(fn, "-") ? fn : "stdin"); return EXIT_FAILURE; } #if CONFIG_OS_SUPPORT @@ -840,89 +660,60 @@ int main_loop(int argc, const char **argv_) { return EXIT_FAILURE; } #endif - input.infile = infile; - if (file_is_ivf(infile, &fourcc, &width, &height, &fps_den, - &fps_num)) - input.kind = IVF_FILE; - else if (file_is_webm(&input, &fourcc, &width, &height, &fps_den, &fps_num)) - input.kind = WEBM_FILE; - else if (file_is_raw(infile, &fourcc, &width, &height, &fps_den, &fps_num)) - input.kind = RAW_FILE; + 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.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; else { fprintf(stderr, "Unrecognized input file type.\n"); return EXIT_FAILURE; } - /* If the output file is not set or doesn't have a sequence number in - * it, then we only open it once. - */ outfile_pattern = outfile_pattern ? outfile_pattern : "-"; - single_file = 1; - { - const char *p = outfile_pattern; - do { - p = strchr(p, '%'); - if (p && p[1] >= '1' && p[1] <= '9') { - /* pattern contains sequence number, so it's not unique. */ - single_file = 0; - break; - } - if (p) - p++; - } while (p); - } + single_file = is_single_file(outfile_pattern); - if (single_file && !noblit) { - generate_filename(outfile_pattern, outfile, sizeof(outfile) - 1, - width, height, 0); - out = out_open(outfile, do_md5); + if (!noblit && single_file) { + generate_filename(outfile_pattern, outfile_name, PATH_MAX, + vpx_input_ctx.width, vpx_input_ctx.height, 0); + if (do_md5) + MD5Init(&md5_ctx); + else + outfile = open_outfile(outfile_name); } if (use_y4m && !noblit) { - char buffer[128]; - if (!single_file) { fprintf(stderr, "YUV4MPEG2 not supported with output patterns," " try --i420 or --yv12.\n"); return EXIT_FAILURE; } - if (input.kind == WEBM_FILE) - if (webm_guess_framerate(&input, &fps_den, &fps_num)) { + if (vpx_input_ctx.file_type == FILE_TYPE_WEBM) { + 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; } - - - /*Note: We can't output an aspect ratio here because IVF doesn't - store one, and neither does VP8. - That will have to wait until these tools support WebM natively.*/ - snprintf(buffer, sizeof(buffer), "YUV4MPEG2 W%u H%u F%u:%u I%c ", - width, height, fps_num, fps_den, 'p'); - out_put(out, (unsigned char *)buffer, - (unsigned int)strlen(buffer), do_md5); + } } - /* Try to determine the codec from the fourcc. */ - for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) - if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc) { - vpx_codec_iface_t *ivf_iface = ifaces[i].iface(); - - if (iface && iface != ivf_iface) - fprintf(stderr, "Notice -- IVF header indicates codec: %s\n", - ifaces[i].name); - else - iface = ivf_iface; + fourcc_interface = get_vpx_decoder_by_fourcc(vpx_input_ctx.fourcc); + if (interface && fourcc_interface && interface != fourcc_interface) + warn("Header indicates codec: %s\n", fourcc_interface->name); + else + interface = fourcc_interface; - break; - } + if (!interface) + interface = get_vpx_decoder_by_index(0); dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) | (ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0); - if (vpx_codec_dec_init(&decoder, iface ? iface : ifaces[0].iface(), &cfg, - dec_flags)) { - fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder)); + if (vpx_codec_dec_init(&decoder, interface->interface(), &cfg, dec_flags)) { + fprintf(stderr, "Failed to initialize decoder: %s\n", + vpx_codec_error(&decoder)); return EXIT_FAILURE; } @@ -963,14 +754,27 @@ int main_loop(int argc, const char **argv_) { #endif - if(arg_skip) - fprintf(stderr, "Skiping first %d frames.\n", arg_skip); + if (arg_skip) + fprintf(stderr, "Skipping first %d frames.\n", arg_skip); while (arg_skip) { - if (read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) + if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) break; arg_skip--; } + if (num_external_frame_buffers > 0) { + ext_fb_list.num_external_frame_buffers = num_external_frame_buffers; + ext_fb_list.ext_fb = (struct ExternalFrameBuffer *)calloc( + num_external_frame_buffers, sizeof(*ext_fb_list.ext_fb)); + if (vpx_codec_set_frame_buffer_functions( + &decoder, get_vp9_frame_buffer, release_vp9_frame_buffer, + &ext_fb_list)) { + fprintf(stderr, "Failed to configure external frame buffers: %s\n", + vpx_codec_error(&decoder)); + return EXIT_FAILURE; + } + } + frame_avail = 1; got_data = 0; @@ -983,24 +787,25 @@ int main_loop(int argc, const char **argv_) { frame_avail = 0; if (!stop_after || frame_in < stop_after) { - if(!read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) { + if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) { frame_avail = 1; frame_in++; vpx_usec_timer_start(&timer); - if (vpx_codec_decode(&decoder, buf, (unsigned int)buf_sz, NULL, 0)) { + if (vpx_codec_decode(&decoder, buf, (unsigned int)bytes_in_buffer, + NULL, 0)) { const char *detail = vpx_codec_error_detail(&decoder); - fprintf(stderr, "Failed to decode frame: %s\n", - vpx_codec_error(&decoder)); + warn("Failed to decode frame %d: %s", + frame_in, vpx_codec_error(&decoder)); if (detail) - fprintf(stderr, " Additional information: %s\n", detail); + warn("Additional information: %s", detail); goto fail; } vpx_usec_timer_mark(&timer); - dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer); + dx_time += vpx_usec_timer_elapsed(&timer); } } @@ -1016,8 +821,7 @@ int main_loop(int argc, const char **argv_) { dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer); if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) { - fprintf(stderr, "Failed VP8_GET_FRAME_CORRUPTED: %s\n", - vpx_codec_error(&decoder)); + warn("Failed VP8_GET_FRAME_CORRUPTED: %s", vpx_codec_error(&decoder)); goto fail; } frames_corrupted += corrupted; @@ -1025,87 +829,86 @@ int main_loop(int argc, const char **argv_) { if (progress) show_progress(frame_in, frame_out, dx_time); - if (!noblit) { - if (frame_out == 1 && img && use_y4m) { - /* Write out the color format to terminate the header line */ - const char *color = - img->fmt == VPX_IMG_FMT_444A ? "C444alpha\n" : - img->fmt == VPX_IMG_FMT_I444 ? "C444\n" : - img->fmt == VPX_IMG_FMT_I422 ? "C422\n" : - "C420jpeg\n"; - - out_put(out, (const unsigned char*)color, strlen(color), do_md5); - } + if (!noblit && img) { + const int PLANES_YUV[] = {VPX_PLANE_Y, VPX_PLANE_U, VPX_PLANE_V}; + const int PLANES_YVU[] = {VPX_PLANE_Y, VPX_PLANE_V, VPX_PLANE_U}; + const int *planes = flipuv ? PLANES_YVU : PLANES_YUV; if (do_scale) { - if (img && frame_out == 1) { - stream_w = img->d_w; - stream_h = img->d_h; - scaled_img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, - stream_w, stream_h, 16); + if (frame_out == 1) { + // If the output frames are to be scaled to a fixed display size then + // use the width and height specified in the container. If either of + // these is set to 0, use the display size set in the first frame + // header. If that is unavailable, use the raw decoded size of the + // first decoded frame. + int display_width = vpx_input_ctx.width; + int display_height = vpx_input_ctx.height; + if (!display_width || !display_height) { + int display_size[2]; + if (vpx_codec_control(&decoder, VP9D_GET_DISPLAY_SIZE, + display_size)) { + // As last resort use size of first frame as display size. + display_width = img->d_w; + display_height = img->d_h; + } else { + display_width = display_size[0]; + display_height = display_size[1]; + } + } + scaled_img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, display_width, + display_height, 16); } - if (img && (img->d_w != stream_w || img->d_h != stream_h)) { - assert(img->fmt == VPX_IMG_FMT_I420); - I420Scale(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y], - img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U], - img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V], - img->d_w, img->d_h, - scaled_img->planes[VPX_PLANE_Y], - scaled_img->stride[VPX_PLANE_Y], - scaled_img->planes[VPX_PLANE_U], - scaled_img->stride[VPX_PLANE_U], - scaled_img->planes[VPX_PLANE_V], - scaled_img->stride[VPX_PLANE_V], - stream_w, stream_h, - kFilterBox); + + if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) { + vpx_image_scale(img, scaled_img, kFilterBox); img = scaled_img; } } - if (img) { - unsigned int y; - char out_fn[PATH_MAX]; - uint8_t *buf; - unsigned int c_w = - img->x_chroma_shift ? (1 + img->d_w) >> img->x_chroma_shift - : img->d_w; - unsigned int c_h = - img->y_chroma_shift ? (1 + img->d_h) >> img->y_chroma_shift - : img->d_h; - - if (!single_file) { - size_t len = sizeof(out_fn) - 1; - - out_fn[len] = '\0'; - generate_filename(outfile_pattern, out_fn, len - 1, - img->d_w, img->d_h, frame_in); - out = out_open(out_fn, do_md5); - } else if (use_y4m) - out_put(out, (unsigned char *)"FRAME\n", 6, do_md5); - - buf = img->planes[VPX_PLANE_Y]; - - for (y = 0; y < img->d_h; y++) { - out_put(out, buf, img->d_w, do_md5); - buf += img->stride[VPX_PLANE_Y]; + if (single_file) { + if (use_y4m) { + char buf[Y4M_BUFFER_SIZE] = {0}; + size_t len = 0; + if (frame_out == 1) { + // Y4M file header + len = y4m_write_file_header(buf, sizeof(buf), + vpx_input_ctx.width, + vpx_input_ctx.height, + &vpx_input_ctx.framerate, img->fmt); + if (do_md5) { + MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len); + } else { + fputs(buf, outfile); + } + } + + // Y4M frame header + len = y4m_write_frame_header(buf, sizeof(buf)); + if (do_md5) { + MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len); + } else { + fputs(buf, outfile); + } } - buf = img->planes[flipuv ? VPX_PLANE_V : VPX_PLANE_U]; - - for (y = 0; y < c_h; y++) { - out_put(out, buf, c_w, do_md5); - buf += img->stride[VPX_PLANE_U]; + if (do_md5) { + update_image_md5(img, planes, &md5_ctx); + } else { + write_image_file(img, planes, outfile); } - - buf = img->planes[flipuv ? VPX_PLANE_U : VPX_PLANE_V]; - - for (y = 0; y < c_h; y++) { - out_put(out, buf, c_w, do_md5); - buf += img->stride[VPX_PLANE_V]; + } else { + generate_filename(outfile_pattern, outfile_name, PATH_MAX, + img->d_w, img->d_h, frame_in); + if (do_md5) { + MD5Init(&md5_ctx); + update_image_md5(img, planes, &md5_ctx); + MD5Final(md5_digest, &md5_ctx); + print_md5(md5_digest, outfile_name); + } else { + outfile = open_outfile(outfile_name); + write_image_file(img, planes, outfile); + fclose(outfile); } - - if (!single_file) - out_close(out, out_fn, do_md5); } } @@ -1124,17 +927,32 @@ int main_loop(int argc, const char **argv_) { fail: if (vpx_codec_destroy(&decoder)) { - fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder)); + fprintf(stderr, "Failed to destroy decoder: %s\n", + vpx_codec_error(&decoder)); return EXIT_FAILURE; } - if (single_file && !noblit) - out_close(out, outfile, do_md5); + if (!noblit && single_file) { + if (do_md5) { + MD5Final(md5_digest, &md5_ctx); + print_md5(md5_digest, outfile_name); + } else { + fclose(outfile); + } + } - if (input.nestegg_ctx) - nestegg_destroy(input.nestegg_ctx); - if (input.kind != WEBM_FILE) + if (input.vpx_input_ctx->file_type == FILE_TYPE_WEBM) + webm_free(input.webm_ctx); + else free(buf); + + if (scaled_img) vpx_img_free(scaled_img); + + for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) { + free(ext_fb_list.ext_fb[i].data); + } + free(ext_fb_list.ext_fb); + fclose(infile); free(argv); |