AV1 libaom编码示例simple_encoder

目录

  1. 参考
  2. 示例说明
  3. 示例代码

1. 参考

  • [1] aomedia.googlesource.com/aom/+/refs/heads/master/examples/simple_encoder.c
  • [2] github.com/FFmpeg/FFmpeg/blob/master/libavcodec/libaomenc.c
  • [3] 雷霄骅/最简单的视频编码器:基于libvpx(编码YUV为VP8)

2. 示例说明

示例主要参考了[1]。功能为把YUV420P的视频数据编码为AV1的压缩数据,使用IVF视频文件封装格式。


libaom_simple_encoder.png

说明:

  • aom_img_alloc():为结构体aom_image_t分配内存,用于存储未编码压缩的图像数据。
  • aom_codec_enc_config_default():设置参数集结构体aom_codec_enc_cfg_t的默认值。
  • aom_codec_enc_init:打开编码器,编码完成之后需使用aom_codec_destroy()关闭编码器。
  • ivf_write_header:写IVF封装格式的文件头。
  • aom_codec_encode():编码一帧图像。
  • aom_codec_get_cx_data():获取一帧压缩编码数据,数据存储在返回的aom_codec_cx_pkt_t结构体中。
  • ivf_write_frame():写IVF封装格式的文每帧数据。

3. 示例代码

#include 
#include 
#include 
#include 
#include 
#include 

#define LOG_ERROR(label)               \
  do {                                 \
    const char *l = label;             \
    va_list ap;                        \
    va_start(ap, fmt);                 \
    if (l) fprintf(stderr, "%s: ", l); \
    vfprintf(stderr, fmt, ap);         \
    fprintf(stderr, "\n");             \
    va_end(ap);                        \
  } while (0)


void io_w8(FILE *f, unsigned char b) {
    fwrite(&b, 1, 1, f);
}

void io_wl16(FILE *f, unsigned int val) {
    io_w8(f, (unsigned char)val);
    io_w8(f, (unsigned char)(val >> 8));
}

void io_wl32(FILE *f, unsigned int val) {
    io_wl16(f,  val & 0xffff);
    io_wl16(f, (val >> 16));
}

void io_wl64(FILE *f, uint64_t val) {
    io_wl32(f, (uint32_t)(val & 0xffffffff));
    io_wl32(f, (uint32_t)(val >> 32));
}

static int ivf_write_header(FILE *f, int width, int height, int framerate, int timescale) {
    fwrite("DKIF", 1, 4, f);
    io_wl16(f, 0);//version;
    io_wl16(f, 32); //header length
    fwrite("AV01", 1, 4, f);
    io_wl16(f, width);
    io_wl16(f, height);
    io_wl32(f, framerate);
    io_wl32(f, timescale);
    io_wl32(f, 0);//frame_count
    io_wl32(f, 0);//unused
    return 0;
}

static int ivf_write_frame(FILE *f, void *buf, int size, int64_t pts) {
    io_wl32(f, size);
    io_wl64(f, pts);
    fwrite(buf, 1, size, f);
    return 0;
}

static int write_header(FILE *f, int width, int height, int framerate, int timescale) {
    return ivf_write_header(f, width, height, framerate, timescale);
}

static int write_frame(FILE *outfile, void *buf, size_t sz, aom_codec_pts_t pts) {
    return ivf_write_frame(outfile, buf, sz, pts);
}

static int img_read(aom_image_t *img, FILE *file) {
  int plane;

  for (plane = 0; plane < 3; ++plane) {
    unsigned char *buf = img->planes[plane];
    const int stride = img->stride[plane];
    const int w = aom_img_plane_width(img, plane) *
                  ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
    const int h = aom_img_plane_height(img, plane);
    int y;

    for (y = 0; y < h; ++y) {
      if (fread(buf, 1, w, file) != (size_t)w) return 0;
      buf += stride;
    }
  }

  return 1;
}

static const char *exec_name;
void usage_exit(void) {
  fprintf(stderr,
          "Usage: %s       \n",
          exec_name);
  exit(EXIT_FAILURE);
}


void die(const char *fmt, ...) {
  LOG_ERROR(NULL);
  usage_exit();
}

