blob: 61d81b8d62b164dd5c9b8a99436f49fbd8a8fef9 [file] [log] [blame]
Yunqing Wang9aaa3c92016-03-25 11:57:20 -07001/*
2 * Copyright (c) 2016 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
12// VP9/VP10 Set Reference Frame
13// ============================
14//
15// This is an example demonstrating how to overwrite the VP9/VP10 encoder's
16// internal reference frame. In the sample we set the last frame to the
17// current frame. This technique could be used to bounce between two cameras.
18//
19// The decoder would also have to set the reference frame to the same value
20// on the same frame, or the video will become corrupt. The 'test_decode'
21// variable is set to 1 in this example that tests if the encoder and decoder
22// results are matching.
23//
24// Usage
25// -----
26// This example encodes a raw video. And the last argument passed in specifies
27// the frame number to update the reference frame on. For example, run
28// examples/vpx_cx_set_ref vp10 352 288 in.yuv out.ivf 4 30
29// The parameter is parsed as follows:
30//
31//
32// Extra Variables
33// ---------------
34// This example maintains the frame number passed on the command line
35// in the `update_frame_num` variable.
36//
37//
38// Configuration
39// -------------
40//
41// The reference frame is updated on the frame specified on the command
42// line.
43//
44// Observing The Effects
45// ---------------------
46// The encoder and decoder results should be matching when the same reference
47// frame setting operation is done in both encoder and decoder. Otherwise,
48// the encoder/decoder mismatch would be seen.
49
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53
54#include "vpx/vp8cx.h"
55#include "vpx/vpx_decoder.h"
56#include "vpx/vpx_encoder.h"
57
58#include "./tools_common.h"
59#include "./video_writer.h"
60
61static const char *exec_name;
62
63void usage_exit() {
64 fprintf(stderr, "Usage: %s <codec> <width> <height> <infile> <outfile> "
65 "<frame> <limit(optional)>\n",
66 exec_name);
67 exit(EXIT_FAILURE);
68}
69
70static int compare_img(const vpx_image_t *const img1,
71 const vpx_image_t *const img2) {
72 uint32_t l_w = img1->d_w;
73 uint32_t c_w =
74 (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
75 const uint32_t c_h =
76 (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
77 uint32_t i;
78 int match = 1;
79
80 match &= (img1->fmt == img2->fmt);
81 match &= (img1->d_w == img2->d_w);
82 match &= (img1->d_h == img2->d_h);
83
84 for (i = 0; i < img1->d_h; ++i)
85 match &= (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y],
86 img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y],
87 l_w) == 0);
88
89 for (i = 0; i < c_h; ++i)
90 match &= (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U],
91 img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U],
92 c_w) == 0);
93
94 for (i = 0; i < c_h; ++i)
95 match &= (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V],
96 img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V],
97 c_w) == 0);
98
99 return match;
100}
101
102#define mmin(a, b) ((a) < (b) ? (a) : (b))
103static void find_mismatch(const vpx_image_t *const img1,
104 const vpx_image_t *const img2,
105 int yloc[4], int uloc[4], int vloc[4]) {
106 const uint32_t bsize = 64;
107 const uint32_t bsizey = bsize >> img1->y_chroma_shift;
108 const uint32_t bsizex = bsize >> img1->x_chroma_shift;
109 const uint32_t c_w =
110 (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
111 const uint32_t c_h =
112 (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
113 int match = 1;
114 uint32_t i, j;
115 yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1;
116 for (i = 0, match = 1; match && i < img1->d_h; i += bsize) {
117 for (j = 0; match && j < img1->d_w; j += bsize) {
118 int k, l;
119 const int si = mmin(i + bsize, img1->d_h) - i;
120 const int sj = mmin(j + bsize, img1->d_w) - j;
121 for (k = 0; match && k < si; ++k) {
122 for (l = 0; match && l < sj; ++l) {
123 if (*(img1->planes[VPX_PLANE_Y] +
124 (i + k) * img1->stride[VPX_PLANE_Y] + j + l) !=
125 *(img2->planes[VPX_PLANE_Y] +
126 (i + k) * img2->stride[VPX_PLANE_Y] + j + l)) {
127 yloc[0] = i + k;
128 yloc[1] = j + l;
129 yloc[2] = *(img1->planes[VPX_PLANE_Y] +
130 (i + k) * img1->stride[VPX_PLANE_Y] + j + l);
131 yloc[3] = *(img2->planes[VPX_PLANE_Y] +
132 (i + k) * img2->stride[VPX_PLANE_Y] + j + l);
133 match = 0;
134 break;
135 }
136 }
137 }
138 }
139 }
140
141 uloc[0] = uloc[1] = uloc[2] = uloc[3] = -1;
142 for (i = 0, match = 1; match && i < c_h; i += bsizey) {
143 for (j = 0; match && j < c_w; j += bsizex) {
144 int k, l;
145 const int si = mmin(i + bsizey, c_h - i);
146 const int sj = mmin(j + bsizex, c_w - j);
147 for (k = 0; match && k < si; ++k) {
148 for (l = 0; match && l < sj; ++l) {
149 if (*(img1->planes[VPX_PLANE_U] +
150 (i + k) * img1->stride[VPX_PLANE_U] + j + l) !=
151 *(img2->planes[VPX_PLANE_U] +
152 (i + k) * img2->stride[VPX_PLANE_U] + j + l)) {
153 uloc[0] = i + k;
154 uloc[1] = j + l;
155 uloc[2] = *(img1->planes[VPX_PLANE_U] +
156 (i + k) * img1->stride[VPX_PLANE_U] + j + l);
157 uloc[3] = *(img2->planes[VPX_PLANE_U] +
158 (i + k) * img2->stride[VPX_PLANE_U] + j + l);
159 match = 0;
160 break;
161 }
162 }
163 }
164 }
165 }
166 vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1;
167 for (i = 0, match = 1; match && i < c_h; i += bsizey) {
168 for (j = 0; match && j < c_w; j += bsizex) {
169 int k, l;
170 const int si = mmin(i + bsizey, c_h - i);
171 const int sj = mmin(j + bsizex, c_w - j);
172 for (k = 0; match && k < si; ++k) {
173 for (l = 0; match && l < sj; ++l) {
174 if (*(img1->planes[VPX_PLANE_V] +
175 (i + k) * img1->stride[VPX_PLANE_V] + j + l) !=
176 *(img2->planes[VPX_PLANE_V] +
177 (i + k) * img2->stride[VPX_PLANE_V] + j + l)) {
178 vloc[0] = i + k;
179 vloc[1] = j + l;
180 vloc[2] = *(img1->planes[VPX_PLANE_V] +
181 (i + k) * img1->stride[VPX_PLANE_V] + j + l);
182 vloc[3] = *(img2->planes[VPX_PLANE_V] +
183 (i + k) * img2->stride[VPX_PLANE_V] + j + l);
184 match = 0;
185 break;
186 }
187 }
188 }
189 }
190 }
191}
192
193static void testing_decode(vpx_codec_ctx_t *encoder,
194 vpx_codec_ctx_t *decoder,
195 vpx_codec_enc_cfg_t *cfg,
196 unsigned int frame_out,
197 int *mismatch_seen) {
198 vpx_image_t enc_img, dec_img;
199 struct vp9_ref_frame ref_enc, ref_dec;
200
201 if (*mismatch_seen)
202 return;
203
204 ref_enc.idx = 0;
205 ref_dec.idx = 0;
206 if (vpx_codec_control(encoder, VP9_GET_REFERENCE, &ref_enc))
207 die_codec(encoder, "Failed to get encoder reference frame");
208 enc_img = ref_enc.img;
209 if (vpx_codec_control(decoder, VP9_GET_REFERENCE, &ref_dec))
210 die_codec(decoder, "Failed to get decoder reference frame");
211 dec_img = ref_dec.img;
212
213 if (!compare_img(&enc_img, &dec_img)) {
214 int y[4], u[4], v[4];
215
216 *mismatch_seen = 1;
217
218 find_mismatch(&enc_img, &dec_img, y, u, v);
219 printf("Encode/decode mismatch on frame %d at"
220 " Y[%d, %d] {%d/%d},"
221 " U[%d, %d] {%d/%d},"
222 " V[%d, %d] {%d/%d}",
223 frame_out,
224 y[0], y[1], y[2], y[3],
225 u[0], u[1], u[2], u[3],
226 v[0], v[1], v[2], v[3]);
227 }
228
229 vpx_img_free(&enc_img);
230 vpx_img_free(&dec_img);
231}
232
233static int encode_frame(vpx_codec_ctx_t *ecodec,
234 vpx_codec_enc_cfg_t *cfg,
235 vpx_image_t *img,
236 unsigned int frame_in,
237 VpxVideoWriter *writer,
238 int test_decode,
239 vpx_codec_ctx_t *dcodec,
240 unsigned int *frame_out,
241 int *mismatch_seen) {
242 int got_pkts = 0;
243 vpx_codec_iter_t iter = NULL;
244 const vpx_codec_cx_pkt_t *pkt = NULL;
245 int got_data;
246 const vpx_codec_err_t res = vpx_codec_encode(ecodec, img, frame_in, 1,
247 0, VPX_DL_GOOD_QUALITY);
248 if (res != VPX_CODEC_OK)
249 die_codec(ecodec, "Failed to encode frame");
250
251 got_data = 0;
252
253 while ((pkt = vpx_codec_get_cx_data(ecodec, &iter)) != NULL) {
254 got_pkts = 1;
255
256 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
257 const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
258
259 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) {
260 *frame_out += 1;
261 }
262
263 if (!vpx_video_writer_write_frame(writer,
264 pkt->data.frame.buf,
265 pkt->data.frame.sz,
266 pkt->data.frame.pts)) {
267 die_codec(ecodec, "Failed to write compressed frame");
268 }
269 printf(keyframe ? "K" : ".");
270 fflush(stdout);
271 got_data = 1;
272
273 // Decode 1 frame.
274 if (test_decode) {
275 if (vpx_codec_decode(dcodec, pkt->data.frame.buf,
276 (unsigned int)pkt->data.frame.sz, NULL, 0))
277 die_codec(dcodec, "Failed to decode frame.");
278 }
279 }
280 }
281
282 // Mismatch checking
283 if (got_data && test_decode) {
284 testing_decode(ecodec, dcodec, cfg, *frame_out, mismatch_seen);
285 }
286
287 return got_pkts;
288}
289
290int main(int argc, char **argv) {
291 FILE *infile = NULL;
292 // Encoder
293 vpx_codec_ctx_t ecodec = {0};
294 vpx_codec_enc_cfg_t cfg = {0};
295 unsigned int frame_in = 0;
296 vpx_image_t raw;
297 vpx_codec_err_t res;
298 VpxVideoInfo info = {0};
299 VpxVideoWriter *writer = NULL;
300 const VpxInterface *encoder = NULL;
301
302 // Test encoder/decoder mismatch.
303 int test_decode = 1;
304 // Decoder
305 vpx_codec_ctx_t dcodec;
306 unsigned int frame_out = 0;
307
308 // The frame number to set reference frame on
309 int update_frame_num = 0;
310 int mismatch_seen = 0;
311
312 const int fps = 30;
313 const int bitrate = 500;
314
315 const char *codec_arg = NULL;
316 const char *width_arg = NULL;
317 const char *height_arg = NULL;
318 const char *infile_arg = NULL;
319 const char *outfile_arg = NULL;
320 int limit = 0;
321 exec_name = argv[0];
322
323 if (argc < 7)
324 die("Invalid number of arguments");
325
326 codec_arg = argv[1];
327 width_arg = argv[2];
328 height_arg = argv[3];
329 infile_arg = argv[4];
330 outfile_arg = argv[5];
331
332 encoder = get_vpx_encoder_by_name(codec_arg);
333 if (!encoder)
334 die("Unsupported codec.");
335
336 update_frame_num = atoi(argv[6]);
337 // In VP9, the reference buffers (cm->buffer_pool->frame_bufs[i].buf) are
338 // allocated while calling vpx_codec_encode(), thus, setting reference for
339 // 1st frame isn't supported.
340 if (update_frame_num <= 1)
341 die("Couldn't parse frame number '%s'\n", argv[6]);
342
343 if (argc > 7) {
344 limit = atoi(argv[7]);
345 if (update_frame_num > limit)
346 die("Update frame number couldn't larger than limit\n");
347 }
348
349 info.codec_fourcc = encoder->fourcc;
350 info.frame_width = strtol(width_arg, NULL, 0);
351 info.frame_height = strtol(height_arg, NULL, 0);
352 info.time_base.numerator = 1;
353 info.time_base.denominator = fps;
354
355 if (info.frame_width <= 0 ||
356 info.frame_height <= 0 ||
357 (info.frame_width % 2) != 0 ||
358 (info.frame_height % 2) != 0) {
359 die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
360 }
361
362 if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
363 info.frame_height, 1)) {
364 die("Failed to allocate image.");
365 }
366
367 printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
368
369 res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
370 if (res)
371 die_codec(&ecodec, "Failed to get default codec config.");
372
373 cfg.g_w = info.frame_width;
374 cfg.g_h = info.frame_height;
375 cfg.g_timebase.num = info.time_base.numerator;
376 cfg.g_timebase.den = info.time_base.denominator;
377 cfg.rc_target_bitrate = bitrate;
378 cfg.g_lag_in_frames = 3;
379
380 writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
381 if (!writer)
382 die("Failed to open %s for writing.", outfile_arg);
383
384 if (!(infile = fopen(infile_arg, "rb")))
385 die("Failed to open %s for reading.", infile_arg);
386
387 if (vpx_codec_enc_init(&ecodec, encoder->codec_interface(), &cfg, 0))
388 die_codec(&ecodec, "Failed to initialize encoder");
389
390 // Disable alt_ref.
391 if (vpx_codec_control(&ecodec, VP8E_SET_ENABLEAUTOALTREF, 0))
392 die_codec(&ecodec, "Failed to set enable auto alt ref");
393
394 if (test_decode) {
395 const VpxInterface *decoder = get_vpx_decoder_by_name(codec_arg);
396 if (vpx_codec_dec_init(&dcodec, decoder->codec_interface(), NULL, 0))
397 die_codec(&dcodec, "Failed to initialize decoder.");
398 }
399
400 // Encode frames.
401 while (vpx_img_read(&raw, infile)) {
402 if (limit && frame_in >= limit)
403 break;
404 if (update_frame_num > 1 && frame_out + 1 == update_frame_num) {
405 vpx_ref_frame_t ref;
406 ref.frame_type = VP8_LAST_FRAME;
407 ref.img = raw;
408 // Set reference frame in encoder.
409 if (vpx_codec_control(&ecodec, VP8_SET_REFERENCE, &ref))
410 die_codec(&ecodec, "Failed to set reference frame");
411 printf(" <SET_REF>");
412
413 // If set_reference in decoder is commented out, the enc/dec mismatch
414 // would be seen.
415 if (test_decode) {
416 if (vpx_codec_control(&dcodec, VP8_SET_REFERENCE, &ref))
417 die_codec(&dcodec, "Failed to set reference frame");
418 }
419 }
420
421 encode_frame(&ecodec, &cfg, &raw, frame_in, writer, test_decode,
422 &dcodec, &frame_out, &mismatch_seen);
423 frame_in++;
424 if (mismatch_seen)
425 break;
426 }
427
428 // Flush encoder.
429 if (!mismatch_seen)
430 while (encode_frame(&ecodec, &cfg, NULL, frame_in, writer, test_decode,
431 &dcodec, &frame_out, &mismatch_seen)) {}
432
433 printf("\n");
434 fclose(infile);
435 printf("Processed %d frames.\n", frame_out);
436
437 if (test_decode) {
438 if (!mismatch_seen)
439 printf("Encoder/decoder results are matching.\n");
440 else
441 printf("Encoder/decoder results are NOT matching.\n");
442 }
443
444 if (test_decode)
445 if (vpx_codec_destroy(&dcodec))
446 die_codec(&dcodec, "Failed to destroy decoder");
447
448 vpx_img_free(&raw);
449 if (vpx_codec_destroy(&ecodec))
450 die_codec(&ecodec, "Failed to destroy encoder.");
451
452 vpx_video_writer_close(writer);
453
454 return EXIT_SUCCESS;
455}