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 59static const char *exec_name; 60 61void usage_exit(void) { 62 fprintf(stderr, "Usage: %s <codec> <width> <height> <infile> <outfile>\n", 63 exec_name); 64 exit(EXIT_FAILURE); 65} 66 67static int get_frame_stats(vpx_codec_ctx_t *ctx, 68 const vpx_image_t *img, 69 vpx_codec_pts_t pts, 70 unsigned int duration, 71 vpx_enc_frame_flags_t flags, 72 unsigned int deadline, 73 vpx_fixed_buf_t *stats) { 74 int got_pkts = 0; 75 vpx_codec_iter_t iter = NULL; 76 const vpx_codec_cx_pkt_t *pkt = NULL; 77 const vpx_codec_err_t res = vpx_codec_encode(ctx, img, pts, duration, flags, 78 deadline); 79 if (res != VPX_CODEC_OK) 80 die_codec(ctx, "Failed to get frame stats."); 81 82 while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) { 83 got_pkts = 1; 84 85 if (pkt->kind == VPX_CODEC_STATS_PKT) { 86 const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf; 87 const size_t pkt_size = pkt->data.twopass_stats.sz; 88 stats->buf = realloc(stats->buf, stats->sz + pkt_size); 89 memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size); 90 stats->sz += pkt_size; 91 } 92 } 93 94 return got_pkts; 95} 96 97static int encode_frame(vpx_codec_ctx_t *ctx, 98 const vpx_image_t *img, 99 vpx_codec_pts_t pts, 100 unsigned int duration, 101 vpx_enc_frame_flags_t flags, 102 unsigned int deadline, 103 VpxVideoWriter *writer) { 104 int got_pkts = 0; 105 vpx_codec_iter_t iter = NULL; 106 const vpx_codec_cx_pkt_t *pkt = NULL; 107 const vpx_codec_err_t res = vpx_codec_encode(ctx, img, pts, duration, flags, 108 deadline); 109 if (res != VPX_CODEC_OK) 110 die_codec(ctx, "Failed to encode frame."); 111 112 while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) { 113 got_pkts = 1; 114 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 115 const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; 116 117 if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf, 118 pkt->data.frame.sz, 119 pkt->data.frame.pts)) 120 die_codec(ctx, "Failed to write compressed frame."); 121 printf(keyframe ? "K" : "."); 122 fflush(stdout); 123 } 124 } 125 126 return got_pkts; 127} 128 129static vpx_fixed_buf_t pass0(vpx_image_t *raw, 130 FILE *infile, 131 const VpxInterface *encoder, 132 const vpx_codec_enc_cfg_t *cfg) { 133 vpx_codec_ctx_t codec; 134 int frame_count = 0; 135 vpx_fixed_buf_t stats = {NULL, 0}; 136 137 if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0)) 138 die_codec(&codec, "Failed to initialize encoder"); 139 140 // Calculate frame statistics. 141 while (vpx_img_read(raw, infile)) { 142 ++frame_count; 143 get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, 144 &stats); 145 } 146 147 // Flush encoder. 148 while (get_frame_stats(&codec, NULL, frame_count, 1, 0, 149 VPX_DL_GOOD_QUALITY, &stats)) {} 150 151 printf("Pass 0 complete. Processed %d frames.\n", frame_count); 152 if (vpx_codec_destroy(&codec)) 153 die_codec(&codec, "Failed to destroy codec."); 154 155 return stats; 156} 157 158static void pass1(vpx_image_t *raw, 159 FILE *infile, 160 const char *outfile_name, 161 const VpxInterface *encoder, 162 const vpx_codec_enc_cfg_t *cfg) { 163 VpxVideoInfo info = { 164 encoder->fourcc, 165 cfg->g_w, 166 cfg->g_h, 167 {cfg->g_timebase.num, cfg->g_timebase.den} 168 }; 169 VpxVideoWriter *writer = NULL; 170 vpx_codec_ctx_t codec; 171 int frame_count = 0; 172 173 writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info); 174 if (!writer) 175 die("Failed to open %s for writing", outfile_name); 176 177 if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0)) 178 die_codec(&codec, "Failed to initialize encoder"); 179 180 // Encode frames. 181 while (vpx_img_read(raw, infile)) { 182 ++frame_count; 183 encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, writer); 184 } 185 186 // Flush encoder. 187 while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_GOOD_QUALITY, writer)) {} 188 189 printf("\n"); 190 191 if (vpx_codec_destroy(&codec)) 192 die_codec(&codec, "Failed to destroy codec."); 193 194 vpx_video_writer_close(writer); 195 196 printf("Pass 1 complete. Processed %d frames.\n", frame_count); 197} 198 199int main(int argc, char **argv) { 200 FILE *infile = NULL; 201 int w, h; 202 vpx_codec_ctx_t codec; 203 vpx_codec_enc_cfg_t cfg; 204 vpx_image_t raw; 205 vpx_codec_err_t res; 206 vpx_fixed_buf_t stats; 207 208 const VpxInterface *encoder = NULL; 209 const int fps = 30; // TODO(dkovalev) add command line argument 210 const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument 211 const char *const codec_arg = argv[1]; 212 const char *const width_arg = argv[2]; 213 const char *const height_arg = argv[3]; 214 const char *const infile_arg = argv[4]; 215 const char *const outfile_arg = argv[5]; 216 exec_name = argv[0]; 217 218 if (argc != 6) 219 die("Invalid number of arguments."); 220 221 encoder = get_vpx_encoder_by_name(codec_arg); 222 if (!encoder) 223 die("Unsupported codec."); 224 225 w = strtol(width_arg, NULL, 0); 226 h = strtol(height_arg, NULL, 0); 227 228 if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0) 229 die("Invalid frame size: %dx%d", w, h); 230 231 if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1)) 232 die("Failed to allocate image", w, h); 233 234 printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface())); 235 236 // Configuration 237 res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0); 238 if (res) 239 die_codec(&codec, "Failed to get default codec config."); 240 241 cfg.g_w = w; 242 cfg.g_h = h; 243 cfg.g_timebase.num = 1; 244 cfg.g_timebase.den = fps; 245 cfg.rc_target_bitrate = bitrate; 246 247 if (!(infile = fopen(infile_arg, "rb"))) 248 die("Failed to open %s for reading", infile_arg); 249 250 // Pass 0 251 cfg.g_pass = VPX_RC_FIRST_PASS; 252 stats = pass0(&raw, infile, encoder, &cfg); 253 254 // Pass 1 255 rewind(infile); 256 cfg.g_pass = VPX_RC_LAST_PASS; 257 cfg.rc_twopass_stats_in = stats; 258 pass1(&raw, infile, outfile_arg, encoder, &cfg); 259 free(stats.buf); 260 261 vpx_img_free(&raw); 262 fclose(infile); 263 264 return EXIT_SUCCESS; 265} 266