AV1 libaom编码示例simple_encoder

目录

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

1. 参考

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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <aom/aom_encoder.h>
#include <aom/aomcx.h>

#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 <width> <height> <infile> <outfile> <fps> <keyframe-interval> \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 <width> <height> <infile> <outfile> <fps> <keyframe-interval>,例如下面的运行命令。
simple_encode 1280 720 julin.yuv julin.ivf 25 0
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容