函数说明:
avcodec_find_encoder_by_name:根据指定的编码器名称查找注册的编码器。
avcodec_alloc_context3:为AVCodecContext分配内存。 avcodec_open2:打开编解码器。
avcodec_send_frame:将AVFrame⾮压缩数据给编码器。。
avcodec_receive_packet:获取到编码后的AVPacket数据。
av_frame_get_buffer: 为⾳频或视频数据分配新的buffer。在调⽤这个函数之前,必须在AVFame上设 置好以下属性:format(视频为像素格式,⾳频为样本格式)、nb_samples(样本个数,针对⾳频)、 channel_layout(通道类型,针对⾳频)、width/height(宽⾼,针对视频)。
av_frame_make_writable:确保AVFrame是可写的,尽可能避免数据的复制。 如果AVFrame不是是可写的,将分配新的buffer和复制数据。
av_image_fill_arrays: 存储⼀帧像素数据存储到AVFrame对应的data buffer。
常用参数
char *in_yuv_file = NULL;
char *out_h264_file = NULL;
FILE *infile = NULL;
FILE *outfile = NULL;
const char *codec_name = NULL;
const AVCodec *codec = NULL;
AVCodecContext *codec_ctx= NULL;
AVFrame *frame = NULL;
AVPacket *pkt = NULL;
初始化编码器 设定编码参数在后面
codec = avcodec_find_encoder_by_name(codec_name);
codec_ctx = avcodec_alloc_context3(codec);
//设定参数
ret = avcodec_open2(codec_ctx, codec, NULL);
设定具体参数 常规 输入格式为YUV420P
//set up resolution
codec_ctx->width = 1280;
codec_ctx->height = 720;
//set up time base
codec_ctx->time_base = (AVRational){1, 25};
codec_ctx->framerate = (AVRational){25, 1};
//set i-frame interval
codec_ctx->gop_size = 25;
codec_ctx->max_b_frames = 0;
codec_ctx->pix_fmt= AV_PIX_FMT_YUV420P;
codec_ctx->bit_rate = 3000000;
如果通过name 获取到的编码器ID 是 AV_CODEC_ID_H264
可以通过下面这三个参数设定编码效率和模式 和编码时间有直接性关系
profile 设计到编码等级越老的可以选择 profile 格式越少
if (codec->id == AV_CODEC_ID_H264) {
// 相关的参数可以参考libx264.c的 AVOption options
// These three paramaters are affect video quality and encoder time
// ultrafast all encode time:2270ms
// medium all encode time:5815ms
// veryslow all encode time:19836ms
ret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);
if (ret != 0) {
printf("av_opt_set preset failed\n");
}
ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0); // 默认是high
if (ret != 0) {
printf("av_opt_set profile failed\n");
}
ret = av_opt_set(codec_ctx->priv_data, "tune", "zerolatency", 0); // 直播是才使用该设置
// ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); // 画质film
if (ret != 0) {
printf("av_opt_set tune failed\n");
}
}
设置多线程编码 设置多线程编码以后送往编码器的数据不会立即编码写入packet 如图 由于IO操作其实段时间解码 也许多线程并不一定快 直播一般不采用
// codec_ctx->thread_count = 64; // 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。
// codec_ctx->thread_type = FF_THREAD_FRAME; // 并 设置为FF_THREAD_FRAME
图一没有开启多线程
图二开启多线程
初始化packet 和frame
pkt = av_packet_alloc();
frame = av_frame_alloc();
frame->format = codec_ctx->pix_fmt;
frame->width = codec_ctx->width;
frame->height = codec_ctx->height;
ret = av_frame_get_buffer(frame, 0);
计算和初始化缓冲区
//Calculate the data of each frame Align memory by 1
int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,
frame->height, 1);
printf("frame_bytes %d\n", frame_bytes);
uint8_t *yuv_buf = (uint8_t *) malloc(frame_bytes);
开始编码
通过上文获取到的数据 从文件中循环 读取数据
size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);
然后将存储一帧图像到AvFrame对应的buffer 中
int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,
frame->format,
frame->width, frame->height, 1);
编码
还是一样的 送入编码器 循环取编码后的结果
static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
FILE *outfile)
{
int ret;
/* send the frame to the encoder */
if (frame)
printf("Send frame %3"PRId64"\n", frame->pts);
/* 通过查阅代码,使用x264进行编码时,具体缓存帧是在x264源码进行,
* 不会增加avframe对应buffer的reference*/
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0)
{
fprintf(stderr, "Error sending a frame for encoding\n");
return -1;
}
while (ret >= 0)
{
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return 0;
} else if (ret < 0) {
fprintf(stderr, "Error encoding audio frame\n");
return -1;
}
if(pkt->flags & AV_PKT_FLAG_KEY)
printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",
pkt->flags, pkt->pts, pkt->dts, pkt->size);
if(!pkt->flags)
printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",
pkt->flags, pkt->pts, pkt->dts, pkt->size);
fwrite(pkt->data, 1, pkt->size, outfile);
}
return 0;
}
源码地址:https://github.com/zycccer/aac_mp3_pcm