| Index: source/libvpx/vpxenc.c
|
| ===================================================================
|
| --- source/libvpx/vpxenc.c (revision 191025)
|
| +++ source/libvpx/vpxenc.c (working copy)
|
| @@ -10,7 +10,7 @@
|
|
|
| #include "vpx_config.h"
|
|
|
| -#if defined(_WIN32) || !CONFIG_OS_SUPPORT
|
| +#if defined(_WIN32) || defined(__OS2__) || !CONFIG_OS_SUPPORT
|
| #define USE_POSIX_MMAP 0
|
| #else
|
| #define USE_POSIX_MMAP 1
|
| @@ -47,6 +47,7 @@
|
| #include "y4minput.h"
|
| #include "libmkv/EbmlWriter.h"
|
| #include "libmkv/EbmlIDs.h"
|
| +#include "third_party/libyuv/include/libyuv/scale.h"
|
|
|
| /* Need special handling of these functions on Windows */
|
| #if defined(_MSC_VER)
|
| @@ -139,10 +140,8 @@
|
| }
|
|
|
|
|
| -static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) {
|
| - va_list ap;
|
| -
|
| - va_start(ap, s);
|
| +static void warn_or_exit_on_errorv(vpx_codec_ctx_t *ctx, int fatal,
|
| + const char *s, va_list ap) {
|
| if (ctx->err) {
|
| const char *detail = vpx_codec_error_detail(ctx);
|
|
|
| @@ -152,10 +151,28 @@
|
| if (detail)
|
| fprintf(stderr, " %s\n", detail);
|
|
|
| - exit(EXIT_FAILURE);
|
| + if (fatal)
|
| + exit(EXIT_FAILURE);
|
| }
|
| }
|
|
|
| +static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) {
|
| + va_list ap;
|
| +
|
| + va_start(ap, s);
|
| + warn_or_exit_on_errorv(ctx, 1, s, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| +static void warn_or_exit_on_error(vpx_codec_ctx_t *ctx, int fatal,
|
| + const char *s, ...) {
|
| + va_list ap;
|
| +
|
| + va_start(ap, s);
|
| + warn_or_exit_on_errorv(ctx, fatal, s, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| /* This structure is used to abstract the different ways of handling
|
| * first pass statistics.
|
| */
|
| @@ -301,6 +318,7 @@
|
| struct input_state {
|
| char *fn;
|
| FILE *file;
|
| + off_t length;
|
| y4m_input y4m;
|
| struct detect_buffer detect;
|
| enum video_file_type file_type;
|
| @@ -949,8 +967,20 @@
|
| "Show encoder parameters");
|
| static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0,
|
| "Show PSNR in status line");
|
| -static const arg_def_t recontest = ARG_DEF(NULL, "test-decode", 0,
|
| - "Test encode/decode mismatch");
|
| +enum TestDecodeFatality {
|
| + TEST_DECODE_OFF,
|
| + TEST_DECODE_FATAL,
|
| + TEST_DECODE_WARN,
|
| +};
|
| +static const struct arg_enum_list test_decode_enum[] = {
|
| + {"off", TEST_DECODE_OFF},
|
| + {"fatal", TEST_DECODE_FATAL},
|
| + {"warn", TEST_DECODE_WARN},
|
| + {NULL, 0}
|
| +};
|
| +static const arg_def_t recontest = ARG_DEF_ENUM(NULL, "test-decode", 1,
|
| + "Test encode/decode mismatch",
|
| + test_decode_enum);
|
| static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1,
|
| "Stream frame rate (rate/scale)");
|
| static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0,
|
| @@ -1565,7 +1595,7 @@
|
| int limit;
|
| int skip_frames;
|
| int show_psnr;
|
| - int test_decode;
|
| + enum TestDecodeFatality test_decode;
|
| int have_framerate;
|
| struct vpx_rational framerate;
|
| int out_part;
|
| @@ -1606,6 +1636,7 @@
|
| uint64_t cx_time;
|
| size_t nbytes;
|
| stats_io_t stats;
|
| + struct vpx_image *img;
|
| vpx_codec_ctx_t decoder;
|
| vpx_ref_frame_t ref_enc;
|
| vpx_ref_frame_t ref_dec;
|
| @@ -1690,7 +1721,7 @@
|
| else if (arg_match(&arg, &psnrarg, argi))
|
| global->show_psnr = 1;
|
| else if (arg_match(&arg, &recontest, argi))
|
| - global->test_decode = 1;
|
| + global->test_decode = arg_parse_enum_or_int(&arg);
|
| else if (arg_match(&arg, &framerate, argi)) {
|
| global->framerate = arg_parse_rational(&arg);
|
| validate_positive_rational(arg.name, &global->framerate);
|
| @@ -1730,6 +1761,14 @@
|
| if (!input->file)
|
| fatal("Failed to open input file");
|
|
|
| + if (!fseeko(input->file, 0, SEEK_END)) {
|
| + /* Input file is seekable. Figure out how long it is, so we can get
|
| + * progress info.
|
| + */
|
| + input->length = ftello(input->file);
|
| + rewind(input->file);
|
| + }
|
| +
|
| /* For RAW input sources, these bytes will applied on the first frame
|
| * in read_frame().
|
| */
|
| @@ -2013,11 +2052,15 @@
|
| static void set_stream_dimensions(struct stream_state *stream,
|
| unsigned int w,
|
| unsigned int h) {
|
| - if ((stream->config.cfg.g_w && stream->config.cfg.g_w != w)
|
| - || (stream->config.cfg.g_h && stream->config.cfg.g_h != h))
|
| - fatal("Stream %d: Resizing not yet supported", stream->index);
|
| - stream->config.cfg.g_w = w;
|
| - stream->config.cfg.g_h = h;
|
| + if (!stream->config.cfg.g_w) {
|
| + if (!stream->config.cfg.g_h)
|
| + stream->config.cfg.g_w = w;
|
| + else
|
| + stream->config.cfg.g_w = w * stream->config.cfg.g_h / h;
|
| + }
|
| + if (!stream->config.cfg.g_h) {
|
| + stream->config.cfg.g_h = h * stream->config.cfg.g_w / w;
|
| + }
|
| }
|
|
|
|
|
| @@ -2177,7 +2220,7 @@
|
| }
|
|
|
| #if CONFIG_DECODERS
|
| - if (global->test_decode) {
|
| + if (global->test_decode != TEST_DECODE_OFF) {
|
| int width, height;
|
|
|
| vpx_codec_dec_init(&stream->decoder, global->codec->dx_iface(), NULL, 0);
|
| @@ -2207,6 +2250,28 @@
|
| next_frame_start = (cfg->g_timebase.den * (int64_t)(frames_in)
|
| * global->framerate.den)
|
| / cfg->g_timebase.num / global->framerate.num;
|
| +
|
| + /* Scale if necessary */
|
| + if (img && (img->d_w != cfg->g_w || img->d_h != cfg->g_h)) {
|
| + if (!stream->img)
|
| + stream->img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420,
|
| + cfg->g_w, cfg->g_h, 16);
|
| + 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,
|
| + stream->img->planes[VPX_PLANE_Y],
|
| + stream->img->stride[VPX_PLANE_Y],
|
| + stream->img->planes[VPX_PLANE_U],
|
| + stream->img->stride[VPX_PLANE_U],
|
| + stream->img->planes[VPX_PLANE_V],
|
| + stream->img->stride[VPX_PLANE_V],
|
| + stream->img->d_w, stream->img->d_h,
|
| + kFilterBox);
|
| +
|
| + img = stream->img;
|
| + }
|
| +
|
| vpx_usec_timer_start(&timer);
|
| vpx_codec_encode(&stream->encoder, img, frame_start,
|
| (unsigned long)(next_frame_start - frame_start),
|
| @@ -2246,9 +2311,6 @@
|
| if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) {
|
| stream->frames_out++;
|
| }
|
| - if (!global->quiet)
|
| - fprintf(stderr, " %6luF",
|
| - (unsigned long)pkt->data.frame.sz);
|
|
|
| update_rate_histogram(&stream->rate_hist, cfg, pkt);
|
| if (stream->config.write_webm) {
|
| @@ -2283,18 +2345,21 @@
|
|
|
| *got_data = 1;
|
| #if CONFIG_DECODERS
|
| - if (global->test_decode) {
|
| + if (global->test_decode != TEST_DECODE_OFF && !stream->mismatch_seen) {
|
| vpx_codec_decode(&stream->decoder, pkt->data.frame.buf,
|
| pkt->data.frame.sz, NULL, 0);
|
| - ctx_exit_on_error(&stream->decoder, "Failed to decode frame");
|
| + if (stream->decoder.err) {
|
| + warn_or_exit_on_error(&stream->decoder,
|
| + global->test_decode == TEST_DECODE_FATAL,
|
| + "Failed to decode frame %d in stream %d",
|
| + stream->frames_out + 1, stream->index);
|
| + stream->mismatch_seen = stream->frames_out + 1;
|
| + }
|
| }
|
| #endif
|
| break;
|
| case VPX_CODEC_STATS_PKT:
|
| stream->frames_out++;
|
| - if (!global->quiet)
|
| - fprintf(stderr, " %6luS",
|
| - (unsigned long)pkt->data.twopass_stats.sz);
|
| stats_write(&stream->stats,
|
| pkt->data.twopass_stats.buf,
|
| pkt->data.twopass_stats.sz);
|
| @@ -2308,8 +2373,6 @@
|
| stream->psnr_sse_total += pkt->data.psnr.sse[0];
|
| stream->psnr_samples_total += pkt->data.psnr.samples[0];
|
| for (i = 0; i < 4; i++) {
|
| - if (!global->quiet)
|
| - fprintf(stderr, "%.3f ", pkt->data.psnr.psnr[i]);
|
| stream->psnr_totals[i] += pkt->data.psnr.psnr[i];
|
| }
|
| stream->psnr_count++;
|
| @@ -2342,31 +2405,52 @@
|
| }
|
|
|
|
|
| -float usec_to_fps(uint64_t usec, unsigned int frames) {
|
| +static float usec_to_fps(uint64_t usec, unsigned int frames) {
|
| return (float)(usec > 0 ? frames * 1000000.0 / (float)usec : 0);
|
| }
|
|
|
|
|
| -static void test_decode(struct stream_state *stream) {
|
| +static void test_decode(struct stream_state *stream,
|
| + enum TestDecodeFatality fatal) {
|
| + if (stream->mismatch_seen)
|
| + return;
|
| +
|
| vpx_codec_control(&stream->encoder, VP8_COPY_REFERENCE, &stream->ref_enc);
|
| ctx_exit_on_error(&stream->encoder, "Failed to get encoder reference frame");
|
| vpx_codec_control(&stream->decoder, VP8_COPY_REFERENCE, &stream->ref_dec);
|
| ctx_exit_on_error(&stream->decoder, "Failed to get decoder reference frame");
|
|
|
| - if (!stream->mismatch_seen
|
| - && !compare_img(&stream->ref_enc.img, &stream->ref_dec.img)) {
|
| - /* TODO(jkoleszar): make fatal. */
|
| + if (!compare_img(&stream->ref_enc.img, &stream->ref_dec.img)) {
|
| int y[2], u[2], v[2];
|
| find_mismatch(&stream->ref_enc.img, &stream->ref_dec.img,
|
| y, u, v);
|
| - warn("Stream %d: Encode/decode mismatch on frame %d"
|
| - " at Y[%d, %d], U[%d, %d], V[%d, %d]",
|
| - stream->index, stream->frames_out,
|
| - y[0], y[1], u[0], u[1], v[0], v[1]);
|
| + warn_or_exit_on_error(&stream->decoder, fatal == TEST_DECODE_FATAL,
|
| + "Stream %d: Encode/decode mismatch on frame %d"
|
| + " at Y[%d, %d], U[%d, %d], V[%d, %d]",
|
| + stream->index, stream->frames_out,
|
| + y[0], y[1], u[0], u[1], v[0], v[1]);
|
| stream->mismatch_seen = stream->frames_out;
|
| }
|
| }
|
|
|
| +
|
| +static void print_time(const char *label, int64_t etl) {
|
| + int hours, mins, secs;
|
| +
|
| + if (etl >= 0) {
|
| + hours = etl / 3600;
|
| + etl -= hours * 3600;
|
| + mins = etl / 60;
|
| + etl -= mins * 60;
|
| + secs = etl;
|
| +
|
| + fprintf(stderr, "[%3s %2d:%02d:%02d] ",
|
| + label, hours, mins, secs);
|
| + } else {
|
| + fprintf(stderr, "[%3s unknown] ", label);
|
| + }
|
| +}
|
| +
|
| int main(int argc, const char **argv_) {
|
| int pass;
|
| vpx_image_t raw;
|
| @@ -2376,8 +2460,9 @@
|
| struct global_config global;
|
| struct stream_state *streams = NULL;
|
| char **argv, **argi;
|
| - unsigned long cx_time = 0;
|
| + uint64_t cx_time = 0;
|
| int stream_cnt = 0;
|
| + int res = 0;
|
|
|
| exec_name = argv_[0];
|
|
|
| @@ -2423,7 +2508,10 @@
|
| usage_exit();
|
|
|
| for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) {
|
| - int frames_in = 0;
|
| + int frames_in = 0, seen_frames = 0;
|
| + int64_t estimated_time_left = -1;
|
| + int64_t average_rate = -1;
|
| + off_t lagged_count = 0;
|
|
|
| open_input_file(&input);
|
|
|
| @@ -2440,6 +2528,9 @@
|
| });
|
|
|
| /* Update stream configurations from the input file's parameters */
|
| + if (!input.w || !input.h)
|
| + fatal("Specify stream dimensions with --width (-w) "
|
| + " and --height (-h)");
|
| FOREACH_STREAM(set_stream_dimensions(stream, input.w, input.h));
|
| FOREACH_STREAM(validate_stream_config(stream));
|
|
|
| @@ -2498,20 +2589,27 @@
|
|
|
| if (frame_avail)
|
| frames_in++;
|
| + seen_frames = frames_in > global.skip_frames ?
|
| + frames_in - global.skip_frames : 0;
|
|
|
| if (!global.quiet) {
|
| + float fps = usec_to_fps(cx_time, seen_frames);
|
| + fprintf(stderr, "\rPass %d/%d ", pass + 1, global.passes);
|
| +
|
| if (stream_cnt == 1)
|
| fprintf(stderr,
|
| - "\rPass %d/%d frame %4d/%-4d %7"PRId64"B \033[K",
|
| - pass + 1, global.passes, frames_in,
|
| - streams->frames_out, (int64_t)streams->nbytes);
|
| + "frame %4d/%-4d %7"PRId64"B ",
|
| + frames_in, streams->frames_out, (int64_t)streams->nbytes);
|
| else
|
| - fprintf(stderr,
|
| - "\rPass %d/%d frame %4d %7lu %s (%.2f fps)\033[K",
|
| - pass + 1, global.passes, frames_in,
|
| - cx_time > 9999999 ? cx_time / 1000 : cx_time,
|
| - cx_time > 9999999 ? "ms" : "us",
|
| - usec_to_fps(cx_time, frames_in));
|
| + fprintf(stderr, "frame %4d ", frames_in);
|
| +
|
| + fprintf(stderr, "%7"PRId64" %s %.2f %s ",
|
| + cx_time > 9999999 ? cx_time / 1000 : cx_time,
|
| + cx_time > 9999999 ? "ms" : "us",
|
| + fps >= 1.0 ? fps : 1000.0 / fps,
|
| + fps >= 1.0 ? "fps" : "ms/f");
|
| + print_time("ETA", estimated_time_left);
|
| + fprintf(stderr, "\033[K");
|
| }
|
|
|
| } else
|
| @@ -2523,15 +2621,42 @@
|
| frame_avail ? &raw : NULL,
|
| frames_in));
|
| vpx_usec_timer_mark(&timer);
|
| - cx_time += (unsigned long)vpx_usec_timer_elapsed(&timer);
|
| + cx_time += vpx_usec_timer_elapsed(&timer);
|
|
|
| FOREACH_STREAM(update_quantizer_histogram(stream));
|
|
|
| got_data = 0;
|
| FOREACH_STREAM(get_cx_data(stream, &global, &got_data));
|
|
|
| - if (got_data && global.test_decode)
|
| - FOREACH_STREAM(test_decode(stream));
|
| + if (!got_data && input.length && !streams->frames_out) {
|
| + lagged_count = global.limit ? seen_frames : ftello(input.file);
|
| + } else if (input.length) {
|
| + int64_t remaining;
|
| + int64_t rate;
|
| +
|
| + if (global.limit) {
|
| + int frame_in_lagged = (seen_frames - lagged_count) * 1000;
|
| +
|
| + rate = cx_time ? frame_in_lagged * (int64_t)1000000 / cx_time : 0;
|
| + remaining = 1000 * (global.limit - global.skip_frames
|
| + - seen_frames + lagged_count);
|
| + } else {
|
| + off_t input_pos = ftello(input.file);
|
| + off_t input_pos_lagged = input_pos - lagged_count;
|
| + int64_t limit = input.length;
|
| +
|
| + rate = cx_time ? input_pos_lagged * (int64_t)1000000 / cx_time : 0;
|
| + remaining = limit - input_pos + lagged_count;
|
| + }
|
| +
|
| + average_rate = (average_rate <= 0)
|
| + ? rate
|
| + : (average_rate * 7 + rate) / 8;
|
| + estimated_time_left = average_rate ? remaining / average_rate : -1;
|
| + }
|
| +
|
| + if (got_data && global.test_decode != TEST_DECODE_OFF)
|
| + FOREACH_STREAM(test_decode(stream, global.test_decode));
|
| }
|
|
|
| fflush(stdout);
|
| @@ -2546,14 +2671,14 @@
|
| "\rPass %d/%d frame %4d/%-4d %7"PRId64"B %7lub/f %7"PRId64"b/s"
|
| " %7"PRId64" %s (%.2f fps)\033[K\n", pass + 1,
|
| global.passes, frames_in, stream->frames_out, (int64_t)stream->nbytes,
|
| - frames_in ? (unsigned long)(stream->nbytes * 8 / frames_in) : 0,
|
| - frames_in ? (int64_t)stream->nbytes * 8
|
| + seen_frames ? (unsigned long)(stream->nbytes * 8 / seen_frames) : 0,
|
| + seen_frames ? (int64_t)stream->nbytes * 8
|
| * (int64_t)global.framerate.num / global.framerate.den
|
| - / frames_in
|
| + / seen_frames
|
| : 0,
|
| stream->cx_time > 9999999 ? stream->cx_time / 1000 : stream->cx_time,
|
| stream->cx_time > 9999999 ? "ms" : "us",
|
| - usec_to_fps(stream->cx_time, frames_in));
|
| + usec_to_fps(stream->cx_time, seen_frames));
|
| );
|
|
|
| if (global.show_psnr)
|
| @@ -2561,7 +2686,7 @@
|
|
|
| FOREACH_STREAM(vpx_codec_destroy(&stream->encoder));
|
|
|
| - if (global.test_decode) {
|
| + if (global.test_decode != TEST_DECODE_OFF) {
|
| FOREACH_STREAM(vpx_codec_destroy(&stream->decoder));
|
| FOREACH_STREAM(vpx_img_free(&stream->ref_enc.img));
|
| FOREACH_STREAM(vpx_img_free(&stream->ref_dec.img));
|
| @@ -2569,6 +2694,9 @@
|
|
|
| close_input_file(&input);
|
|
|
| + if (global.test_decode == TEST_DECODE_FATAL) {
|
| + FOREACH_STREAM(res |= stream->mismatch_seen);
|
| + }
|
| FOREACH_STREAM(close_output_file(stream, global.codec->fourcc));
|
|
|
| FOREACH_STREAM(stats_close(&stream->stats, global.passes - 1));
|
| @@ -2606,5 +2734,5 @@
|
| vpx_img_free(&raw);
|
| free(argv);
|
| free(streams);
|
| - return EXIT_SUCCESS;
|
| + return res ? EXIT_FAILURE : EXIT_SUCCESS;
|
| }
|
|
|