twopass_encoder

00001 /*
00002  *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
00003  *
00004  *  Use of this source code is governed by a BSD-style license
00005  *  that can be found in the LICENSE file in the root of the source
00006  *  tree. An additional intellectual property rights grant can be found
00007  *  in the file PATENTS.  All contributing project authors may
00008  *  be found in the AUTHORS file in the root of the source tree.
00009  */
00010 
00011 // Two Pass Encoder
00012 // ================
00013 //
00014 // This is an example of a two pass encoder loop. It takes an input file in
00015 // YV12 format, passes it through the encoder twice, and writes the compressed
00016 // frames to disk in IVF format. It builds upon the simple_encoder example.
00017 //
00018 // Twopass Variables
00019 // -----------------
00020 // Twopass mode needs to track the current pass number and the buffer of
00021 // statistics packets.
00022 //
00023 // Updating The Configuration
00024 // ---------------------------------
00025 // In two pass mode, the configuration has to be updated on each pass. The
00026 // statistics buffer is passed on the last pass.
00027 //
00028 // Encoding A Frame
00029 // ----------------
00030 // Encoding a frame in two pass mode is identical to the simple encoder
00031 // example. To increase the quality while sacrificing encoding speed,
00032 // VPX_DL_BEST_QUALITY can be used in place of VPX_DL_GOOD_QUALITY.
00033 //
00034 // Processing Statistics Packets
00035 // -----------------------------
00036 // Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data
00037 // for this frame. We write a IVF frame header, followed by the raw data.
00038 //
00039 //
00040 // Pass Progress Reporting
00041 // -----------------------------
00042 // It's sometimes helpful to see when each pass completes.
00043 //
00044 //
00045 // Clean-up
00046 // -----------------------------
00047 // Destruction of the encoder instance must be done on each pass. The
00048 // raw image should be destroyed at the end as usual.
00049 
00050 #include <stdio.h>
00051 #include <stdlib.h>
00052 #include <string.h>
00053 
00054 #include "vpx/vpx_encoder.h"
00055 
00056 #include "../tools_common.h"
00057 #include "../video_writer.h"
00058 
00059 static const char *exec_name;
00060 
00061 void usage_exit(void) {
00062   fprintf(stderr,
00063           "Usage: %s <codec> <width> <height> <infile> <outfile> "
00064           "<frame limit>\n",
00065           exec_name);
00066   exit(EXIT_FAILURE);
00067 }
00068 
00069 static int get_frame_stats(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
00070                            vpx_codec_pts_t pts, unsigned int duration,
00071                            vpx_enc_frame_flags_t flags, unsigned int deadline,
00072                            vpx_fixed_buf_t *stats) {
00073   int got_pkts = 0;
00074   vpx_codec_iter_t iter = NULL;
00075   const vpx_codec_cx_pkt_t *pkt = NULL;
00076   const vpx_codec_err_t res =
00077       vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
00078   if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to get frame stats.");
00079 
00080   while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
00081     got_pkts = 1;
00082 
00083     if (pkt->kind == VPX_CODEC_STATS_PKT) {
00084       const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
00085       const size_t pkt_size = pkt->data.twopass_stats.sz;
00086       stats->buf = realloc(stats->buf, stats->sz + pkt_size);
00087       memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
00088       stats->sz += pkt_size;
00089     }
00090   }
00091 
00092   return got_pkts;
00093 }
00094 
00095 static int encode_frame(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
00096                         vpx_codec_pts_t pts, unsigned int duration,
00097                         vpx_enc_frame_flags_t flags, unsigned int deadline,
00098                         VpxVideoWriter *writer) {
00099   int got_pkts = 0;
00100   vpx_codec_iter_t iter = NULL;
00101   const vpx_codec_cx_pkt_t *pkt = NULL;
00102   const vpx_codec_err_t res =
00103       vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
00104   if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to encode frame.");
00105 
00106   while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
00107     got_pkts = 1;
00108     if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
00109       const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
00110 
00111       if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
00112                                         pkt->data.frame.sz,
00113                                         pkt->data.frame.pts))
00114         die_codec(ctx, "Failed to write compressed frame.");
00115       printf(keyframe ? "K" : ".");
00116       fflush(stdout);
00117     }
00118   }
00119 
00120   return got_pkts;
00121 }
00122 
00123 static vpx_fixed_buf_t pass0(vpx_image_t *raw, FILE *infile,
00124                              const VpxInterface *encoder,
00125                              const vpx_codec_enc_cfg_t *cfg, int max_frames) {
00126   vpx_codec_ctx_t codec;
00127   int frame_count = 0;
00128   vpx_fixed_buf_t stats = { NULL, 0 };
00129 
00130   if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
00131     die_codec(&codec, "Failed to initialize encoder");
00132 
00133   // Calculate frame statistics.
00134   while (vpx_img_read(raw, infile)) {
00135     ++frame_count;
00136     get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
00137                     &stats);
00138     if (max_frames > 0 && frame_count >= max_frames) break;
00139   }
00140 
00141   // Flush encoder.
00142   while (get_frame_stats(&codec, NULL, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
00143                          &stats)) {
00144   }
00145 
00146   printf("Pass 0 complete. Processed %d frames.\n", frame_count);
00147   if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
00148 
00149   return stats;
00150 }
00151 
00152 static void pass1(vpx_image_t *raw, FILE *infile, const char *outfile_name,
00153                   const VpxInterface *encoder, const vpx_codec_enc_cfg_t *cfg,
00154                   int max_frames) {
00155   VpxVideoInfo info = { encoder->fourcc,
00156                         cfg->g_w,
00157                         cfg->g_h,
00158                         { cfg->g_timebase.num, cfg->g_timebase.den } };
00159   VpxVideoWriter *writer = NULL;
00160   vpx_codec_ctx_t codec;
00161   int frame_count = 0;
00162 
00163   writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info);
00164   if (!writer) die("Failed to open %s for writing", outfile_name);
00165 
00166   if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
00167     die_codec(&codec, "Failed to initialize encoder");
00168 
00169   // Encode frames.
00170   while (vpx_img_read(raw, infile)) {
00171     ++frame_count;
00172     encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, writer);
00173 
00174     if (max_frames > 0 && frame_count >= max_frames) break;
00175   }
00176 
00177   // Flush encoder.
00178   while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_GOOD_QUALITY, writer)) {
00179   }
00180 
00181   printf("\n");
00182 
00183   if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
00184 
00185   vpx_video_writer_close(writer);
00186 
00187   printf("Pass 1 complete. Processed %d frames.\n", frame_count);
00188 }
00189 
00190 int main(int argc, char **argv) {
00191   FILE *infile = NULL;
00192   int w, h;
00193   vpx_codec_ctx_t codec;
00194   vpx_codec_enc_cfg_t cfg;
00195   vpx_image_t raw;
00196   vpx_codec_err_t res;
00197   vpx_fixed_buf_t stats;
00198 
00199   const VpxInterface *encoder = NULL;
00200   const int fps = 30;       // TODO(dkovalev) add command line argument
00201   const int bitrate = 200;  // kbit/s TODO(dkovalev) add command line argument
00202   const char *const codec_arg = argv[1];
00203   const char *const width_arg = argv[2];
00204   const char *const height_arg = argv[3];
00205   const char *const infile_arg = argv[4];
00206   const char *const outfile_arg = argv[5];
00207   int max_frames = 0;
00208   exec_name = argv[0];
00209 
00210   if (argc != 7) die("Invalid number of arguments.");
00211 
00212   max_frames = (int)strtol(argv[6], NULL, 0);
00213 
00214   encoder = get_vpx_encoder_by_name(codec_arg);
00215   if (!encoder) die("Unsupported codec.");
00216 
00217   w = (int)strtol(width_arg, NULL, 0);
00218   h = (int)strtol(height_arg, NULL, 0);
00219 
00220   if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
00221     die("Invalid frame size: %dx%d", w, h);
00222 
00223   if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1))
00224     die("Failed to allocate image", w, h);
00225 
00226   printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
00227 
00228   // Configuration
00229   res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
00230   if (res) die_codec(&codec, "Failed to get default codec config.");
00231 
00232   cfg.g_w = w;
00233   cfg.g_h = h;
00234   cfg.g_timebase.num = 1;
00235   cfg.g_timebase.den = fps;
00236   cfg.rc_target_bitrate = bitrate;
00237 
00238   if (!(infile = fopen(infile_arg, "rb")))
00239     die("Failed to open %s for reading", infile_arg);
00240 
00241   // Pass 0
00242   cfg.g_pass = VPX_RC_FIRST_PASS;
00243   stats = pass0(&raw, infile, encoder, &cfg, max_frames);
00244 
00245   // Pass 1
00246   rewind(infile);
00247   cfg.g_pass = VPX_RC_LAST_PASS;
00248   cfg.rc_twopass_stats_in = stats;
00249   pass1(&raw, infile, outfile_arg, encoder, &cfg, max_frames);
00250   free(stats.buf);
00251 
00252   vpx_img_free(&raw);
00253   fclose(infile);
00254 
00255   return EXIT_SUCCESS;
00256 }

Generated on 16 Jun 2017 for WebM Codec SDK by  doxygen 1.6.1