求助:ffmpeg编码YUV420p至H264的问题

时间:2021-12-03 13:30:29
最近编写一个采集PAL摄像头视频数据,并将采集的数据编码为h264,h264视频已经编码成功,但是文件太大,一秒钟就有2MB的数据,感觉保存的264数据与YUV数据比较,大小基本没有变化,不知道哪里设置的有问题,恳请各位大侠指点;

下面是我初始化ffmpeg编码的代码:

void CVideoCompressTestDlg::OnSavefile() 
{
// TODO: Add your command handler code here

CString path="C:\\123.h264";
char * ppath =(LPSTR)(LPCTSTR)path;
  if(NULL == (pSaveVideoFile = fopen(ppath, "wb"))) //创建文件
{
TRACE("UYVYtoYUV420encode fopen error!\n");
  }

ULONG fps = 0;
if ((fps<=0)||(fps>60))
{
fps = 50;
}
//压缩视频数据
/* avcodec init */
i_codec_id = CODEC_ID_H264;
const char* out_file = "src01.h264";
av_register_all();  //注册所有可用的文件格式和编解码库
pCodec = avcodec_find_encoder((CodecID)i_codec_id);//调用H264编码器
if (!pCodec)
{
return;
}
pCodecCtx = avcodec_alloc_context();
if (!pCodecCtx)
{
return;
}
/* avcodec frame alloc */
pFrame_420p = avcodec_alloc_frame();//为编码帧分配内存
if(!pFrame_420p)
{
return;
}

/* Init Codec Parameters */
pCodecCtx->codec_id = i_codec_id;
pCodecCtx->codec_type = CODEC_TYPE_VIDEO;   
pCodecCtx->width = dwWidth; 
pCodecCtx->height =dwHeight;
pCodecCtx->time_base.num = 1;  

pCodecCtx->time_base.den = fps*7/10; 

pCodecCtx->dct_algo = 0; 
pCodecCtx->me_pre_cmp = 2; 

if(CODEC_ID_H264 == i_codec_id)
{
pCodecCtx->bit_rate =400000 /*3*1024*1000*/;  
pCodecCtx->cqp = 21; 
pCodecCtx->me_method = 7;  //For CODEC_ID_H264
pCodecCtx->qmin = 3;  //x264 dislike(2)
pCodecCtx->qmax = 30;  //x264 dislike(31)
}
else
{
return;
}

pCodecCtx->gop_size = 250/*250*/; //x264 dislike(12) --------keyframe interval 关键帧间隔
pCodecCtx->max_qdiff = 3; 
pCodecCtx->qcompress = (float)0.6; //x264 dislike(0.5)
pCodecCtx->qblur = (float)0.5; 
pCodecCtx->nsse_weight = 8; 
pCodecCtx->i_quant_factor = (float)0.8; 
pCodecCtx->b_quant_factor = (float)1.30; //x264 dislike(value - 1.25) < 0.01)
pCodecCtx->b_quant_offset = (float)1.30; //x264 dislike(value - 1.25) < 0.01)
pCodecCtx->pix_fmt = PIX_FMT_YUV420P;//当前YUV420 

/* Obtain scale context */
p_sws_context = sws_getContext(pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P/*PIX_FMT_UYVY422*/,  //Src
pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P,  //Dst
SWS_BILINEAR, NULL, NULL, NULL);
if(!p_sws_context)
{
//printf("sws_getContext error, Exit\n", i_ret_fread, i_actual_stream_size);
return;
}

/* 打开 avcodec */
if(avcodec_open(pCodecCtx, pCodec) < 0)
{
return;

///////////////////////////////////////////////////////

p_video_buf = (unsigned char *)av_malloc(sizeof(UCHAR)*pCodecCtx->width * pCodecCtx->height * 3 /2); 

pFrame_420p->data[0] = p_video_buf;
pFrame_420p->data[1] = p_video_buf + pCodecCtx->width * pCodecCtx->height;
pFrame_420p->data[2] = pFrame_420p->data[1] + (pCodecCtx->width * pCodecCtx->height / 4);
pFrame_420p->linesize[0] = pCodecCtx->width;
pFrame_420p->linesize[1] = pCodecCtx->width / 2;
pFrame_420p->linesize[2] = pCodecCtx->width / 2;



i_stream_buf_size = sizeof(UCHAR)*pCodecCtx->width * pCodecCtx->height * 3 /2;
p_stream_buf = (unsigned char *)av_malloc(i_stream_buf_size);

savevideoflag = 1;

////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

这里是我实时接收到摄像头的每一帧数据进行压缩编码,每秒采集25帧图像:
void CVideoCompressTestDlg::PlayVideo()
{

  while (playflag)
  {
//  i++;
JMVSDK_GetVideo(pYUV420, CFormat_YUV420P, &ts, iChannelIndex);//采集卡采集到数据流
// fwrite(pYUV420, 1, dwWidth * dwHeight * 3 / 2, yuvfile);
pFrame_420p=avcodec_alloc_frame();//申请存储空间
// pCodecCtx = avcodec_alloc_context();
// avcodec_get_frame_defaults(pFrame_420p);

YUV420PToAVFrame(pYUV420,nLen,dwWidth,dwHeight, pFrame_420p);//将数据码流强制转换成YUV420P格式

pDDraw.DrawAllYUV((AVPicture*)pFrame_420p,CFormat_YUV420P,videorect,TRUE);   //DDRAW上输出显示
if (savevideoflag)
{
/* encode one frame */ //压缩视频数据
int i_actual_stream_size = 0;
//  int i_ret = sws_scale((SwsContext *)p_sws_context, pFrame_420p->data, pFrame_420p->linesize, 0, pCodecCtx->height,
//  pFrame_420p->data, pFrame_420p->linesize);  //将YUV422转YUV420P

i_actual_stream_size = avcodec_encode_video(pCodecCtx, p_stream_buf, i_stream_buf_size, pFrame_420p);
if(i_actual_stream_size < 0)
{
TRACE("Encode frame %d error, exit\n", 5555);
//break;
}
if (NULL != pSaveVideoFile)  //将视频数据写入H264文件
{
if (0 == fwrite(p_stream_buf,sizeof(unsigned char), i_actual_stream_size, pSaveVideoFile))
{
TRACE("UYVYtoYUV420encode fwrite error!\n");
}

fflush(pSaveVideoFile);
}
else
{
TRACE("fopen failed!!!\n");
}
}
}

}
其他的编码部分就没有了,哪位大侠帮忙看下,多谢了,分全压上了!

12 个解决方案

#1


没有人知道吗?

#2


没有变化?你怎么推出来的没有变化?
720*576*1.5*25

#3


i_actual_stream_size

#4


avcodec_encode_video每次的返回值是多少? printf出来看看?

#5


我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样

#6


我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样

#7


引用 6 楼 waterml930 的回复:
我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


应该是哪里写错了。。这个我也做过。弄完后要比较小的,你可以试试把这个文件用相应软件转成H264(不是你的代码)然后再来对比下,就可以知道你的有没有转错了

#8


引用 6 楼 waterml930 的回复:
我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


文件大小多大,YUV420存10分钟应该是9331200000字节,约等于9个GB,你存了这么大的文件?

#9


引用 8 楼 falloutmx 的回复:
Quote: 引用 6 楼 waterml930 的回复:

我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


文件大小多大,YUV420存10分钟应该是9331200000字节,约等于9个GB,你存了这么大的文件?


我确实保存了这么大的文件做的测试,就是不知道哪里参数设置错了,有类似的例子吗?可以发我邮箱 hushuihao_ml@163。com

#10


引用 7 楼 Evankaka 的回复:
Quote: 引用 6 楼 waterml930 的回复:

我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


应该是哪里写错了。。这个我也做过。弄完后要比较小的,你可以试试把这个文件用相应软件转成H264(不是你的代码)然后再来对比下,就可以知道你的有没有转错了



你有以前的例子吗?能发我邮箱hushuihao_ml@163.com看看吗?谢谢!

#11


引用 4 楼 mstlq 的回复:
avcodec_encode_video每次的返回值是多少? printf出来看看?



返回值在287至1868之间,基本都是随机的,没什么规律都是大于0的,小于0就出错了

#12


引用 11 楼 waterml930 的回复:
Quote: 引用 4 楼 mstlq 的回复:

avcodec_encode_video每次的返回值是多少? printf出来看看?



返回值在287至1868之间,基本都是随机的,没什么规律都是大于0的,小于0就出错了

把它们加起来,十分钟的数据能到能到9g?
假设你30fps好了,最大也就是1868*30*60*10=,大概32m
楼主检查一下自己写文件的相关操作吧

#1


没有人知道吗?

#2


没有变化?你怎么推出来的没有变化?
720*576*1.5*25

#3


i_actual_stream_size

#4


avcodec_encode_video每次的返回值是多少? printf出来看看?

#5


我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样

#6


我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样

#7


引用 6 楼 waterml930 的回复:
我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


应该是哪里写错了。。这个我也做过。弄完后要比较小的,你可以试试把这个文件用相应软件转成H264(不是你的代码)然后再来对比下,就可以知道你的有没有转错了

#8


引用 6 楼 waterml930 的回复:
我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


文件大小多大,YUV420存10分钟应该是9331200000字节,约等于9个GB,你存了这么大的文件?

#9


引用 8 楼 falloutmx 的回复:
Quote: 引用 6 楼 waterml930 的回复:

我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


文件大小多大,YUV420存10分钟应该是9331200000字节,约等于9个GB,你存了这么大的文件?


我确实保存了这么大的文件做的测试,就是不知道哪里参数设置错了,有类似的例子吗?可以发我邮箱 hushuihao_ml@163。com

#10


引用 7 楼 Evankaka 的回复:
Quote: 引用 6 楼 waterml930 的回复:

我把得到的YUV420P保存文件,记录10分钟,然后再264文件保存成文件,也是10分钟,文件大小基本一样


应该是哪里写错了。。这个我也做过。弄完后要比较小的,你可以试试把这个文件用相应软件转成H264(不是你的代码)然后再来对比下,就可以知道你的有没有转错了



你有以前的例子吗?能发我邮箱hushuihao_ml@163.com看看吗?谢谢!

#11


引用 4 楼 mstlq 的回复:
avcodec_encode_video每次的返回值是多少? printf出来看看?



返回值在287至1868之间,基本都是随机的,没什么规律都是大于0的,小于0就出错了

#12


引用 11 楼 waterml930 的回复:
Quote: 引用 4 楼 mstlq 的回复:

avcodec_encode_video每次的返回值是多少? printf出来看看?



返回值在287至1868之间,基本都是随机的,没什么规律都是大于0的,小于0就出错了

把它们加起来,十分钟的数据能到能到9g?
假设你30fps好了,最大也就是1868*30*60*10=,大概32m
楼主检查一下自己写文件的相关操作吧