继续ffmpeg的学习之路。。。
看了雷博的YUV转H264代码,理解了一下大致的流程以及逻辑,然后迫不及待的手敲了一遍,然后编译运行,中间遇到了一些问题,便记录一下。
根据前两篇 YUV420转JPG, PCM转AAC,其实大致流程都是一致的,初始化相关结构体—>赋值一些必要的参数—>打开编码器—>读取原始数据—>编码—>写到输出文件中去….
一、一些概念
在赋值一些参数的时候,有些比较重要,单独列出来加以理解。。。
1.time_base
time_base的原型为
/**
* rational number numerator/denominator
*/
typedef struct AVRational{
int num; ///< numerator
int den; ///< denominator
} AVRational;
其中,num可以理解为分子,den可以理解为分母,实际上time_base的意思就是时间的刻度:
如(1,25),那么时间刻度就是1/25
(1,9000),那么时间刻度就是1/90000
在 AVStream AVCodecContext结构中都存在。
详细介绍可参考:https://blog.csdn.net/supermanwg/article/details/14521869
2.bit_rate
目标的码率,即采样码率;采样码率越大,视频大小越大,但是编码质量也会更好
3.gop_size
可以理解为I帧间隔
4.qmin qmax
最小最大量化系数
5.max_b_frames
两个非B帧之间最大的B帧数目。设置为0时,则不使用B帧,当然B帧越多,整个视频的大小就会越小,但是也可能影响其编码实时性。
6.编码参数
一些编码参数是通过AVDictionary的方式来设置的。主要是与编码速度相关的参数,设置如下:
av_dict_set(¶m, "preset", "slow", 0);
av_dict_set(¶m, "tune", "zerolatency", 0);
av_dict_set(¶m, "profile", "main", 0);
其中,preset可设置的选项为
ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow
按照前两篇 YUV420转JPG, PCM转AAC的代码,YUV420编码为H264/H265也是分为两个方法,第一种就是标准的编码,然后写到AVFormatContext输出文件中去,第二种则是将编码过来的AVPacket里面的数据直接写到文件中去。
另外,在编码H265的时候,如果提示找不到编码器之类的错误,需要安装libx265,参考:PC下安装FFMPEG
#define OUT_H264 1
#define OUT_H265 (!OUT_H264)
#define debug_msg(fmt, args ...) printf("--->sq[%s,%d] " fmt "\n\n", __FUNCTION__, __LINE__, ##args)
int flush_encoder(AVFormatContext * fmt_ctx, unsigned int stream_index)
{
int ret = 0;
int got_frame = 0;
AVPacket enc_pkt;
if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities & AV_CODEC_CAP_DELAY))
return 0;
while(1)
{
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame);
if (ret < 0)
break;
if (got_frame == 1)
{
printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
ret = av_write_frame(fmt_ctx, &enc_pkt);
if (ret < 0)
break;
av_free_packet(&enc_pkt);
}
else
{
ret = 0;
break;
}
}
return ret;
}
int YUV_2_H26X_1()
{
AVFormatContext* pFormatCtx = NULL;
AVStream* pStream = NULL;
AVOutputFormat* pFmt = NULL;
AVCodecContext* pCodecCtx = NULL;
AVCodec* pCodec = NULL;
AVPacket pkt;
AVFrame* pFrame = NULL;
FILE* inFileFd = fopen("../ds_480x272.yuv", "rb");
#if OUT_H265
char outFile[] = "./test.hevc";
#else
char outFile[] = "./test.h264";
#endif
int ret = 0;
int videoWidth = 480;
int videoHeight = 272;
int picSize = 0;
unsigned char* picBuff = NULL;
int ySize = 0;
int gotPic = 0;
av_register_all();
pFormatCtx = avformat_alloc_context();
pFmt = av_guess_format(NULL, outFile, NULL);
pFormatCtx->oformat = pFmt;
if (avio_open(&pFormatCtx->pb, outFile, AVIO_FLAG_READ_WRITE) < 0)
{
debug_msg("avio_open failure!\n");
ret = -1;
goto ERR_1;
}
if (NULL == (pStream = avformat_new_stream(pFormatCtx, NULL)))
{
debug_msg("avformat_new_stream failure!\n");
ret = -1;
goto ERR_1;
}
//pStream->time_base.num = 1;
//pStream->time_base.den = 25*3600;
pCodecCtx = pStream->codec;
pCodecCtx->codec_id = pFmt->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;// PIX_FMT_YUV420P;
pCodecCtx->width = videoWidth;
pCodecCtx->height = videoHeight;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
pCodecCtx->bit_rate = 400000;
pCodecCtx->gop_size = 10;
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
pCodecCtx->max_b_frames = 0;
ySize = pCodecCtx->width * pCodecCtx->height;
AVDictionary* pDict = NULL;
if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
{
av_dict_set(&pDict, "preset", "slow", 0);
av_dict_set(&pDict, "tune", "zerolatency", 0);
av_dict_set(&pDict, "profile", "main", 0);
}
else if (pCodecCtx->codec_id == AV_CODEC_ID_H265)
{
//av_dict_set(&pDict, "x265-params", "qp=20", 0);
//av_dict_set(&pDict, "preset", "ultrafast", 0);
// av_dict_set(&pDict, "tune", "zero-latency", 0);
av_dict_set(&pDict, "preset", "ultrafast", 0);
av_dict_set(&pDict, "tune", "zerolatency", 0);
av_dict_set(&pDict, "profile", "main", 0);
}
if ( NULL == (pCodec = avcodec_find_encoder(pCodecCtx->codec_id)))
{
debug_msg("avcodec_find_encoder error!\n");
ret = -1;
goto ERR_1;
}
if ((avcodec_open2(pCodecCtx, pCodec, &pDict)) < 0)
{
debug_msg("avcodec_find_encoder error!\n");
ret = -1;
goto ERR_1;
}
pFrame = av_frame_alloc();
picSize = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
picBuff = (unsigned char *)av_malloc(picSize);
avpicture_fill((AVPicture *)pFrame, picBuff, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
pFrame->format = pCodecCtx->pix_fmt;
avformat_write_header(pFormatCtx, NULL);
av_new_packet(&pkt,picSize);
debug_msg("----->ysize is %d, picsize is %d\n",ySize*3/2 ,picSize);
int i = 0;
pkt.stream_index = pStream->index;
debug_msg("index is %d\n",pkt.stream_index );
while(fread(picBuff, 1, ySize*3/2, inFileFd) > 0)
{
pFrame->data[0] = picBuff; // Y
pFrame->data[1] = picBuff+ ySize; // U
pFrame->data[2] = picBuff+ ySize*5/4; // V
pFrame->pts = i++;
if (avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &gotPic) < 0)
{
debug_msg("avcodec_encode_video2 error!\n");
ret = -1;
goto ERR_2;
}
if (gotPic == 1)
{
av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
}
}
if (flush_encoder(pFormatCtx,0) < 0)
{
debug_msg("flush_encoder error!\n");
ret = -1;
goto ERR_2;
}
av_write_trailer(pFormatCtx);
ERR_2:
avcodec_close(pCodecCtx);
av_free(pFrame);
if (picBuff != NULL)
av_free(picBuff);
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
av_dict_free(&pDict);
ERR_1:
fclose(inFileFd);
return ret;
}
#define OUT_H264 1
#define OUT_H265 (!OUT_H264)
int YUV_2_H26X_2()
{
AVCodecContext* pCodecCtx = NULL;
AVCodec * pCodec = NULL;
AVPacket pkt;
AVFrame* pFrame = NULL;
//int cnt = 0;
int got_frame = 0;
int ret = -1;
FILE* out_file = NULL;
FILE* in_file = NULL;
int videoWidth = 480;
int videoHeight = 272;
int picSize = 0;
unsigned char* picBuff = NULL;
int gotPic = 0;
int ySize = 0;
in_file = fopen("../ds_480x272.yuv", "rb");
if (NULL == in_file)
{
printf("open in file error!\n");
ret = -1;
return ret;
}
#if OUT_H265
out_file = fopen("test.hevc", "wb");
#else
out_file = fopen("test.h264", "wb");
#endif
if (NULL == out_file)
{
printf("open out file error!\n");
goto ERR_3;
}
avcodec_register_all();
pCodecCtx = avcodec_alloc_context3(NULL);
if (NULL == pCodecCtx)
{
printf("alloc AVCodecContext failure!\n");
ret = -1;
goto ERR_2;
}
#if OUT_H265
pCodecCtx->codec_id = AV_CODEC_ID_H265;
#else
pCodecCtx->codec_id = AV_CODEC_ID_H264;
#endif
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;// PIX_FMT_YUV420P;
pCodecCtx->width = videoWidth;
pCodecCtx->height = videoHeight;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
pCodecCtx->bit_rate = 400000;
pCodecCtx->gop_size = 10;
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
pCodecCtx->max_b_frames = 0;
ySize = pCodecCtx->width * pCodecCtx->height;
AVDictionary* pDict = NULL;
if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
{
av_dict_set(&pDict, "preset", "slow", 0);
av_dict_set(&pDict, "tune", "zerolatency", 0);
av_dict_set(&pDict, "profile", "main", 0);
}
else if (pCodecCtx->codec_id == AV_CODEC_ID_H265)
{
//av_dict_set(&pDict, "x265-params", "qp=20", 0);
//av_dict_set(&pDict, "preset", "ultrafast", 0);
// av_dict_set(&pDict, "tune", "zero-latency", 0);
av_dict_set(&pDict, "preset", "ultrafast", 0);
av_dict_set(&pDict, "tune", "zerolatency", 0);
av_dict_set(&pDict, "profile", "main", 0);
}
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (NULL == pCodec)
{
printf("can not find encoder!\n");
ret = -1;
goto ERR_1;
}
if (avcodec_open2(pCodecCtx, pCodec, &pDict) < 0)
{
printf("open encoder error!\n");
ret = -1;
goto ERR_1;
}
pFrame = av_frame_alloc();
picSize = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
picBuff = (unsigned char *)av_malloc(picSize);
avpicture_fill((AVPicture *)pFrame, picBuff, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
pFrame->format = pCodecCtx->pix_fmt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
memset(picBuff, 0, picSize);
int i =0;
while((fread(picBuff, 1, picSize, in_file)) > 0)
{
pFrame->data[0] = picBuff; // Y
pFrame->data[1] = picBuff+ ySize; // U
pFrame->data[2] = picBuff+ ySize*5/4; // V
pFrame->pts = i++;
if (avcodec_encode_audio2(pCodecCtx, &pkt, pFrame, &got_frame) >= 0)
{
if (got_frame)
{
fwrite(pkt.data, 1, pkt.size, out_file);
av_free_packet(&pkt);
}
else
{
continue;
}
}
else
{
printf("encode audio error!\n");
ret = -1;
goto ERR_1;
}
memset(picBuff,0, picSize);
}
//fresh
pkt.data = NULL;
pkt.size = 0;
while(1)
{
if (avcodec_encode_audio2(pCodecCtx, &pkt, NULL, &got_frame) >= 0)
{
if (got_frame)
{
fwrite(pkt.data, 1, pkt.size, out_file);
av_free_packet(&pkt);
}
else
{
break;
}
}
else
{
printf("fresh encode audio error!\n");
ret = -1;
goto ERR_1;
}
}
ERR_1:
av_freep(&pFrame->data[0]);
//av_freep(frame_buff);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
ERR_2:
fclose(out_file);
ERR_3:
fclose(in_file);
return ret;
}