simple_encoder.c 9.28 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
1
/*
2
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
John Koleszar's avatar
John Koleszar committed
3
 *
4
 *  Use of this source code is governed by a BSD-style license
5 6
 *  that can be found in the LICENSE file in the root of the source
 *  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.
John Koleszar's avatar
John Koleszar committed
9 10
 */

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
// Simple Encoder
// ==============
//
// This is an example of a simple encoder loop. It takes an input file in
// YV12 format, passes it through the encoder, and writes the compressed
// frames to disk in IVF format. Other decoder examples build upon this
// one.
//
// The details of the IVF format have been elided from this example for
// simplicity of presentation, as IVF files will not generally be used by
// your application. In general, an IVF file consists of a file header,
// followed by a variable number of frames. Each frame consists of a frame
// header followed by a variable length payload. The length of the payload
// is specified in the first four bytes of the frame header. The payload is
// the raw compressed data.
//
// Standard Includes
// -----------------
// For encoders, you only have to include `vpx_encoder.h` and then any
// header files for the specific codecs you use. In this case, we're using
// vp8. The `VPX_CODEC_DISABLE_COMPAT` macro can be defined to ensure
// strict compliance with the latest SDK by disabling some backwards
// compatibility features. Defining this macro is encouraged.
//
// Getting The Default Configuration
// ---------------------------------
// Encoders have the notion of "usage profiles." For example, an encoder
// may want to publish default configurations for both a video
39
// conferencing application and a best quality offline encoder. These
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
// obviously have very different default settings. Consult the
// documentation for your codec to see if it provides any default
// configurations. All codecs provide a default configuration, number 0,
// which is valid for material in the vacinity of QCIF/QVGA.
//
// Updating The Configuration
// ---------------------------------
// Almost all applications will want to update the default configuration
// with settings specific to their usage. Here we set the width and height
// of the video file to that specified on the command line. We also scale
// the default bitrate based on the ratio between the default resolution
// and the resolution specified on the command line.
//
// Initializing The Codec
// ----------------------
// The encoder is initialized by the following code.
//
// Encoding A Frame
// ----------------
// The frame is read as a continuous block (size width * height * 3 / 2)
// from the input file. If a frame was read (the input file has not hit
// EOF) then the frame is passed to the encoder. Otherwise, a NULL
// is passed, indicating the End-Of-Stream condition to the encoder. The
// `frame_cnt` is reused as the presentation time stamp (PTS) and each
// frame is shown for one frame-time in duration. The flags parameter is
// unused in this example. The deadline is set to VPX_DL_REALTIME to
// make the example run as quickly as possible.
67 68 69 70 71 72 73 74 75

// Forced Keyframes
// ----------------
// Keyframes can be forced by setting the VPX_EFLAG_FORCE_KF bit of the
// flags passed to `vpx_codec_control()`. In this example, we force a
// keyframe every <keyframe-interval> frames. Note, the output stream can
// contain additional keyframes beyond those that have been forced using the
// VPX_EFLAG_FORCE_KF flag because of automatic keyframe placement by the
// encoder.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
//
// Processing The Encoded Data
// ---------------------------
// Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data
// for this frame. We write a IVF frame header, followed by the raw data.
//
// Cleanup
// -------
// The `vpx_codec_destroy` call frees any memory allocated by the codec.
//
// Error Handling
// --------------
// This example does not special case any error return codes. If there was
// an error, a descriptive message is printed and the program exits. With
// few exeptions, vpx_codec functions return an enumerated error status,
// with the value `0` indicating success.
92 93 94 95 96 97 98
//
// Error Resiliency Features
// -------------------------
// Error resiliency is controlled by the g_error_resilient member of the
// configuration structure. Use the `decode_with_drops` example to decode with
// frames 5-10 dropped. Compare the output for a file encoded with this example
// versus one encoded with the `simple_encoder` example.
John Koleszar's avatar
John Koleszar committed
99 100 101 102 103

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

104 105
#define VPX_CODEC_DISABLE_COMPAT 1
#include "vpx/vpx_encoder.h"
John Koleszar's avatar
John Koleszar committed
106

107 108
#include "./tools_common.h"
#include "./video_writer.h"
John Koleszar's avatar
John Koleszar committed
109

110
static const char *exec_name;
111

112
void usage_exit() {
113 114
  fprintf(stderr,
          "Usage: %s <codec> <width> <height> <infile> <outfile> "
115 116
              "<keyframe-interval> [<error-resilient>]\nSee comments in "
              "simple_encoder.c for more information.\n",
117
          exec_name);
118
  exit(EXIT_FAILURE);
119
}
John Koleszar's avatar
John Koleszar committed
120

