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