void die_codec(aom_codec_ctx_t *ctx, const char *s) {
  const char *detail = aom_codec_error_detail(ctx);

  fprintf(stderr, "%s: %s\n", s, aom_codec_error(ctx));
  if (detail) printf("    %s\n", detail);
  exit(EXIT_FAILURE);
}

static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img,
                        int frame_index, int flags, FILE*outfile) {
  int got_pkts = 0;
  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);
  if (res != AOM_CODEC_OK) die_codec(codec, "Failed to encode frame");

  while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) {
    got_pkts = 1;
    if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
      const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
      if (write_frame(outfile, pkt->data.frame.buf,
                                        pkt->data.frame.sz,
                                        pkt->data.frame.pts) < 0) {
        die_codec(codec, "Failed to write compressed frame");
      }
      printf(keyframe ? "K" : ".");
      fflush(stdout);
    }
  }
  return got_pkts;
}

int main(int argc, char **argv) {
  FILE *infile = NULL;
  FILE *outfile = NULL;
  aom_codec_ctx_t codec;
  aom_codec_enc_cfg_t cfg;
  aom_image_t img;
  aom_codec_err_t res;
  int frame_count = 0;

  const int bitrate = 1500;
  int fps = 0;
  int keyframe_interval = 0;
  int frames_encoded = 0;
  int width = 0;
  int height = 0;
  const char *infile_arg = NULL;
  const char *outfile_arg = NULL;
  const char *keyframe_interval_arg = NULL;
  exec_name = argv[0];
  if (argc != 7) die("Invalid number of arguments");

  width = (int)strtol(argv[1], NULL, 0);
  height = (int)strtol(argv[2], NULL, 0);

  if (width <= 0 || height <= 0 ||
      (width % 2) != 0 || (height % 2) != 0) {
    die("Invalid frame size: %dx%d", width, height);
  }

  infile_arg = argv[3];
  outfile_arg = argv[4];

  fps = (int)strtol(argv[5], NULL, 0);
  if (fps <= 0) die("Invalid fps value.");

  keyframe_interval = (int)strtol(argv[6], NULL, 0);
  if (keyframe_interval < 0) die("Invalid keyframe interval value.");

  fprintf(stdout, "aom_codec_version:%s\n", aom_codec_version_str());
  fprintf(stdout, "aom_codec_build_config:%s\n", aom_codec_build_config());

  const struct aom_codec_iface *iface = aom_codec_av1_cx();

  if (!aom_img_alloc(&img, AOM_IMG_FMT_I420, width,
                     height, 1)) {
    die("Failed to allocate image.");
  }

  res = aom_codec_enc_config_default(iface, &cfg, 0);
  if (res) die_codec(&codec, "Failed to get default codec config.");

  cfg.g_w = width;
  cfg.g_h = height;
  cfg.g_timebase.num = 1;
  cfg.g_timebase.den = fps;
  cfg.rc_target_bitrate = bitrate;

  if (!(infile = fopen(infile_arg, "rb")))
    fprintf(stderr,"Failed to open %s for reading.", infile_arg);

  if (!(outfile = fopen(outfile_arg, "wb"))) {
    fprintf(stderr, "Failed to open %s for writing.", outfile_arg);
  }

  if (aom_codec_enc_init(&codec, iface, &cfg, 0))
    die_codec(&codec, "Failed to initialize encoder");

  write_header(outfile, width, height, fps, 1);  

  // Encode frames.
  while (img_read(&img, infile)) {
    int flags = 0;
    if (keyframe_interval > 0 && frame_count % keyframe_interval == 0)
      flags |= AOM_EFLAG_FORCE_KF;
    encode_frame(&codec, &img, frame_count++, flags, outfile);
    frames_encoded++;
  }

  // Flush encoder.
  while (encode_frame(&codec, NULL, -1, 0, outfile)) continue;
  printf("\n");
  fclose(infile);
  fclose(outfile);
  printf("Processed %d frames.\n", frame_count);
  aom_img_free(&img);
  if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");

  return EXIT_SUCCESS;
}
  • 示例的使用方法Usage: simple_encoder ,例如下面的运行命令。
simple_encode 1280 720 julin.yuv julin.ivf 25 0

你可能感兴趣的:(AV1 libaom编码示例simple_encoder)