simple_encoder.c 8.96 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
John Koleszar's avatar
John Koleszar committed
3
 *
4 5 6 7 8 9
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
John Koleszar's avatar
John Koleszar committed
10 11
 */

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// 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
// -----------------
Adrian Grange's avatar
Adrian Grange committed
30
// For encoders, you only have to include `aom_encoder.h` and then any
31
// header files for the specific codecs you use. In this case, we're using
32
// vp8.
33 34 35 36 37
//
// 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
38
// conferencing application and a best quality offline encoder. These
39 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
// 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.
66 67 68 69

// Forced Keyframes
// ----------------
// Keyframes can be forced by setting the VPX_EFLAG_FORCE_KF bit of the
Adrian Grange's avatar
Adrian Grange committed
70
// flags passed to `aom_codec_control()`. In this example, we force a
71 72 73 74
// 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.
75 76 77 78 79 80 81 82
//
// 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
// -------
Adrian Grange's avatar
Adrian Grange committed
83
// The `aom_codec_destroy` call frees any memory allocated by the codec.
84 85 86 87 88
//
// 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
Adrian Grange's avatar
Adrian Grange committed
89
// few exeptions, aom_codec functions return an enumerated error status,
90
// with the value `0` indicating success.
91 92 93 94 95 96 97
//
// 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
98 99 100 101 102

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

Adrian Grange's avatar
Adrian Grange committed
103
#include "aom/aom_encoder.h"
John Koleszar's avatar
John Koleszar committed
104

Tom Finegan's avatar
Tom Finegan committed
105 106
#include "../tools_common.h"
#include "../video_writer.h"
John Koleszar's avatar
John Koleszar committed
107

108
static const char *exec_name;
109

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

Adrian Grange's avatar
Adrian Grange committed
119
static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img,
120
                        int frame_index, int flags, VpxVideoWriter *writer) {
121
  int got_pkts = 0;
Adrian Grange's avatar
Adrian Grange committed
122 123 124 125
  aom_codec_iter_t iter = NULL;
  const aom_codec_cx_pkt_t *pkt = NULL;
  const aom_codec_err_t res =
      aom_codec_encode(codec, img, frame_index, 1, flags, VPX_DL_GOOD_QUALITY);
126
  if (res != VPX_CODEC_OK) die_codec(codec, "Failed to encode frame");
127

Adrian Grange's avatar
Adrian Grange committed
128
  while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) {
129 130
    got_pkts = 1;

131 132
    if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
      const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
Adrian Grange's avatar
Adrian Grange committed
133
      if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
134 135 136 137 138 139 140 141
                                        pkt->data.frame.sz,
                                        pkt->data.frame.pts)) {
        die_codec(codec, "Failed to write compressed frame");
      }
      printf(keyframe ? "K" : ".");
      fflush(stdout);
    }
  }
142 143

  return got_pkts;
144 145
}

John Koleszar's avatar
John Koleszar committed
146
int main(int argc, char **argv) {
147
  FILE *infile = NULL;
Adrian Grange's avatar
Adrian Grange committed
148 149
  aom_codec_ctx_t codec;
  aom_codec_enc_cfg_t cfg;
150
  int frame_count = 0;
Adrian Grange's avatar
Adrian Grange committed
151 152
  aom_image_t raw;
  aom_codec_err_t res;
153
  VpxVideoInfo info = { 0 };
154
  VpxVideoWriter *writer = NULL;
155
  const VpxInterface *encoder = NULL;
156 157
  const int fps = 30;       // TODO(dkovalev) add command line argument
  const int bitrate = 200;  // kbit/s TODO(dkovalev) add command line argument
158 159 160 161
  int keyframe_interval = 0;

  // TODO(dkovalev): Add some simple command line parsing code to make the
  // command line more flexible.
162 163 164 165 166
  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;
167
  const char *keyframe_interval_arg = NULL;
168 169 170

  exec_name = argv[0];

171
  if (argc < 7) die("Invalid number of arguments");
172

173 174 175 176 177
  codec_arg = argv[1];
  width_arg = argv[2];
  height_arg = argv[3];
  infile_arg = argv[4];
  outfile_arg = argv[5];
178
  keyframe_interval_arg = argv[6];
179

Adrian Grange's avatar
Adrian Grange committed
180
  encoder = get_aom_encoder_by_name(codec_arg);
181
  if (!encoder) die("Unsupported codec.");
182 183 184 185

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

189 190
  if (info.frame_width <= 0 || info.frame_height <= 0 ||
      (info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
191 192
    die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
  }
193

Adrian Grange's avatar
Adrian Grange committed
194
  if (!aom_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
195
                     info.frame_height, 1)) {
196 197
    die("Failed to allocate image.");
  }
198

199
  keyframe_interval = strtol(keyframe_interval_arg, NULL, 0);
200
  if (keyframe_interval < 0) die("Invalid keyframe interval value.");
201

Adrian Grange's avatar
Adrian Grange committed
202
  printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
203

Adrian Grange's avatar
Adrian Grange committed
204
  res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
205
  if (res) die_codec(&codec, "Failed to get default codec config.");
206 207 208 209 210 211

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

Adrian Grange's avatar
Adrian Grange committed
214
  writer = aom_video_writer_open(outfile_arg, kContainerIVF, &info);
215
  if (!writer) die("Failed to open %s for writing.", outfile_arg);
216

217 218
  if (!(infile = fopen(infile_arg, "rb")))
    die("Failed to open %s for reading.", infile_arg);
219

Adrian Grange's avatar
Adrian Grange committed
220
  if (aom_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
221 222
    die_codec(&codec, "Failed to initialize encoder");

223
  // Encode frames.
Adrian Grange's avatar
Adrian Grange committed
224
  while (aom_img_read(&raw, infile)) {
225 226 227 228 229
    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);
  }
230 231

  // Flush encoder.
232
  while (encode_frame(&codec, NULL, -1, 0, writer)) continue;
233

234 235 236
  printf("\n");
  fclose(infile);
  printf("Processed %d frames.\n", frame_count);
237

Adrian Grange's avatar
Adrian Grange committed
238 239
  aom_img_free(&raw);
  if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
John Koleszar's avatar
John Koleszar committed
240

Adrian Grange's avatar
Adrian Grange committed
241
  aom_video_writer_close(writer);
John Koleszar's avatar
John Koleszar committed
242

243
  return EXIT_SUCCESS;
John Koleszar's avatar
John Koleszar committed
244
}