将yuv420p编码成h264最小示例
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
int main(int argc, char *argv[])
{
int ret = -1;
int frame_index = 0;
AVFormatContext *fmt_ctx = NULL;
const AVOutputFormat *ofmt = NULL;
AVStream *stream = NULL;
const AVCodec *codec = NULL;
AVCodecContext *codec_ctx = NULL;
AVFrame *frame = NULL;
AVPacket *packet = NULL;
int width = 1280; // 输入YUV文件的宽度
int height = 536; // 输入YUV文件的高度
enum AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P; // 输入YUV文件的像素格式
int frame_rate = 60; // 输出视频帧率
char in_file[256];
char *in_filename = "frame_out/%dx%d_%d.yuv420p";
char *out_filename = "xxx.h264";
// 分配输出格式上下文
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, out_filename);
if (!fmt_ctx)
{
printf("avformat_alloc_output_context2 failed\n");
goto end;
}
ofmt = fmt_ctx->oformat;
// 创建新的视频流
stream = avformat_new_stream(fmt_ctx, NULL);
if (!stream)
{
printf("avformat_new_stream failed\n");
goto end;
}
// 查找编码器
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec)
{
printf("Codec not found\n");
goto end;
}
printf("codec name: %s\n", codec->name);
// 分配编码器上下文
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx)
{
printf("avcodec_alloc_context3 failed\n");
goto end;
}
/* 设置编码器参数 */
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
codec_ctx->width = width;
codec_ctx->height = height;
codec_ctx->time_base = (AVRational){1, frame_rate}; // 设置时间基
// 打开编码器
if (avcodec_open2(codec_ctx, NULL, NULL) < 0)
{
printf("avcodec_open2 failed\n");
goto end;
}
// 将编码器参数复制到流
ret = avcodec_parameters_from_context(stream->codecpar, codec_ctx);
if (ret < 0)
{
printf("avcodec_parameters_from_context failed\n");
goto end;
}
frame = av_frame_alloc();
packet = av_packet_alloc();
if (!frame || !packet)
{
printf("allocate frame or packet failed\n");
goto end;
}
// 设置帧参数
frame->format = codec_ctx->pix_fmt;
frame->width = codec_ctx->width;
frame->height = codec_ctx->height;
// 分配帧数据缓冲区
ret = av_frame_get_buffer(frame, 0);
if (ret < 0)
{
printf("av_frame_get_buffer failed\n");
goto end;
}
// 打开输出文件
if (!(ofmt->flags & AVFMT_NOFILE))
{
ret = avio_open(&fmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("open output file failed\n");
goto end;
}
}
// 写文件头
ret = avformat_write_header(fmt_ctx, NULL);
if (ret < 0)
{
printf("avformat_write_header failed\n");
goto end;
}
// 编码循环
while (1)
{
// 从文件中读取一帧yuv420p图片
snprintf(in_file, sizeof(in_file), in_filename, width, height, frame_index++);
printf("in_file: %s\n", in_file);
FILE * f = fopen(in_file, "rb");
if (f == NULL) {
perror("fopen failed");
break;
}
fread(frame->data[0], 1, width*height, f);
fread(frame->data[1], 1, width*height/4, f);
fread(frame->data[2], 1, width*height/4, f);
fclose(f);
frame->pts = frame_index;
frame_index++;
// 发送帧到编码器
ret = avcodec_send_frame(codec_ctx, frame);
if (ret < 0)
{
printf("avcodec_send_frame error (errmsg '%s')\n", av_err2str(ret));
goto end;
}
// 接收编码后的数据包
while (ret >= 0)
{
ret = avcodec_receive_packet(codec_ctx, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
printf("avcodec_receive_packet error (errmsg '%s')\n", av_err2str(ret));
goto end;
}
// 写数据包到输出文件
ret = av_interleaved_write_frame(fmt_ctx, packet);
if (ret < 0)
{
printf("av_interleaved_write_frame failed\n");
av_packet_unref(packet);
goto end;
}
av_packet_unref(packet);
}
}
// 发送NULL到编码器,刷新编码器内部缓冲区
ret = avcodec_send_frame(codec_ctx, NULL);
while (ret >= 0)
{
ret = avcodec_receive_packet(codec_ctx, packet);
if (ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
printf("avcodec_receive_packet error (errmsg '%s')\n", av_err2str(ret));
goto end;
}
ret = av_interleaved_write_frame(fmt_ctx, packet);
if (ret < 0)
{
printf("av_interleaved_write_frame failed\n");
av_packet_unref(packet);
goto end;
}
av_packet_unref(packet);
}
// 写文件尾
av_write_trailer(fmt_ctx);
end:
av_frame_free(&frame);
av_packet_free(&packet);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx);
return 0;
}