纯属学习记录,有问题请评论指点
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
static int ptsIndex = 0;
void encode(AVFormatContext* outFormatCtx,AVFrame* frame,AVPacket* packet,AVCodecContext* codecCtx,FILE* outFile);
int main(int argc,char *argv[])
{
int ret = 0;
AVCodec *codec = NULL;
AVCodecContext* codecCtx = NULL;
AVFormatContext* outFormatCtx = NULL;
AVStream* outStream = NULL;
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
codecCtx = avcodec_alloc_context3(codec);
if (!codecCtx) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* 设置码率为400000b/s,大概为390kb/s */
codecCtx->bit_rate = 400000;
/* 分辨率必须为偶数 */
codecCtx->width = 176;
codecCtx->height = 144;
/* AVRational表示一个分数,前面是分子,后面是分母
time_base为时间基,用来计算时间,例如PTS*timebase,计算显示时间*/
codecCtx->time_base = (AVRational){1,25};
/* 帧率,表示一秒多少帧,一般与time_base互为倒数 */
codecCtx->framerate = (AVRational){25,1};
/* 设置gop为12,表示两个关键帧的距离 */
codecCtx->gop_size = 12;
/* 表示两个非B帧之间允许存在最大数目的B帧 */
codecCtx->max_b_frames = 0;
/* Pixel format 像素格式 */
codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
/* 特定的文件大小或恒定的比特率,使用较慢的预设获得更好的质量。同样,对于
恒定质量的编码,您只需选择较慢的预设即可节省比特率 */
if(codec->id == AV_CODEC_ID_H264)
av_opt_set(codecCtx->priv_data,"preset","slow",0);
/* 初始化AVCodecContext,各种分配内存和检查,假如是H264的,编码器就会初始化为libx264 */
ret = avcodec_open2(codecCtx,codec,NULL);
if (ret < 0) {
fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
exit(1);
}
AVPacket* packet = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
frame->format = codecCtx->pix_fmt;
frame->height = codecCtx->height;
frame->width = codecCtx->width;
/* 一开始第二个参数align(对齐)也和官方demo一样设为32,结果输出的画面是花屏,
大家可以参考下这篇博客:https://blog.csdn.net/grafx/article/details/29185147 */
ret = av_frame_get_buffer(frame,1);
if( ret != 0)
{
printf("frame get buffer fail\n");
goto end;
}
const char* inputFileName = "source/akiyo_qcif.yuv";
const char* outFileName = "source/akiyo_qcif.h264";
/* 分配内存,根据格式名(第三个参数)或者文件名(第四个参数)获取输出格式 */
avformat_alloc_output_context2(&outFormatCtx,NULL,NULL,outFileName);
if(outFormatCtx == NULL)
{
fprintf(stderr," alloc output contex fail,exit!!\n");
exit(1);
}
outStream = avformat_new_stream(outFormatCtx,codec);
#if 0 /* 老版函数,试过也是可以的,不过会打印警告 */
if (avcodec_copy_context(outStream->codec, codecCtx) < 0) {
printf("Failed to copy context from input to output stream codec context\n");
return -1;
}
outStream->codec->codec_tag = 0;
if (outFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
#else
if(avcodec_parameters_from_context(outStream->codecpar,codecCtx) < 0)
{
fprintf(stderr,"copy parameters fail!\n");
goto end;
}
#endif
/* AVFMT_NOFILE跟是否是一个真实的文件无关 */
if(!(outFormatCtx->oformat->flags & AVFMT_NOFILE))
{
if(avio_open2(&outFormatCtx->pb,"source/akiyo_qcif.h264",AVIO_FLAG_WRITE,NULL,NULL))
{
printf("avio open fail\n");
goto end;
}
}
FILE* inputFile = fopen(inputFileName,"rb");
FILE* outFile = fopen(outFileName,"wb+");
/* 针对不同的编码格式,写不同的文件头*/
if(avformat_write_header(outFormatCtx,NULL) < 0)
{
fprintf(stderr, "Error occurred when opening output file: %s\n",av_err2str(ret));
goto end;
}
while (!feof(inputFile))
{
/* 确保frame是可写的,例如编码器要缓存此帧,下面函数就会分配新的缓冲区,并复制参数 */
ret = av_frame_make_writable(frame);
if(ret < 0)
{
printf("%s %d ret:%d\n",__FUNCTION__,__LINE__,ret);
goto end;
}
fread(frame->data[0],1,frame->width*frame->height,inputFile); //Y数据
fread(frame->data[1],1,frame->width*frame->height/4,inputFile); //U数据
fread(frame->data[2],1,frame->width*frame->height/4,inputFile); //V数据
encode(outFormatCtx,frame,packet,codecCtx,outFile);
}
/* 发送一个空帧,可以防止编码器中数据未处理完 */
encode(outFormatCtx,NULL,packet,codecCtx,outFile);
/* 针对不同的编码格式,写不同的文件尾*/
if(av_write_trailer(outFormatCtx) != 0)
printf("write file trail fail\n");
end:
avcodec_free_context(&codecCtx);
avformat_free_context(outFormatCtx);
if(inputFile)
fclose(inputFile);
if(outFile)
fclose(outFile);
av_frame_free(&frame);
av_packet_free(&packet);
return 0;
}
void encode(AVFormatContext* outFormatCtx,AVFrame* frame,AVPacket* packet,AVCodecContext* codecCtx,FILE* outFile)
{
int ret = 0;
if(frame)
frame->pts = ptsIndex++;
ret = avcodec_send_frame(codecCtx,frame);
if (ret < 0)
{
fprintf(stderr, "Error sending the frame to the encoder\n");
exit(1);
}
while(ret >= 0)
{
ret = avcodec_receive_packet(codecCtx,packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
exit(1);
}
packet->stream_index = 0;
#if 1 /* 两种写数据的方式都是可以的 */
if(av_interleaved_write_frame(outFormatCtx,packet) != 0)
{
printf("write frame fail\n");
continue;
}
#else
fwrite(packet->data,1,packet->size,outFile);
#endif
av_packet_unref(packet);
}
}