提问人:Ben 提问时间:11/1/2023 最后编辑:Ben 更新时间:11/1/2023 访问量:31
为什么最后一帧没有显示在 libav 生成的 MP4 中?
Why is the last frame not showing in an MP4 generated by libav?
问:
注意:我这里有一个问题的工作示例。
我正在使用 libav/ffmpeg API 生成带有 h4 编解码器的 MP264。在我的特定情况下,我正在生成最大数量为 2 个“B”帧的文件。我能够生成具有正确帧数的 Mp4,以便单个单独的“B”帧是正在写入的最后一帧。发生这种情况时,编码器会将该帧的数据包设置为丢弃(我已经用ffprobe验证了这一点)。最终结果是,某些播放器(例如,将 MP4 放入 Edge 或 Chrome 时)将仅显示 n-1 帧(忽略丢弃的数据包)。其他播放器,例如 VLC,将播放完整的 n 帧(不忽略丢弃的数据包)。因此,结果是不一致的。
ffmpeg.exe本身似乎没有这个问题。相反,它会将唯一的“B”帧设置为“P”帧。这意味着无论使用什么播放器,文件都将播放相同的内容。
问题是:我不知道如何使用 SDK 模仿 ffmpeg 的行为,因此无论播放器如何,最后一帧都会播放。据我所知,我通过刷新编码器缓冲区来正确关闭文件。我一定在某个地方做错了什么。
我提供了指向上面完整源代码的链接,但在高层次上,我正在初始化编解码器上下文和流,如下所示:
newStream->codecpar->codec_id = AV_CODEC_ID_H264;
newStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
newStream->codecpar->width = Width;
newStream->codecpar->height = Height;
newStream->codecpar->format = AV_PIX_FMT_YUV420P;
newStream->time_base = { 1, 75 };
avcodec_parameters_to_context(codecContext, newStream->codecpar);
codecContext->time_base = { 1, 75 };
codecContext->gop_size = 30;
然后,我坐在一个循环中,使用 OpenCV 生成帧(每个帧上都绘制了帧号):
auto matrix = cv::Mat(Height, Width, CV_8UC3, cv::Scalar(0, 0, 0));
std::stringstream ss;
ss << f;
cv::putText(matrix, ss.str().c_str(), textOrg, fontFace, fontScale, cv::Scalar(255, 255, 255), thickness, 8);
然后我像这样写出帧(如果需要更多数据,则循环):
if ((ret = avcodec_send_frame(codecContext, frame)) == 0) {
ret = avcodec_receive_packet(codecContext, &pkt);
if (ret == AVERROR(EAGAIN))
{
continue;
}
else
{
av_interleaved_write_frame(pFormat, &pkt);
}
av_packet_unref(&pkt);
}
最后,我像这样在最后刷新文件:
if ((ret = avcodec_send_frame(codecContext, NULL)) == 0)
{
for (;;)
{
if ((ret = avcodec_receive_packet(codecContext, &pkt)) == AVERROR_EOF)
{
break;
}
else
{
ret = av_interleaved_write_frame(pFormat, &pkt);
av_packet_unref(&pkt);
}
}
av_write_trailer(pFormat);
avio_close(pFormat->pb);
}
然而,当我在 Chrome 中玩游戏时,播放器在第 6758 帧结束,
在 VLC 中,播放器在第 6759 帧结束。
我做错了什么?
答: 暂无答案
评论