121 122 123 124 125 126
static int encode_frame(vpx_codec_ctx_t *codec,
                        vpx_image_t *img,
                        int frame_index,
                        int flags,
                        VpxVideoWriter *writer) {
  int got_pkts = 0;
127 128
  vpx_codec_iter_t iter = NULL;
  const vpx_codec_cx_pkt_t *pkt = NULL;
129 130
  const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index, 1,
                                               flags, VPX_DL_GOOD_QUALITY);
131 132 133 134
  if (res != VPX_CODEC_OK)
    die_codec(codec, "Failed to encode frame");

  while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) {
135 136
    got_pkts = 1;

137 138 139 140 141 142 143 144 145 146 147 148
    if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
      const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
      if (!vpx_video_writer_write_frame(writer,
                                        pkt->data.frame.buf,
                                        pkt->data.frame.sz,
                                        pkt->data.frame.pts)) {
        die_codec(codec, "Failed to write compressed frame");
      }
      printf(keyframe ? "K" : ".");
      fflush(stdout);
    }
  }
149 150

  return got_pkts;
151 152
}

John Koleszar's avatar
John Koleszar committed
153
int main(int argc, char **argv) {
154
  FILE *infile = NULL;
155 156 157 158 159
  vpx_codec_ctx_t codec;
  vpx_codec_enc_cfg_t cfg;
  int frame_count = 0;
  vpx_image_t raw;
  vpx_codec_err_t res;
160 161
  VpxVideoInfo info = {0};
  VpxVideoWriter *writer = NULL;
162
  const VpxInterface *encoder = NULL;
163 164
  const int fps = 30;        // TODO(dkovalev) add command line argument
  const int bitrate = 200;   // kbit/s TODO(dkovalev) add command line argument
165 166 167 168
  int keyframe_interval = 0;

  // TODO(dkovalev): Add some simple command line parsing code to make the
  // command line more flexible.
169 170 171 172 173
  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;
174
  const char *keyframe_interval_arg = NULL;
175 176 177

  exec_name = argv[0];

178
  if (argc < 7)
179 180
    die("Invalid number of arguments");

181 182 183 184 185
  codec_arg = argv[1];
  width_arg = argv[2];
  height_arg = argv[3];
  infile_arg = argv[4];
  outfile_arg = argv[5];
186
  keyframe_interval_arg = argv[6];
187

188 189 190 191 192 193 194
  encoder = get_vpx_encoder_by_name(codec_arg);
  if (!encoder)
     die("Unsupported codec.");

  info.codec_fourcc = encoder->fourcc;
  info.frame_width = strtol(width_arg, NULL, 0);
  info.frame_height = strtol(height_arg, NULL, 0);
195 196 197
  info.time_base.numerator = 1;
  info.time_base.denominator = fps;

198 199 200 201 202 203
  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);
  }
204 205

  if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
206 207 208
                                             info.frame_height, 1)) {
    die("Failed to allocate image.");
  }
209

210 211 212 213
  keyframe_interval = strtol(keyframe_interval_arg, NULL, 0);
  if (keyframe_interval < 0)
    die("Invalid keyframe interval value.");

214
  printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
215

216
  res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
217 218
  if (res)
    die_codec(&codec, "Failed to get default codec config.");
219 220 221 222 223 224

  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;
225
  cfg.g_error_resilient = argc > 7 ? strtol(argv[7], NULL, 0) : 0;
226

227
  writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
228
  if (!writer)
229
    die("Failed to open %s for writing.", outfile_arg);
230

231 232
  if (!(infile = fopen(infile_arg, "rb")))
    die("Failed to open %s for reading.", infile_arg);
233

234
  if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
235 236
    die_codec(&codec, "Failed to initialize encoder");

237
  // Encode frames.
238 239 240 241 242 243
  while (vpx_img_read(&raw, infile)) {
    int flags = 0;
    if (keyframe_interval > 0 && frame_count % keyframe_interval == 0)
      flags |= VPX_EFLAG_FORCE_KF;
    encode_frame(&codec, &raw, frame_count++, flags, writer);
  }
244 245 246

  // Flush encoder.
  while (encode_frame(&codec, NULL, -1, 0, writer)) {};
247

248 249 250
  printf("\n");
  fclose(infile);
  printf("Processed %d frames.\n", frame_count);
251

252 253 254
  vpx_img_free(&raw);
  if (vpx_codec_destroy(&codec))
    die_codec(&codec, "Failed to destroy codec.");
John Koleszar's avatar
John Koleszar committed
255

256
  vpx_video_writer_close(writer);
John Koleszar's avatar
John Koleszar committed
257

258
  return EXIT_SUCCESS;
John Koleszar's avatar
John Koleszar committed
259
}