| Index: source/libvpx/examples/twopass_encoder.c
|
| ===================================================================
|
| --- source/libvpx/examples/twopass_encoder.c (revision 291857)
|
| +++ source/libvpx/examples/twopass_encoder.c (working copy)
|
| @@ -66,13 +66,14 @@
|
| exit(EXIT_FAILURE);
|
| }
|
|
|
| -static void get_frame_stats(vpx_codec_ctx_t *ctx,
|
| - const vpx_image_t *img,
|
| - vpx_codec_pts_t pts,
|
| - unsigned int duration,
|
| - vpx_enc_frame_flags_t flags,
|
| - unsigned int deadline,
|
| - vpx_fixed_buf_t *stats) {
|
| +static int get_frame_stats(vpx_codec_ctx_t *ctx,
|
| + const vpx_image_t *img,
|
| + vpx_codec_pts_t pts,
|
| + unsigned int duration,
|
| + vpx_enc_frame_flags_t flags,
|
| + unsigned int deadline,
|
| + vpx_fixed_buf_t *stats) {
|
| + int got_pkts = 0;
|
| vpx_codec_iter_t iter = NULL;
|
| const vpx_codec_cx_pkt_t *pkt = NULL;
|
| const vpx_codec_err_t res = vpx_codec_encode(ctx, img, pts, duration, flags,
|
| @@ -81,6 +82,8 @@
|
| die_codec(ctx, "Failed to get frame stats.");
|
|
|
| while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
|
| + got_pkts = 1;
|
| +
|
| if (pkt->kind == VPX_CODEC_STATS_PKT) {
|
| const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
|
| const size_t pkt_size = pkt->data.twopass_stats.sz;
|
| @@ -89,15 +92,18 @@
|
| stats->sz += pkt_size;
|
| }
|
| }
|
| +
|
| + return got_pkts;
|
| }
|
|
|
| -static void encode_frame(vpx_codec_ctx_t *ctx,
|
| - const vpx_image_t *img,
|
| - vpx_codec_pts_t pts,
|
| - unsigned int duration,
|
| - vpx_enc_frame_flags_t flags,
|
| - unsigned int deadline,
|
| - VpxVideoWriter *writer) {
|
| +static int encode_frame(vpx_codec_ctx_t *ctx,
|
| + const vpx_image_t *img,
|
| + vpx_codec_pts_t pts,
|
| + unsigned int duration,
|
| + vpx_enc_frame_flags_t flags,
|
| + unsigned int deadline,
|
| + VpxVideoWriter *writer) {
|
| + int got_pkts = 0;
|
| vpx_codec_iter_t iter = NULL;
|
| const vpx_codec_cx_pkt_t *pkt = NULL;
|
| const vpx_codec_err_t res = vpx_codec_encode(ctx, img, pts, duration, flags,
|
| @@ -106,6 +112,7 @@
|
| die_codec(ctx, "Failed to encode frame.");
|
|
|
| while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
|
| + got_pkts = 1;
|
| if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
| const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
|
|
| @@ -117,19 +124,90 @@
|
| fflush(stdout);
|
| }
|
| }
|
| +
|
| + return got_pkts;
|
| }
|
|
|
| +static vpx_fixed_buf_t pass0(vpx_image_t *raw,
|
| + FILE *infile,
|
| + const VpxInterface *encoder,
|
| + const vpx_codec_enc_cfg_t *cfg) {
|
| + vpx_codec_ctx_t codec;
|
| + int frame_count = 0;
|
| + vpx_fixed_buf_t stats = {NULL, 0};
|
| +
|
| + if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
|
| + die_codec(&codec, "Failed to initialize encoder");
|
| +
|
| + // Calculate frame statistics.
|
| + while (vpx_img_read(raw, infile)) {
|
| + ++frame_count;
|
| + get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_BEST_QUALITY,
|
| + &stats);
|
| + }
|
| +
|
| + // Flush encoder.
|
| + while (get_frame_stats(&codec, NULL, frame_count, 1, 0,
|
| + VPX_DL_BEST_QUALITY, &stats)) {}
|
| +
|
| + printf("Pass 0 complete. Processed %d frames.\n", frame_count);
|
| + if (vpx_codec_destroy(&codec))
|
| + die_codec(&codec, "Failed to destroy codec.");
|
| +
|
| + return stats;
|
| +}
|
| +
|
| +static void pass1(vpx_image_t *raw,
|
| + FILE *infile,
|
| + const char *outfile_name,
|
| + const VpxInterface *encoder,
|
| + const vpx_codec_enc_cfg_t *cfg) {
|
| + VpxVideoInfo info = {
|
| + encoder->fourcc,
|
| + cfg->g_w,
|
| + cfg->g_h,
|
| + {cfg->g_timebase.num, cfg->g_timebase.den}
|
| + };
|
| + VpxVideoWriter *writer = NULL;
|
| + vpx_codec_ctx_t codec;
|
| + int frame_count = 0;
|
| +
|
| + writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info);
|
| + if (!writer)
|
| + die("Failed to open %s for writing", outfile_name);
|
| +
|
| + if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
|
| + die_codec(&codec, "Failed to initialize encoder");
|
| +
|
| + // Encode frames.
|
| + while (vpx_img_read(raw, infile)) {
|
| + ++frame_count;
|
| + encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_BEST_QUALITY, writer);
|
| + }
|
| +
|
| + // Flush encoder.
|
| + while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_BEST_QUALITY, writer)) {}
|
| +
|
| + printf("\n");
|
| +
|
| + if (vpx_codec_destroy(&codec))
|
| + die_codec(&codec, "Failed to destroy codec.");
|
| +
|
| + vpx_video_writer_close(writer);
|
| +
|
| + printf("Pass 1 complete. Processed %d frames.\n", frame_count);
|
| +}
|
| +
|
| int main(int argc, char **argv) {
|
| FILE *infile = NULL;
|
| - VpxVideoWriter *writer = NULL;
|
| + int w, h;
|
| vpx_codec_ctx_t codec;
|
| vpx_codec_enc_cfg_t cfg;
|
| vpx_image_t raw;
|
| vpx_codec_err_t res;
|
| - vpx_fixed_buf_t stats = {0};
|
| - VpxVideoInfo info = {0};
|
| + vpx_fixed_buf_t stats;
|
| +
|
| const VpxInterface *encoder = NULL;
|
| - int pass;
|
| const int fps = 30; // TODO(dkovalev) add command line argument
|
| const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument
|
| const char *const codec_arg = argv[1];
|
| @@ -146,85 +224,44 @@
|
| if (!encoder)
|
| die("Unsupported codec.");
|
|
|
| - info.codec_fourcc = encoder->fourcc;
|
| - info.time_base.numerator = 1;
|
| - info.time_base.denominator = fps;
|
| - info.frame_width = strtol(width_arg, NULL, 0);
|
| - info.frame_height = strtol(height_arg, NULL, 0);
|
| + w = strtol(width_arg, NULL, 0);
|
| + h = strtol(height_arg, NULL, 0);
|
|
|
| - if (info.frame_width <= 0 ||
|
| - info.frame_height <= 0 ||
|
| - (info.frame_width % 2) != 0 ||
|
| - (info.frame_height % 2) != 0) {
|
| - die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
| - }
|
| + if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
|
| + die("Invalid frame size: %dx%d", w, h);
|
|
|
| - if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
|
| - info.frame_height, 1)) {
|
| - die("Failed to allocate image", info.frame_width, info.frame_height);
|
| - }
|
| + if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1))
|
| + die("Failed to allocate image", w, h);
|
|
|
| - writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
|
| - if (!writer)
|
| - die("Failed to open %s for writing", outfile_arg);
|
| -
|
| printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
|
|
| + // Configuration
|
| res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
| if (res)
|
| die_codec(&codec, "Failed to get default codec config.");
|
|
|
| - cfg.g_w = info.frame_width;
|
| - cfg.g_h = info.frame_height;
|
| - cfg.g_timebase.num = info.time_base.numerator;
|
| - cfg.g_timebase.den = info.time_base.denominator;
|
| + cfg.g_w = w;
|
| + cfg.g_h = h;
|
| + cfg.g_timebase.num = 1;
|
| + cfg.g_timebase.den = fps;
|
| cfg.rc_target_bitrate = bitrate;
|
|
|
| - for (pass = 0; pass < 2; ++pass) {
|
| - int frame_count = 0;
|
| + if (!(infile = fopen(infile_arg, "rb")))
|
| + die("Failed to open %s for reading", infile_arg);
|
|
|
| - if (pass == 0) {
|
| - cfg.g_pass = VPX_RC_FIRST_PASS;
|
| - } else {
|
| - cfg.g_pass = VPX_RC_LAST_PASS;
|
| - cfg.rc_twopass_stats_in = stats;
|
| - }
|
| + // Pass 0
|
| + cfg.g_pass = VPX_RC_FIRST_PASS;
|
| + stats = pass0(&raw, infile, encoder, &cfg);
|
|
|
| - if (!(infile = fopen(infile_arg, "rb")))
|
| - die("Failed to open %s for reading", infile_arg);
|
| + // Pass 1
|
| + rewind(infile);
|
| + cfg.g_pass = VPX_RC_LAST_PASS;
|
| + cfg.rc_twopass_stats_in = stats;
|
| + pass1(&raw, infile, outfile_arg, encoder, &cfg);
|
| + free(stats.buf);
|
|
|
| - if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
| - die_codec(&codec, "Failed to initialize encoder");
|
| -
|
| - while (vpx_img_read(&raw, infile)) {
|
| - ++frame_count;
|
| -
|
| - if (pass == 0) {
|
| - get_frame_stats(&codec, &raw, frame_count, 1, 0, VPX_DL_BEST_QUALITY,
|
| - &stats);
|
| - } else {
|
| - encode_frame(&codec, &raw, frame_count, 1, 0, VPX_DL_BEST_QUALITY,
|
| - writer);
|
| - }
|
| - }
|
| -
|
| - if (pass == 0) {
|
| - get_frame_stats(&codec, NULL, frame_count, 1, 0, VPX_DL_BEST_QUALITY,
|
| - &stats);
|
| - } else {
|
| - printf("\n");
|
| - }
|
| -
|
| - fclose(infile);
|
| - printf("Pass %d complete. Processed %d frames.\n", pass + 1, frame_count);
|
| - if (vpx_codec_destroy(&codec))
|
| - die_codec(&codec, "Failed to destroy codec.");
|
| - }
|
| -
|
| vpx_img_free(&raw);
|
| - free(stats.buf);
|
| + fclose(infile);
|
|
|
| - vpx_video_writer_close(writer);
|
| -
|
| return EXIT_SUCCESS;
|
| }
|
|
|