目录
- 参考
- 示例说明
- 示例代码
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视频文件封装格式。
说明:
- 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