使用ffmpeg libav和libx264从图像创建视频?

使用ffmpeg libav和libx264从图像创建视频?,ffmpeg,h.264,video-encoding,libav,libx264,Ffmpeg,H.264,Video Encoding,Libav,Libx264,我正在尝试使用ffmpeg库从图像创建视频。这些图像的大小为1920x1080,应该使用.mkv容器使用H.264进行编码。 我遇到了各种各样的问题,以为我正在接近一个解决方案,但这一个我真的被卡住了。通过我使用的设置,我的视频的前X帧(大约40帧,取决于我使用的图像和视频的数量)不会被编码。avcodec_encode_video2在got_picture_ptr=0时不返回任何错误(返回值为0)。 结果是一个视频,实际上看起来和预期的一样,但第一秒是奇怪的跳跃 这就是我创建视频文件的方式:

我正在尝试使用ffmpeg库从图像创建视频。这些图像的大小为1920x1080,应该使用.mkv容器使用H.264进行编码。 我遇到了各种各样的问题,以为我正在接近一个解决方案,但这一个我真的被卡住了。通过我使用的设置,我的视频的前X帧(大约40帧,取决于我使用的图像和视频的数量)不会被编码。avcodec_encode_video2在got_picture_ptr=0时不返回任何错误(返回值为0)。 结果是一个视频,实际上看起来和预期的一样,但第一秒是奇怪的跳跃

这就是我创建视频文件的方式:

//m_codecContext是AVCodecContext类型的实例变量*
//m_formatCtx是AVFormatContext类型的实例变量*
//outputFileName是以.mkv结尾的有效文件名
AVOutputFormat*oformat=av_guess_格式(NULL,outputFileName,NULL);
如果(oformat==NULL)
{
oformat=av_guess_格式(“mpeg”,NULL,NULL);
}
//oformat->video_codec是AV_codec_ID_H264
AVCodec*codec=AVCodec\u find\u编码器(oformat->video\u codec);
m_codecContext=avcodec_alloc_context3(编解码器);
m_codecContext->codec_id=oformat->video_codec;
m_codecContext->codec_type=AVMEDIA_type_VIDEO;
m_codecContext->gop_size=30;
m_code上下文->比特率=宽度*高度*4
m_code上下文->宽度=宽度;
m_->context->height=高度;
m_codecContext->time_base=(AVRational){1,frameRate};
m_codecContext->max_b_frames=1;
m_codecContext->pix_fmt=AV_pix_fmt_YUV420P;
m_formatCtx=avformat_alloc_context();
m_formatCtx->oformat=oformat;
m_formatCtx->video_codec_id=oformat->video_codec;
snprintf(m_formatCtx->filename,sizeof(m_formatCtx->filename),“%s”,outputFileName);
AVStream*videoStream=avformat_new_流(m_formatCtx,编解码器);
如果(!视频流)
{
printf(“无法分配流\n”);
}
视频流->编解码器=m_编解码器上下文;
if(m_formatCtx->oformat->flags&AVFMT_GLOBALHEADER)
{
m_codecContext->flags |=编解码器_FLAG_全局_头;
}
avcodec_open2(m_codecContext,codec,NULL)<0);
avio_打开(&m_formatCtx->pb,outputFileName.tostString().c_str(),avio_标志_写入);
avformat_write_头(m_formatCtx,NULL);
以下是添加框架的方式:

void VideoCreator::writeImageToVideo(const QSharedPointer&img,int frameIndex)
{
AVFrame*frame=avcodec_alloc_frame();
/*alloc映像和输出缓冲区*/
int size=m_codecContext->width*m_codecContext->height;
int numBytes=avpicture\u get\u size(m\u codecContext->pix\u fmt,m\u codecContext->width,m\u codecContext->height);
uint8_t*exputf=(uint8_t*)malloc(numBytes);
uint8*picture\u buf=(uint8\u t*)av\u malloc(单位:字节);
int-ret=av_图像_填充_数组(帧->数据,帧->线条大小,图片大小,m_编码上下文->pix_fmt,m_编码上下文->宽度,m_编码上下文->高度,1);
帧->数据[0]=图片\u buf;
帧->数据[1]=帧->数据[0]+大小;
帧->数据[2]=帧->数据[1]+大小/4;
帧->线宽[0]=m\u编码上下文->宽度;
框架->线条尺寸[1]=m\u编码上下文->宽度/2;
框架->线条尺寸[2]=m\u上下文->宽度/2;
fflush(stdout);
对于(int y=0;yheight;y++)
{
对于(int x=0;xwidth;x++)
{
无符号字符b=img->bits()[(y*m_codecContext->width+x)*4+0];
无符号字符g=img->bits()[(y*m_codecContext->width+x)*4+1];
无符号字符r=img->bits()[(y*m_codecContext->width+x)*4+2];
无符号字符Y=(0.257*r)+(0.504*g)+(0.098*b)+16;
帧->数据[0][y*帧->线宽[0]+x]=y;
如果(y%2==0&&x%2==0)
{
无符号字符V=(0.439*r)-(0.368*g)-(0.071*b)+128;
无符号字符U=-(0.148*r)-(0.291*g)+(0.439*b)+128;
帧->数据[1][y/2*帧->线宽[1]+x/2]=U;
帧->数据[2][y/2*帧->线宽[2]+x/2]=V;
}
}
}
int pts=frameIndex;//(1.0/30.0)*90.0*frameIndex;
frame->pts=pts;//av_rescale_q(m_codecContext->coded_frame->pts,m_codecContext->time_base,formatCtx->streams[0]->time_base);//(1.0/30.0)*90.0*frameIndex;
int got_packet_ptr;
数据包;
av_初始_数据包(&数据包);
packet.data=exputf;
packet.size=numBytes;
packet.stream_index=formatCtx->streams[0]->index;
packet.flags |=AV_PKT_FLAG_密钥;
packet.pts=packet.dts=pts;
m_codecContext->coded_frame->pts=pts;
ret=avcodec_encode_video2(m_codecContext,&packet,frame,&got_packet_ptr);
如果(得到数据包\u ptr!=0)
{
m_codeContext->coded_frame->pts=pts;//设置时间戳
如果(m_codeContext->coded_frame->pts!=(0x80000000000000LL))
{
pts=av_rescale_q(m_codecContext->coded_frame->pts,m_codecContext->time_base,formatCtx->streams[0]->time_base);
}
packet.pts=pts;
如果(m_编码上下文->编码帧->关键帧)
{
packet.flags |=AV_PKT_FLAG_密钥;
}

std::coutLibav可能会延迟初始帧的处理。一个好的做法是在处理完所有帧后检查是否有任何延迟帧。具体操作如下:

int i=NUMBER_OF_FRAMES_PREVIOUSLY_ENCODED
for(; got_packet_ptr; i++)
   ret = avcodec_encode_video2(m_codecContext, &packet, NULL, &got_packet_ptr);
//Write the packets to a container after this.
关键是传递一个空指针来代替要编码的帧,并继续这样做,直到您得到的数据包为非空。有关代码示例,请参阅“获取延迟帧”下的部分

一个更简单的方法是将b帧的数量设置为0

m_codecContext->max_b_frames = 0;
让我知道这是否有效

此外,您根本没有使用libx264 API。您可以使用libx264 API对视频进行编码,它们的语法更简单、更清晰。此外,它还为您提供了对设置的更多控制和更高的性能

用于将视频流写入mkv容器
frame->width = m_codecContext->width;
frame->height = m_codecContext->height;
frame->format = m_codecContext->pix_fmt;