Home | History | Annotate | Download | only in examples
      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