Moved the API patch from NextGen to NextGenv2 and also added this API to VP10. An example was included. To try it, for example, run the following command: $ examples/vpx_cx_set_ref vp10 352 288 in.yuv out.ivf 4 30 Change-Id: Ib56bc3d365e530cfc8d859a13ddbf4c007907b81
		
			
				
	
	
		
			456 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			456 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *  Copyright (c) 2016 The WebM project authors. All Rights Reserved.
 | 
						|
 *
 | 
						|
 *  Use of this source code is governed by a BSD-style license
 | 
						|
 *  that can be found in the LICENSE file in the root of the source
 | 
						|
 *  tree. An additional intellectual property rights grant can be found
 | 
						|
 *  in the file PATENTS.  All contributing project authors may
 | 
						|
 *  be found in the AUTHORS file in the root of the source tree.
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
// VP9/VP10 Set Reference Frame
 | 
						|
// ============================
 | 
						|
//
 | 
						|
// This is an example demonstrating how to overwrite the VP9/VP10 encoder's
 | 
						|
// internal reference frame. In the sample we set the last frame to the
 | 
						|
// current frame. This technique could be used to bounce between two cameras.
 | 
						|
//
 | 
						|
// The decoder would also have to set the reference frame to the same value
 | 
						|
// on the same frame, or the video will become corrupt. The 'test_decode'
 | 
						|
// variable is set to 1 in this example that tests if the encoder and decoder
 | 
						|
// results are matching.
 | 
						|
//
 | 
						|
// Usage
 | 
						|
// -----
 | 
						|
// This example encodes a raw video. And the last argument passed in specifies
 | 
						|
// the frame number to update the reference frame on. For example, run
 | 
						|
// examples/vpx_cx_set_ref vp10 352 288 in.yuv out.ivf 4 30
 | 
						|
// The parameter is parsed as follows:
 | 
						|
//
 | 
						|
//
 | 
						|
// Extra Variables
 | 
						|
// ---------------
 | 
						|
// This example maintains the frame number passed on the command line
 | 
						|
// in the `update_frame_num` variable.
 | 
						|
//
 | 
						|
//
 | 
						|
// Configuration
 | 
						|
// -------------
 | 
						|
//
 | 
						|
// The reference frame is updated on the frame specified on the command
 | 
						|
// line.
 | 
						|
//
 | 
						|
// Observing The Effects
 | 
						|
// ---------------------
 | 
						|
// The encoder and decoder results should be matching when the same reference
 | 
						|
// frame setting operation is done in both encoder and decoder. Otherwise,
 | 
						|
// the encoder/decoder mismatch would be seen.
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include "vpx/vp8cx.h"
 | 
						|
#include "vpx/vpx_decoder.h"
 | 
						|
#include "vpx/vpx_encoder.h"
 | 
						|
 | 
						|
#include "./tools_common.h"
 | 
						|
#include "./video_writer.h"
 | 
						|
 | 
						|
static const char *exec_name;
 | 
						|
 | 
						|
void usage_exit() {
 | 
						|
  fprintf(stderr, "Usage: %s <codec> <width> <height> <infile> <outfile> "
 | 
						|
          "<frame> <limit(optional)>\n",
 | 
						|
          exec_name);
 | 
						|
  exit(EXIT_FAILURE);
 | 
						|
}
 | 
						|
 | 
						|
static int compare_img(const vpx_image_t *const img1,
 | 
						|
                       const vpx_image_t *const img2) {
 | 
						|
  uint32_t l_w = img1->d_w;
 | 
						|
  uint32_t c_w =
 | 
						|
      (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
 | 
						|
  const uint32_t c_h =
 | 
						|
      (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
 | 
						|
  uint32_t i;
 | 
						|
  int match = 1;
 | 
						|
 | 
						|
  match &= (img1->fmt == img2->fmt);
 | 
						|
  match &= (img1->d_w == img2->d_w);
 | 
						|
  match &= (img1->d_h == img2->d_h);
 | 
						|
 | 
						|
  for (i = 0; i < img1->d_h; ++i)
 | 
						|
    match &= (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y],
 | 
						|
                     img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y],
 | 
						|
                     l_w) == 0);
 | 
						|
 | 
						|
  for (i = 0; i < c_h; ++i)
 | 
						|
    match &= (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U],
 | 
						|
                     img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U],
 | 
						|
                     c_w) == 0);
 | 
						|
 | 
						|
  for (i = 0; i < c_h; ++i)
 | 
						|
    match &= (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V],
 | 
						|
                     img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V],
 | 
						|
                     c_w) == 0);
 | 
						|
 | 
						|
  return match;
 | 
						|
}
 | 
						|
 | 
						|
