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