Home | History | Annotate | Download | only in examples
      1 /*
      2  *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 // Two Pass Encoder
     12 // ================
     13 //
     14 // This is an example of a two pass encoder loop. It takes an input file in
     15 // YV12 format, passes it through the encoder twice, and writes the compressed
     16 // frames to disk in IVF format. It builds upon the simple_encoder example.
     17 //
     18 // Twopass Variables
     19 // -----------------
     20 // Twopass mode needs to track the current pass number and the buffer of
     21 // statistics packets.
     22 //
     23 // Updating The Configuration
     24 // ---------------------------------
     25 // In two pass mode, the configuration has to be updated on each pass. The
     26 // statistics buffer is passed on the last pass.
     27 //
     28 // Encoding A Frame
     29 // ----------------
     30 // Encoding a frame in two pass mode is identical to the simple encoder
     31 // example. To increase the quality while sacrificing encoding speed,
     32 // VPX_DL_BEST_QUALITY can be used in place of VPX_DL_GOOD_QUALITY.
     33 //
     34 // Processing Statistics Packets
     35 // -----------------------------
     36 // Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data
     37 // for this frame. We write a IVF frame header, followed by the raw data.
     38 //
     39 //
     40 // Pass Progress Reporting
     41 // -----------------------------
     42 // It's sometimes helpful to see when each pass completes.
     43 //
     44 //
     45 // Clean-up
     46 // -----------------------------
     47 // Destruction of the encoder instance must be done on each pass. The
     48 // raw image should be destroyed at the end as usual.
     49 
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 
     54 #include "vpx/vpx_encoder.h"
     55 
     56 #include "../tools_common.h"
     57 #include "../video_writer.h"
     58 
     59 static const char *exec_name;
     60 
     61 void usage_exit(void) {
     62   fprintf(stderr,
     63           "Usage: %s <codec> <width> <height> <infile> <outfile> "
     64           "<frame limit>\n",
     65           exec_name);
     66   exit(EXIT_FAILURE);
     67 }
     68 
     69 static int get_frame_stats(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
     70                            vpx_codec_pts_t pts, unsigned int duration,
     71                            vpx_enc_frame_flags_t flags, unsigned int deadline,
     72                            vpx_fixed_buf_t *stats) {
     73   int got_pkts = 0;
     74   vpx_codec_iter_t iter = NULL;
     75   const vpx_codec_cx_pkt_t *pkt = NULL;
     76   const vpx_codec_err_t res =
     77       vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
     78   if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to get frame stats.");
     79 
     80   while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
     81     got_pkts = 1;
     82 
     83     if (pkt->kind == VPX_CODEC_STATS_PKT) {
     84       const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
     85       const size_t pkt_size = pkt->data.twopass_stats.sz;
     86       stats->buf = realloc(stats->buf, stats->sz + pkt_size);
     87       memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
     88       stats->sz += pkt_size;
     89     }
     90   }
     91 
     92   return got_pkts;
     93 }
     94 
     95 static int encode_frame(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
     96                         vpx_codec_pts_t pts, unsigned int duration,
     97                         vpx_enc_frame_flags_t flags, unsigned int deadline,
     98                         VpxVideoWriter *writer) {
     99   int got_pkts = 0;
    100   vpx_codec_iter_t iter = NULL;
    101   const vpx_codec_cx_pkt_t *pkt = NULL;
    102   const vpx_codec_err_t res =
    103       vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
    104   if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to encode frame.");
    105 
    106   while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
    107     got_pkts = 1;
    108     if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
    109       const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
    110 
    111       if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
    112                                         pkt->data.frame.sz,
    113                                         pkt->data.frame.pts))
    114         die_codec(ctx, "Failed to write compressed frame.");
    115       printf(keyframe ? "K" : ".");
    116       fflush(stdout);
    117     }
    118   }
    119 
    120   return got_pkts;
    121 }
    122 
    123 static vpx_fixed_buf_t pass0(vpx_image_t *raw, FILE *infile,
    124                              const VpxInterface *encoder,
    125                              const vpx_codec_enc_cfg_t *cfg, int max_frames) {
    126   vpx_codec_ctx_t codec;
    127   int frame_count = 0;
    128   vpx_fixed_buf_t stats = { NULL, 0 };
    129 
    130   if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
    131     die_codec(&codec, "Failed to initialize encoder");
    132 
    133   // Calculate frame statistics.
    134   while (vpx_img_read(raw, infile)) {
    135     ++frame_count;
    136     get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
    137                     &stats);
    138     if (max_frames > 0 && frame_count >= max_frames) break;
    139   }
    140 
    141   // Flush encoder.
    142   while (get_frame_stats(&codec, NULL, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
    143                          &stats)) {
    144   }
    145 
    146   printf("Pass 0 complete. Processed %d frames.\n", frame_count);
    147   if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
    148 
    149   return stats;
    150 }
    151 
    152 static void pass1(vpx_image_t *raw, FILE *infile, const char *outfile_name,
    153                   const VpxInterface *encoder, const vpx_codec_enc_cfg_t *cfg,
    154                   int max_frames) {
    155   VpxVideoInfo info = { encoder->fourcc,
    156                         cfg->g_w,
    157                         cfg->g_h,
    158                         { cfg->g_timebase.num, cfg->g_timebase.den } };
    159   VpxVideoWriter *writer = NULL;
    160   vpx_codec_ctx_t codec;
    161   int frame_count = 0;
    162 
    163   writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info);
    164   if (!writer) die("Failed to open %s for writing", outfile_name);
    165 
    166   if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
    167     die_codec(&codec, "Failed to initialize encoder");
    168 
    169   // Encode frames.
    170   while (vpx_img_read(raw, infile)) {
    171     ++frame_count;
    172     encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, writer);
    173 
    174     if (max_frames > 0 && frame_count >= max_frames) break;
    175   }
    176 
    177   // Flush encoder.
    178   while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_GOOD_QUALITY, writer)) {
    179   }
    180 
    181   printf("\n");
    182 
    183   if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
    184 
    185   vpx_video_writer_close(writer);
    186 
    187   printf("Pass 1 complete. Processed %d frames.\n", frame_count);
    188 }
    189 
    190 int main(int argc, char **argv) {
    191   FILE *infile = NULL;
    192   int w, h;
    193   vpx_codec_ctx_t codec;
    194   vpx_codec_enc_cfg_t cfg;
    195   vpx_image_t raw;
    196   vpx_codec_err_t res;
    197   vpx_fixed_buf_t stats;
    198 
    199   const VpxInterface *encoder = NULL;
    200   const int fps = 30;       // TODO(dkovalev) add command line argument
    201   const int bitrate = 200;  // kbit/s TODO(dkovalev) add command line argument
    202   const char *const codec_arg = argv[1];
    203   const char *const width_arg = argv[2];
    204   const char *const height_arg = argv[3];
    205   const char *const infile_arg = argv[4];
    206   const char *const outfile_arg = argv[5];
    207   int max_frames = 0;
    208   exec_name = argv[0];
    209 
    210   if (argc != 7) die("Invalid number of arguments.");
    211 
    212   max_frames = (int)strtol(argv[6], NULL, 0);
    213 
    214   encoder = get_vpx_encoder_by_name(codec_arg);
    215   if (!encoder) die("Unsupported codec.");
    216 
    217   w = (int)strtol(width_arg, NULL, 0);
    218   h = (int)strtol(height_arg, NULL, 0);
    219 
    220   if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
    221     die("Invalid frame size: %dx%d", w, h);
    222 
    223   if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1))
    224     die("Failed to allocate image", w, h);
    225 
    226   printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
    227 
    228   // Configuration
    229   res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
    230   if (res) die_codec(&codec, "Failed to get default codec config.");
    231 
    232   cfg.g_w = w;
    233   cfg.g_h = h;
    234   cfg.g_timebase.num = 1;
    235   cfg.g_timebase.den = fps;
    236   cfg.rc_target_bitrate = bitrate;
    237 
    238   if (!(infile = fopen(infile_arg, "rb")))
    239     die("Failed to open %s for reading", infile_arg);
    240 
    241   // Pass 0
    242   cfg.g_pass = VPX_RC_FIRST_PASS;
    243   stats = pass0(&raw, infile, encoder, &cfg, max_frames);
    244 
    245   // Pass 1
    246   rewind(infile);
    247   cfg.g_pass = VPX_RC_LAST_PASS;
    248   cfg.rc_twopass_stats_in = stats;
    249   pass1(&raw, infile, outfile_arg, encoder, &cfg, max_frames);
    250   free(stats.buf);
    251 
    252   vpx_img_free(&raw);
    253   fclose(infile);
    254 
    255   return EXIT_SUCCESS;
    256 }
    257