#define mmin(a, b)  ((a) < (b) ? (a) : (b))
 | 
						|
static void find_mismatch(const vpx_image_t *const img1,
 | 
						|
                          const vpx_image_t *const img2,
 | 
						|
                          int yloc[4], int uloc[4], int vloc[4]) {
 | 
						|
  const uint32_t bsize = 64;
 | 
						|
  const uint32_t bsizey = bsize >> img1->y_chroma_shift;
 | 
						|
  const uint32_t bsizex = bsize >> img1->x_chroma_shift;
 | 
						|
  const uint32_t c_w =
 | 
						|
      (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
 | 
						|
  const uint32_t c_h =
 | 
						|
      (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
 | 
						|
  int match = 1;
 | 
						|
  uint32_t i, j;
 | 
						|
  yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1;
 | 
						|
  for (i = 0, match = 1; match && i < img1->d_h; i += bsize) {
 | 
						|
    for (j = 0; match && j < img1->d_w; j += bsize) {
 | 
						|
      int k, l;
 | 
						|
      const int si = mmin(i + bsize, img1->d_h) - i;
 | 
						|
      const int sj = mmin(j + bsize, img1->d_w) - j;
 | 
						|
      for (k = 0; match && k < si; ++k) {
 | 
						|
        for (l = 0; match && l < sj; ++l) {
 | 
						|
          if (*(img1->planes[VPX_PLANE_Y] +
 | 
						|
                (i + k) * img1->stride[VPX_PLANE_Y] + j + l) !=
 | 
						|
              *(img2->planes[VPX_PLANE_Y] +
 | 
						|
                (i + k) * img2->stride[VPX_PLANE_Y] + j + l)) {
 | 
						|
            yloc[0] = i + k;
 | 
						|
            yloc[1] = j + l;
 | 
						|
            yloc[2] = *(img1->planes[VPX_PLANE_Y] +
 | 
						|
                        (i + k) * img1->stride[VPX_PLANE_Y] + j + l);
 | 
						|
            yloc[3] = *(img2->planes[VPX_PLANE_Y] +
 | 
						|
                        (i + k) * img2->stride[VPX_PLANE_Y] + j + l);
 | 
						|
            match = 0;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  uloc[0] = uloc[1] = uloc[2] = uloc[3] = -1;
 | 
						|
  for (i = 0, match = 1; match && i < c_h; i += bsizey) {
 | 
						|
    for (j = 0; match && j < c_w; j += bsizex) {
 | 
						|
      int k, l;
 | 
						|
      const int si = mmin(i + bsizey, c_h - i);
 | 
						|
      const int sj = mmin(j + bsizex, c_w - j);
 | 
						|
      for (k = 0; match && k < si; ++k) {
 | 
						|
        for (l = 0; match && l < sj; ++l) {
 | 
						|
          if (*(img1->planes[VPX_PLANE_U] +
 | 
						|
                (i + k) * img1->stride[VPX_PLANE_U] + j + l) !=
 | 
						|
              *(img2->planes[VPX_PLANE_U] +
 | 
						|
                (i + k) * img2->stride[VPX_PLANE_U] + j + l)) {
 | 
						|
            uloc[0] = i + k;
 | 
						|
            uloc[1] = j + l;
 | 
						|
            uloc[2] = *(img1->planes[VPX_PLANE_U] +
 | 
						|
                        (i + k) * img1->stride[VPX_PLANE_U] + j + l);
 | 
						|
            uloc[3] = *(img2->planes[VPX_PLANE_U] +
 | 
						|
                        (i + k) * img2->stride[VPX_PLANE_U] + j + l);
 | 
						|
            match = 0;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1;
 | 
						|
  for (i = 0, match = 1; match && i < c_h; i += bsizey) {
 | 
						|
    for (j = 0; match && j < c_w; j += bsizex) {
 | 
						|
      int k, l;
 | 
						|
      const int si = mmin(i + bsizey, c_h - i);
 | 
						|
      const int sj = mmin(j + bsizex, c_w - j);
 | 
						|
      for (k = 0; match && k < si; ++k) {
 | 
						|
        for (l = 0; match && l < sj; ++l) {
 | 
						|
          if (*(img1->planes[VPX_PLANE_V] +
 | 
						|
                (i + k) * img1->stride[VPX_PLANE_V] + j + l) !=
 | 
						|
              *(img2->planes[VPX_PLANE_V] +
 | 
						|
                (i + k) * img2->stride[VPX_PLANE_V] + j + l)) {
 | 
						|
            vloc[0] = i + k;
 | 
						|
            vloc[1] = j + l;
 | 
						|
            vloc[2] = *(img1->planes[VPX_PLANE_V] +
 | 
						|
                        (i + k) * img1->stride[VPX_PLANE_V] + j + l);
 | 
						|
            vloc[3] = *(img2->planes[VPX_PLANE_V] +
 | 
						|
                        (i + k) * img2->stride[VPX_PLANE_V] + j + l);
 | 
						|
            match = 0;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void testing_decode(vpx_codec_ctx_t *encoder,
 | 
						|
                           vpx_codec_ctx_t *decoder,
 | 
						|
                           vpx_codec_enc_cfg_t *cfg,
 | 
						|
                           unsigned int frame_out,
 | 
						|
                           int *mismatch_seen) {
 | 
						|
  vpx_image_t enc_img, dec_img;
 | 
						|
  struct vp9_ref_frame ref_enc, ref_dec;
 | 
						|
 | 
						|
  if (*mismatch_seen)
 | 
						|
    return;
 | 
						|
 | 
						|
  ref_enc.idx = 0;
 | 
						|
  ref_dec.idx = 0;
 | 
						|
  if (vpx_codec_control(encoder, VP9_GET_REFERENCE, &ref_enc))
 | 
						|
    die_codec(encoder,  "Failed to get encoder reference frame");
 | 
						|
  enc_img = ref_enc.img;
 | 
						|
  if (vpx_codec_control(decoder, VP9_GET_REFERENCE, &ref_dec))
 | 
						|
    die_codec(decoder, "Failed to get decoder reference frame");
 | 
						|
  dec_img = ref_dec.img;
 | 
						|
 | 
						|
  if (!compare_img(&enc_img, &dec_img)) {
 | 
						|
    int y[4], u[4], v[4];
 | 
						|
 | 
						|
    *mismatch_seen = 1;
 | 
						|
 | 
						|
    find_mismatch(&enc_img, &dec_img, y, u, v);
 | 
						|
    printf("Encode/decode mismatch on frame %d at"
 | 
						|
           " Y[%d, %d] {%d/%d},"
 | 
						|
           " U[%d, %d] {%d/%d},"
 | 
						|
           " V[%d, %d] {%d/%d}",
 | 
						|
           frame_out,
 | 
						|
           y[0], y[1], y[2], y[3],
 | 
						|
           u[0], u[1], u[2], u[3],
 | 
						|
           v[0], v[1], v[2], v[3]);
 | 
						|
  }
 | 
						|
 | 
						|
  vpx_img_free(&enc_img);
 | 
						|
  vpx_img_free(&dec_img);
 | 
						|
}
 | 
						|
 | 
						|
static int encode_frame(vpx_codec_ctx_t *ecodec,
 | 
						|
                        vpx_codec_enc_cfg_t *cfg,
 | 
						|
                        vpx_image_t *img,
 | 
						|
                        unsigned int frame_in,
 | 
						|
                        VpxVideoWriter *writer,
 | 
						|
                        int test_decode,
 | 
						|
                        vpx_codec_ctx_t *dcodec,
 | 
						|
                        unsigned int *frame_out,
 | 
						|
                        int *mismatch_seen) {
 | 
						|
  int got_pkts = 0;
 | 
						|
  vpx_codec_iter_t iter = NULL;
 | 
						|
  const vpx_codec_cx_pkt_t *pkt = NULL;
 | 
						|
  int got_data;
 | 
						|
  const vpx_codec_err_t res = vpx_codec_encode(ecodec, img, frame_in, 1,
 | 
						|
                                               0, VPX_DL_GOOD_QUALITY);
 | 
						|
  if (res != VPX_CODEC_OK)
 | 
						|
    die_codec(ecodec, "Failed to encode frame");
 | 
						|
 | 
						|
  got_data = 0;
 | 
						|
 | 
						|
  while ((pkt = vpx_codec_get_cx_data(ecodec, &iter)) != NULL) {
 | 
						|
    got_pkts = 1;
 | 
						|
 | 
						|
    if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
 | 
						|
      const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
 | 
						|
 | 
						|
      if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) {
 | 
						|
                *frame_out += 1;
 | 
						|
        }
 | 
						|
 | 
						|
      if (!vpx_video_writer_write_frame(writer,
 | 
						|
                                        pkt->data.frame.buf,
 | 
						|
                                        pkt->data.frame.sz,
 | 
						|
                                        pkt->data.frame.pts)) {
 | 
						|
        die_codec(ecodec, "Failed to write compressed frame");
 | 
						|
      }
 | 
						|
      printf(keyframe ? "K" : ".");
 | 
						|
      fflush(stdout);
 | 
						|
      got_data = 1;
 | 
						|
 | 
						|
      // Decode 1 frame.
 | 
						|
      if (test_decode) {
 | 
						|
        if (vpx_codec_decode(dcodec, pkt->data.frame.buf,
 | 
						|
                             (unsigned int)pkt->data.frame.sz, NULL, 0))
 | 
						|
          die_codec(dcodec, "Failed to decode frame.");
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Mismatch checking
 | 
						|
  if (got_data && test_decode) {
 | 
						|
    testing_decode(ecodec, dcodec, cfg, *frame_out, mismatch_seen);
 | 
						|
  }
 | 
						|
 | 
						|
  return got_pkts;
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv) {
 | 
						|
  FILE *infile = NULL;
 | 
						|
  // Encoder
 | 
						|
  vpx_codec_ctx_t ecodec = {0};
 | 
						|
  vpx_codec_enc_cfg_t cfg = {0};
 | 
						|
  unsigned int frame_in = 0;
 | 
						|
  vpx_image_t raw;
 | 
						|
  vpx_codec_err_t res;
 | 
						|
  VpxVideoInfo info = {0};
 | 
						|
  VpxVideoWriter *writer = NULL;
 | 
						|
  const VpxInterface *encoder = NULL;
 | 
						|
 | 
						|
  // Test encoder/decoder mismatch.
 | 
						|
  int test_decode = 1;
 | 
						|
  // Decoder
 | 
						|
  vpx_codec_ctx_t dcodec;
 | 
						|
  unsigned int frame_out = 0;
 | 
						|
 | 
						|
  // The frame number to set reference frame on
 | 
						|
  int update_frame_num = 0;
 | 
						|
  int mismatch_seen = 0;
 | 
						|
 | 
						|
  const int fps = 30;
 | 
						|
  const int bitrate = 500;
 | 
						|
 | 
						|
  const char *codec_arg = NULL;
 | 
						|
  const char *width_arg = NULL;
 | 
						|
  const char *height_arg = NULL;
 | 
						|
  const char *infile_arg = NULL;
 | 
						|
  const char *outfile_arg = NULL;
 | 
						|
  int limit = 0;
 | 
						|
  exec_name = argv[0];
 | 
						|
 | 
						|
  if (argc < 7)
 | 
						|
    die("Invalid number of arguments");
 | 
						|
 | 
						|
  codec_arg = argv[1];
 | 
						|
  width_arg = argv[2];
 | 
						|
  height_arg = argv[3];
 | 
						|
  infile_arg = argv[4];
 | 
						|
  outfile_arg = argv[5];
 | 
						|
 | 
						|
  encoder = get_vpx_encoder_by_name(codec_arg);
 | 
						|
  if (!encoder)
 | 
						|
    die("Unsupported codec.");
 | 
						|
 | 
						|
  update_frame_num = atoi(argv[6]);
 | 
						|
  // In VP9, the reference buffers (cm->buffer_pool->frame_bufs[i].buf) are
 | 
						|
  // allocated while calling vpx_codec_encode(), thus, setting reference for
 | 
						|
  // 1st frame isn't supported.
 | 
						|
  if (update_frame_num <= 1)
 | 
						|
    die("Couldn't parse frame number '%s'\n", argv[6]);
 | 
						|
 | 
						|
  if (argc > 7) {
 | 
						|
    limit = atoi(argv[7]);
 | 
						|
    if (update_frame_num > limit)
 | 
						|
      die("Update frame number couldn't larger than limit\n");
 | 
						|
  }
 | 
						|
 | 
						|
  info.codec_fourcc = encoder->fourcc;
 | 
						|
  info.frame_width = strtol(width_arg, NULL, 0);
 | 
						|
  info.frame_height = strtol(height_arg, NULL, 0);
 | 
						|
  info.time_base.numerator = 1;
 | 
						|
  info.time_base.denominator = fps;
 | 
						|
 | 
						|
  if (info.frame_width <= 0 ||
 | 
						|
      info.frame_height <= 0 ||
 | 
						|
      (info.frame_width % 2) != 0 ||
 | 
						|
      (info.frame_height % 2) != 0) {
 | 
						|
    die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
 | 
						|
                                             info.frame_height, 1)) {
 | 
						|
    die("Failed to allocate image.");
 | 
						|
  }
 | 
						|
 | 
						|
  printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
 | 
						|
 | 
						|
  res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
 | 
						|
  if (res)
 | 
						|
    die_codec(&ecodec, "Failed to get default codec config.");
 | 
						|
 | 
						|
  cfg.g_w = info.frame_width;
 | 
						|
  cfg.g_h = info.frame_height;
 | 
						|
  cfg.g_timebase.num = info.time_base.numerator;
 | 
						|
  cfg.g_timebase.den = info.time_base.denominator;
 | 
						|
  cfg.rc_target_bitrate = bitrate;
 | 
						|
  cfg.g_lag_in_frames = 3;
 | 
						|
 | 
						|
  writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
 | 
						|
  if (!writer)
 | 
						|
    die("Failed to open %s for writing.", outfile_arg);
 | 
						|
 | 
						|
  if (!(infile = fopen(infile_arg, "rb")))
 | 
						|
    die("Failed to open %s for reading.", infile_arg);
 | 
						|
 | 
						|
  if (vpx_codec_enc_init(&ecodec, encoder->codec_interface(), &cfg, 0))
 | 
						|
    die_codec(&ecodec, "Failed to initialize encoder");
 | 
						|
 | 
						|
  // Disable alt_ref.
 | 
						|
  if (vpx_codec_control(&ecodec, VP8E_SET_ENABLEAUTOALTREF, 0))
 | 
						|
    die_codec(&ecodec, "Failed to set enable auto alt ref");
 | 
						|
 | 
						|
  if (test_decode) {
 | 
						|
      const VpxInterface *decoder = get_vpx_decoder_by_name(codec_arg);
 | 
						|
      if (vpx_codec_dec_init(&dcodec, decoder->codec_interface(), NULL, 0))
 | 
						|
        die_codec(&dcodec, "Failed to initialize decoder.");
 | 
						|
  }
 | 
						|
 | 
						|
  // Encode frames.
 | 
						|
  while (vpx_img_read(&raw, infile)) {
 | 
						|
    if (limit && frame_in >= limit)
 | 
						|
      break;
 | 
						|
    if (update_frame_num > 1 && frame_out + 1 == update_frame_num) {
 | 
						|
      vpx_ref_frame_t ref;
 | 
						|
      ref.frame_type = VP8_LAST_FRAME;
 | 
						|
      ref.img = raw;
 | 
						|
      // Set reference frame in encoder.
 | 
						|
      if (vpx_codec_control(&ecodec, VP8_SET_REFERENCE, &ref))
 | 
						|
        die_codec(&ecodec, "Failed to set reference frame");
 | 
						|
      printf(" <SET_REF>");
 | 
						|
 | 
						|
      // If set_reference in decoder is commented out, the enc/dec mismatch
 | 
						|
      // would be seen.
 | 
						|
      if (test_decode) {
 | 
						|
        if (vpx_codec_control(&dcodec, VP8_SET_REFERENCE, &ref))
 | 
						|
          die_codec(&dcodec, "Failed to set reference frame");
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    encode_frame(&ecodec, &cfg, &raw, frame_in, writer, test_decode,
 | 
						|
                 &dcodec, &frame_out, &mismatch_seen);
 | 
						|
    frame_in++;
 | 
						|
    if (mismatch_seen)
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  // Flush encoder.
 | 
						|
  if (!mismatch_seen)
 | 
						|
    while (encode_frame(&ecodec, &cfg, NULL, frame_in, writer, test_decode,
 | 
						|
                        &dcodec, &frame_out, &mismatch_seen)) {}
 | 
						|
 | 
						|
  printf("\n");
 | 
						|
  fclose(infile);
 | 
						|
  printf("Processed %d frames.\n", frame_out);
 | 
						|
 | 
						|
  if (test_decode) {
 | 
						|
    if (!mismatch_seen)
 | 
						|
      printf("Encoder/decoder results are matching.\n");
 | 
						|
    else
 | 
						|
      printf("Encoder/decoder results are NOT matching.\n");
 | 
						|
  }
 | 
						|
 | 
						|
  if (test_decode)
 | 
						|
    if (vpx_codec_destroy(&dcodec))
 | 
						|
      die_codec(&dcodec, "Failed to destroy decoder");
 | 
						|
 | 
						|
  vpx_img_free(&raw);
 | 
						|
  if (vpx_codec_destroy(&ecodec))
 | 
						|
    die_codec(&ecodec, "Failed to destroy encoder.");
 | 
						|
 | 
						|
  vpx_video_writer_close(writer);
 | 
						|
 | 
						|
  return EXIT_SUCCESS;
 | 
						|
}
 |