我正在尝试使用FFMpeg中的libavcodec库进行解码,然后重新编码h264视频。
我的解码部分正常工作(可以正常使用SDL窗口),但是当我尝试对帧进行重新编码时,在重新编码的视频样本中得到了不良数据。
这是我的编码逻辑的简化代码段。
EncodeResponse H264Codec::EncodeFrame(AVFrame* pFrame, StreamCodecContainer* pStreamCodecContainer, AVPacket* pPacket)
{
int result = 0;
result = avcodec_send_frame(pStreamCodecContainer->pEncodingCodecContext, pFrame);
if(result < 0)
{
return EncodeResponse::Fail;
}
while (result >= 0)
{
result = avcodec_receive_packet(pStreamCodecContainer->pEncodingCodecContext, pPacket);
// If the encoder needs more frames to create a packed then return and wait for
// method to be called again upon a new frame been present.
// Else check if we have failed to encode for some reason.
// Else a packet has successfully been returned, then write it to the file.
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
{
// Higher level logic, dedcodes next frame from source
// video then calls this method again.
return EncodeResponse::SendNextFrame;
}
else if (result < 0)
{
return EncodeResponse::Fail;
}
else
{
// Prepare packet for muxing.
if (pStreamCodecContainer->codecType == AVMEDIA_TYPE_VIDEO)
{
av_packet_rescale_ts(m_pPacket, pStreamCodecContainer->pEncodingCodecContext->time_base,
m_pDecodingFormatContext->streams[pStreamCodecContainer->streamIndex]->time_base);
}
m_pPacket->stream_index = pStreamCodecContainer->streamIndex;
int result = av_interleaved_write_frame(m_pEncodingFormatContext, m_pPacket);
av_packet_unref(m_pPacket);
}
}
return EncodeResponse::EncoderEndOfFile;
}
我注意到的奇怪行为是,在我从avcodec_receive_packet获取第一个数据包之前,我必须向avcodec_send_frame发送50多个帧。
我构建了FFMpeg的调试版本,并逐步进入代码,我注意到avcodec_receive_packet返回了AVERROR(EAGAIN),因为x264encoder :: encode编码器.c中存在以下内容
if( h->frames.i_input <= h->frames.i_delay + 1 - h->i_thread_frames )
{
/* Nothing yet to encode, waiting for filling of buffers */
pic_out->i_type = X264_TYPE_AUTO;
return 0;
}
由于某种原因,我的代码上下文(h)从来没有任何框架。我花了很长时间尝试调试ffmpeg并确定我做错了什么。但是已经达到了我的视频编解码器知识的极限(很少)。
我正在用没有音频的视频进行测试,以减少复杂性。
我已经创建了应用程序的简化版本,并提供了一个自包含的项目(具有ffmpeg和SDL构建的依赖项)。希望这可以帮助任何愿意帮助我的人:)。
项目链接 https://github.com/maxhap/video-codec
研究了编码器初始化后,我发现必须在调用avcodec_open2之前设置编解码器AV_CODEC_FLAG_GLOBAL_HEADER
pStreamCodecContainer->pEncodingCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
此更改导致重新编码的moov框看起来更加清晰(使用MP4Box.js对其进行解析)。但是,视频仍无法正确播放,在VLC中播放时,输出视频在开始时会出现灰色帧,而在其他播放器中将无法播放。
此后,我尝试通过示例代码(而不是使用解码编解码器参数)创建编码上下文。这导致修复错误/数据或编码问题。但是,我的DTS时间越来越多了
这是我的新编解码器初始化
if (pStreamCodecContainer->codecType == AVMEDIA_TYPE_VIDEO)
{
pStreamCodecContainer->pEncodingCodecContext->height = pStreamCodecContainer->pDecodingCodecContext->height;
pStreamCodecContainer->pEncodingCodecContext->width = pStreamCodecContainer->pDecodingCodecContext->width;
pStreamCodecContainer->pEncodingCodecContext->sample_aspect_ratio = pStreamCodecContainer->pDecodingCodecContext->sample_aspect_ratio;
/* take first format from list of supported formats */
if (pStreamCodecContainer->pEncodingCodec->pix_fmts)
{
pStreamCodecContainer->pEncodingCodecContext->pix_fmt = pStreamCodecContainer->pEncodingCodec->pix_fmts[0];
}
else
{
pStreamCodecContainer->pEncodingCodecContext->pix_fmt = pStreamCodecContainer->pDecodingCodecContext->pix_fmt;
}
/* video time_base can be set to whatever is handy and supported by encoder */
pStreamCodecContainer->pEncodingCodecContext->time_base = av_inv_q(pStreamCodecContainer->pDecodingCodecContext->framerate);
pStreamCodecContainer->pEncodingCodecContext->sample_aspect_ratio = pStreamCodecContainer->pDecodingCodecContext->sample_aspect_ratio;
}
else
{
pStreamCodecContainer->pEncodingCodecContext->channel_layout = pStreamCodecContainer->pDecodingCodecContext->channel_layout;
pStreamCodecContainer->pEncodingCodecContext->channels =
av_get_channel_layout_nb_channels(pStreamCodecContainer->pEncodingCodecContext->channel_layout);
/* take first format from list of supported formats */
pStreamCodecContainer->pEncodingCodecContext->sample_fmt = pStreamCodecContainer->pEncodingCodec->sample_fmts[0];
pStreamCodecContainer->pEncodingCodecContext->time_base = AVRational{ 1, pStreamCodecContainer->pEncodingCodecContext->sample_rate };
}
有什么想法为什么我的DTS时间没有正确缩放?
我设法直接从解码流中使用time_base值来修复DTS标度。
所以
pStreamCodecContainer->pEncodingCodecContext->time_base = m_pDecodingFormatContext->streams[pStreamCodecContainer->streamIndex]->time_base
代替
pStreamCodecContainer->pEncodingCodecContext->time_base = av_inv_q(pStreamCodecContainer->pDecodingCodecContext->framerate);
我将根据我的所有发现创建答案。
答案 0 :(得分:0)
要解决moov框损坏的最初问题,我必须在调用avcodec_open2之前向编码编解码器上下文中添加AV_CODEC_FLAG_GLOBAL_HEADER
标志。
encCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
下一个问题是编码包中的DTS值缩放不正确,这导致最终mp4持续时间长达数百小时。为了解决这个问题,我不得不将编码编解码器上下文时基更改为解码上下文流时基。这与avcodec转码示例中建议的使用av_inv_q(framerate)
不同。
encCodecContext->time_base = decCodecFormatContext->streams[streamIndex]->time_base;