1 /* 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 // VP9 Set Reference Frame 12 // ============================ 13 // 14 // This is an example demonstrating how to overwrite the VP9 encoder's 15 // internal reference frame. In the sample we set the last frame to the 16 // current frame. This technique could be used to bounce between two cameras. 17 // 18 // The decoder would also have to set the reference frame to the same value 19 // on the same frame, or the video will become corrupt. The 'test_decode' 20 // variable is set to 1 in this example that tests if the encoder and decoder 21 // results are matching. 22 // 23 // Usage 24 // ----- 25 // This example encodes a raw video. And the last argument passed in specifies 26 // the frame number to update the reference frame on. For example, run 27 // examples/vp9cx_set_ref 352 288 in.yuv out.ivf 4 30 28 // The parameter is parsed as follows: 29 // 30 // 31 // Extra Variables 32 // --------------- 33 // This example maintains the frame number passed on the command line 34 // in the `update_frame_num` variable. 35 // 36 // 37 // Configuration 38 // ------------- 39 // 40 // The reference frame is updated on the frame specified on the command 41 // line. 42 // 43 // Observing The Effects 44 // --------------------- 45 // The encoder and decoder results should be matching when the same reference 46 // frame setting operation is done in both encoder and decoder. Otherwise, 47 // the encoder/decoder mismatch would be seen. 48 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #include "vpx/vp8cx.h" 54 #include "vpx/vpx_decoder.h" 55 #include "vpx/vpx_encoder.h" 56 #include "vp9/common/vp9_common.h" 57 58 #include "./tools_common.h" 59 #include "./video_writer.h" 60 61 static const char *exec_name; 62 63 void usage_exit() { 64 fprintf(stderr, 65 "Usage: %s <width> <height> <infile> <outfile> " 66 "<frame> <limit(optional)>\n", 67 exec_name); 68 exit(EXIT_FAILURE); 69 } 70 71 static int compare_img(const vpx_image_t *const img1, 72 const vpx_image_t *const img2) { 73 uint32_t l_w = img1->d_w; 74 uint32_t c_w = (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)) 103 static void find_mismatch(const vpx_image_t *const img1, 104 const vpx_image_t *const img2, int yloc[4], 105 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 193 static void testing_decode(vpx_codec_ctx_t *encoder, vpx_codec_ctx_t *decoder, 194 unsigned int frame_out, int *mismatch_seen) { 195 vpx_image_t enc_img, dec_img; 196 struct vp9_ref_frame ref_enc, ref_dec; 197 198 if (*mismatch_seen) return; 199 200 ref_enc.idx = 0; 201 ref_dec.idx = 0; 202 if (vpx_codec_control(encoder, VP9_GET_REFERENCE, &ref_enc)) 203 die_codec(encoder, "Failed to get encoder reference frame"); 204 enc_img = ref_enc.img; 205 if (vpx_codec_control(decoder, VP9_GET_REFERENCE, &ref_dec)) 206 die_codec(decoder, "Failed to get decoder reference frame"); 207 dec_img = ref_dec.img; 208 209 if (!compare_img(&enc_img, &dec_img)) { 210 int y[4], u[4], v[4]; 211 212 *mismatch_seen = 1; 213 214 find_mismatch(&enc_img, &dec_img, y, u, v); 215 printf( 216 "Encode/decode mismatch on frame %d at" 217 " Y[%d, %d] {%d/%d}," 218 " U[%d, %d] {%d/%d}," 219 " V[%d, %d] {%d/%d}", 220 frame_out, y[0], y[1], y[2], y[3], u[0], u[1], u[2], u[3], v[0], v[1], 221 v[2], v[3]); 222 } 223 224 vpx_img_free(&enc_img); 225 vpx_img_free(&dec_img); 226 } 227 228 static int encode_frame(vpx_codec_ctx_t *ecodec, vpx_image_t *img, 229 unsigned int frame_in, VpxVideoWriter *writer, 230 int test_decode, vpx_codec_ctx_t *dcodec, 231 unsigned int *frame_out, int *mismatch_seen) { 232 int got_pkts = 0; 233 vpx_codec_iter_t iter = NULL; 234 const vpx_codec_cx_pkt_t *pkt = NULL; 235 int got_data; 236 const vpx_codec_err_t res = 237 vpx_codec_encode(ecodec, img, frame_in, 1, 0, VPX_DL_GOOD_QUALITY); 238 if (res != VPX_CODEC_OK) die_codec(ecodec, "Failed to encode frame"); 239 240 got_data = 0; 241 242 while ((pkt = vpx_codec_get_cx_data(ecodec, &iter)) != NULL) { 243 got_pkts = 1; 244 245 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 246 const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; 247 248 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) { 249 *frame_out += 1; 250 } 251 252 if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf, 253 pkt->data.frame.sz, 254 pkt->data.frame.pts)) { 255 die_codec(ecodec, "Failed to write compressed frame"); 256 } 257 printf(keyframe ? "K" : "."); 258 fflush(stdout); 259 got_data = 1; 260 261 // Decode 1 frame. 262 if (test_decode) { 263 if (vpx_codec_decode(dcodec, pkt->data.frame.buf, 264 (unsigned int)pkt->data.frame.sz, NULL, 0)) 265 die_codec(dcodec, "Failed to decode frame."); 266 } 267 } 268 } 269 270 // Mismatch checking 271 if (got_data && test_decode) { 272 testing_decode(ecodec, dcodec, *frame_out, mismatch_seen); 273 } 274 275 return got_pkts; 276 } 277 278 int main(int argc, char **argv) { 279 FILE *infile = NULL; 280 // Encoder 281 vpx_codec_ctx_t ecodec; 282 vpx_codec_enc_cfg_t cfg; 283 unsigned int frame_in = 0; 284 vpx_image_t raw; 285 vpx_codec_err_t res; 286 VpxVideoInfo info; 287 VpxVideoWriter *writer = NULL; 288 const VpxInterface *encoder = NULL; 289 290 // Test encoder/decoder mismatch. 291 int test_decode = 1; 292 // Decoder 293 vpx_codec_ctx_t dcodec; 294 unsigned int frame_out = 0; 295 296 // The frame number to set reference frame on 297 unsigned int update_frame_num = 0; 298 int mismatch_seen = 0; 299 300 const int fps = 30; 301 const int bitrate = 500; 302 303 const char *width_arg = NULL; 304 const char *height_arg = NULL; 305 const char *infile_arg = NULL; 306 const char *outfile_arg = NULL; 307 const char *update_frame_num_arg = NULL; 308 unsigned int limit = 0; 309 310 vp9_zero(ecodec); 311 vp9_zero(cfg); 312 vp9_zero(info); 313 314 exec_name = argv[0]; 315 316 if (argc < 6) die("Invalid number of arguments"); 317 318 width_arg = argv[1]; 319 height_arg = argv[2]; 320 infile_arg = argv[3]; 321 outfile_arg = argv[4]; 322 update_frame_num_arg = argv[5]; 323 324 encoder = get_vpx_encoder_by_name("vp9"); 325 if (!encoder) die("Unsupported codec."); 326 327 update_frame_num = (unsigned int)strtoul(update_frame_num_arg, NULL, 0); 328 // In VP9, the reference buffers (cm->buffer_pool->frame_bufs[i].buf) are 329 // allocated while calling vpx_codec_encode(), thus, setting reference for 330 // 1st frame isn't supported. 331 if (update_frame_num <= 1) { 332 die("Couldn't parse frame number '%s'\n", update_frame_num_arg); 333 } 334 335 if (argc > 6) { 336 limit = (unsigned int)strtoul(argv[6], NULL, 0); 337 if (update_frame_num > limit) 338 die("Update frame number couldn't larger than limit\n"); 339 } 340 341 info.codec_fourcc = encoder->fourcc; 342 info.frame_width = (int)strtol(width_arg, NULL, 0); 343 info.frame_height = (int)strtol(height_arg, NULL, 0); 344 info.time_base.numerator = 1; 345 info.time_base.denominator = fps; 346 347 if (info.frame_width <= 0 || info.frame_height <= 0 || 348 (info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) { 349 die("Invalid frame size: %dx%d", info.frame_width, info.frame_height); 350 } 351 352 if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width, 353 info.frame_height, 1)) { 354 die("Failed to allocate image."); 355 } 356 357 printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface())); 358 359 res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0); 360 if (res) die_codec(&ecodec, "Failed to get default codec config."); 361 362 cfg.g_w = info.frame_width; 363 cfg.g_h = info.frame_height; 364 cfg.g_timebase.num = info.time_base.numerator; 365 cfg.g_timebase.den = info.time_base.denominator; 366 cfg.rc_target_bitrate = bitrate; 367 cfg.g_lag_in_frames = 3; 368 369 writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info); 370 if (!writer) die("Failed to open %s for writing.", outfile_arg); 371 372 if (!(infile = fopen(infile_arg, "rb"))) 373 die("Failed to open %s for reading.", infile_arg); 374 375 if (vpx_codec_enc_init(&ecodec, encoder->codec_interface(), &cfg, 0)) 376 die_codec(&ecodec, "Failed to initialize encoder"); 377 378 // Disable alt_ref. 379 if (vpx_codec_control(&ecodec, VP8E_SET_ENABLEAUTOALTREF, 0)) 380 die_codec(&ecodec, "Failed to set enable auto alt ref"); 381 382 if (test_decode) { 383 const VpxInterface *decoder = get_vpx_decoder_by_name("vp9"); 384 if (vpx_codec_dec_init(&dcodec, decoder->codec_interface(), NULL, 0)) 385 die_codec(&dcodec, "Failed to initialize decoder."); 386 } 387 388 // Encode frames. 389 while (vpx_img_read(&raw, infile)) { 390 if (limit && frame_in >= limit) break; 391 if (update_frame_num > 1 && frame_out + 1 == update_frame_num) { 392 vpx_ref_frame_t ref; 393 ref.frame_type = VP8_LAST_FRAME; 394 ref.img = raw; 395 // Set reference frame in encoder. 396 if (vpx_codec_control(&ecodec, VP8_SET_REFERENCE, &ref)) 397 die_codec(&ecodec, "Failed to set reference frame"); 398 printf(" <SET_REF>"); 399 400 // If set_reference in decoder is commented out, the enc/dec mismatch 401 // would be seen. 402 if (test_decode) { 403 if (vpx_codec_control(&dcodec, VP8_SET_REFERENCE, &ref)) 404 die_codec(&dcodec, "Failed to set reference frame"); 405 } 406 } 407 408 encode_frame(&ecodec, &raw, frame_in, writer, test_decode, &dcodec, 409 &frame_out, &mismatch_seen); 410 frame_in++; 411 if (mismatch_seen) break; 412 } 413 414 // Flush encoder. 415 if (!mismatch_seen) 416 while (encode_frame(&ecodec, NULL, frame_in, writer, test_decode, &dcodec, 417 &frame_out, &mismatch_seen)) { 418 } 419 420 printf("\n"); 421 fclose(infile); 422 printf("Processed %d frames.\n", frame_out); 423 424 if (test_decode) { 425 if (!mismatch_seen) 426 printf("Encoder/decoder results are matching.\n"); 427 else 428 printf("Encoder/decoder results are NOT matching.\n"); 429 } 430 431 if (test_decode) 432 if (vpx_codec_destroy(&dcodec)) 433 die_codec(&dcodec, "Failed to destroy decoder"); 434 435 vpx_img_free(&raw); 436 if (vpx_codec_destroy(&ecodec)) 437 die_codec(&ecodec, "Failed to destroy encoder."); 438 439 vpx_video_writer_close(writer); 440 441 return EXIT_SUCCESS; 442 } 443