[英]C++ ffmpeg video missing frames and won't play in Quicktime
I wrote some C++ code that uses ffmpeg to encode a video.我编写了一些使用 ffmpeg 对视频进行编码的 C++ 代码。 I'm having two strange issues:我有两个奇怪的问题:
ffprobe -show_frames -pretty $VIDEO | grep -F '[FRAME]' | wc -l
tells me.也就是说,如果我让它编码 10 帧,最终视频只有 9(至少ffprobe -show_frames -pretty $VIDEO | grep -F '[FRAME]' | wc -l
告诉我。My code is roughly this (modified a bit to remove types that are unique to our code base):我的代码大致是这样(稍微修改以删除我们代码库独有的类型):
First, I open the video file, write the headers and initialize things:首先,我打开视频文件,编写标题并初始化:
template <class PtrT>
using UniquePtrWithDeleteFunction = std::unique_ptr<PtrT, std::function<void (PtrT*)>>;
std::unique_ptr<FfmpegEncodingFrameSink> FfmpegEncodingFrameSink::Create(
const std::string& dest_url) {
AVFormatContext* tmp_format_ctxt;
auto alloc_format_res = avformat_alloc_output_context2(&tmp_format_ctxt, nullptr, "mp4", dest_url.c_str());
if (alloc_format_res < 0) {
throw FfmpegException("Error opening output file.");
}
auto format_ctxt = UniquePtrWithDeleteFunction<AVFormatContext>(
tmp_format_ctxt, CloseAvFormatContext);
AVStream* out_stream_video = avformat_new_stream(format_ctxt.get(), nullptr);
if (out_stream_video == nullptr) {
throw FfmpegException("Could not create outputstream");
}
auto codec_context = GetCodecContext(options);
out_stream_video->time_base = codec_context->time_base;
auto ret = avcodec_parameters_from_context(out_stream_video->codecpar, codec_context.get());
if (ret < 0) {
throw FfmpegException("Failed to copy encoder parameters to outputstream");
}
if (!(format_ctxt->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&format_ctxt->pb, dest_url.c_str(), AVIO_FLAG_WRITE);
if (ret < 0) {
throw VideoDecodeException("Could not open output file: " + dest_url);
}
}
ret = avformat_init_output(format_ctxt.get(), nullptr);
if (ret < 0) {
throw FfmpegException("Unable to initialize the codec.");
}
ret = avformat_write_header(format_ctxt.get(), nullptr);
if (ret < 0) {
throw FfmpegException("Error occurred writing format header");
}
return std::unique_ptr<FfmpegEncodingFrameSink>(
new FfmpegEncodingFrameSink(std::move(format_ctxt), std::move(codec_context)));
}
Then, every time I get a new frame to encode I pass it to this function (the frames are being decoded via ffmpeg from another mp4 file which Quicktime plays just fine):然后,每次我得到一个新的帧进行编码时,我都会将它传递给这个函数(帧是通过 ffmpeg 从另一个 Quicktime 播放的 mp4 文件中解码的):
// If frame == nullptr then we're done and we're just flushing the encoder
// otherwise encode an actual frame
void FfmpegEncodingFrameSink::EncodeAndWriteFrame(
const AVFrame* frame) {
auto ret = avcodec_send_frame(codec_ctxt_.get(), frame);
if (ret < 0) {
throw FfmpegException("Error encoding the frame.");
}
AVPacket enc_packet;
enc_packet.data = nullptr;
enc_packet.size = 0;
av_init_packet(&enc_packet);
do {
ret = avcodec_receive_packet(codec_ctxt_.get(), &enc_packet);
if (ret == AVERROR(EAGAIN)) {
CHECK(frame != nullptr);
break;
} else if (ret == AVERROR_EOF) {
CHECK(frame == nullptr);
break;
} else if (ret < 0) {
throw FfmpegException("Error putting the encoded frame into the packet.");
}
assert(ret == 0);
enc_packet.stream_index = 0;
LOG(INFO) << "Writing packet to stream.";
av_interleaved_write_frame(format_ctxt_.get(), &enc_packet);
av_packet_unref(&enc_packet);
} while (ret == 0);
}
Finally, in my destructor I close everything up like so:最后,在我的析构函数中,我像这样关闭所有内容:
FfmpegEncodingFrameSink::~FfmpegEncodingFrameSink() {
// Pass a nullptr to EncodeAndWriteFrame so it flushes the encoder
EncodeAndWriteFrame(nullptr);
// write mp4 trailer
av_write_trailer(format_ctxt_.get());
}
If I run this passing n
frames to EncodeAndWriteFrame
line LOG(INFO) << "Writing packet to stream.";
如果我运行这个将n
帧传递给EncodeAndWriteFrame
行LOG(INFO) << "Writing packet to stream.";
gets run n
times indicating the n
packets were written to the stream. get 运行n
次,表明n
数据包已写入流。 But ffprobe
always shows only n - 1
frames int he video.但是ffprobe
在视频中始终只显示n - 1
帧。 And the final video doesn't play on quicktime.最后的视频不能在 quicktime 上播放。
What am I doing wrong??我做错了什么??
Sorry for the delay but as i just had the same problem and noticed that this question deserves an answer, here how i solved this.抱歉耽搁了,但因为我刚刚遇到了同样的问题,并注意到这个问题值得回答,这里我是如何解决这个问题的。 Up in front, the Problem only occured for me when using mov, mp4, 3gp as format.在前面,问题只发生在我使用 mov、mp4、3gp 作为格式时。 It worked frame accurate when using eg avi format.使用例如 avi 格式时,它的工作帧准确。 When i wrote uncompressed video frames to the container, i saw that the avi and mov had the same count of frames stored but the mov obviously had some problem in it's header.当我将未压缩的视频帧写入容器时,我看到 avi 和 mov 存储的帧数相同,但 mov 的标题显然存在一些问题。
Counting the number of frames in the mov using header metadata showed one frame is missing:使用标头元数据计算 mov 中的帧数显示缺少一帧:
ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 c:\temp\myinput.mov
While ignoring the index showed the correct number of frames:忽略索引显示正确的帧数:
-ignore_editlist 1
The solution for me was, set the timebase to the AVStream->CodeContext of the video stream .我的解决方案是,将时基设置为视频流的 AVStream->CodeContext 。
The code above attempts to do this in this line:上面的代码试图在这一行中做到这一点:
out_stream_video->time_base = codec_context->time_base;
But the problem is that the posted code above does not expose the function GetCodecContext so we do not know if the time_base is correctly set for "codec_context".但问题是上面发布的代码没有公开 GetCodecContext 函数,所以我们不知道 time_base 是否为“codec_context”正确设置。 So it is my believe that the author's problem was that his function GetCodecContext did not set the time_base correctly.所以我认为作者的问题是他的函数 GetCodecContext 没有正确设置 time_base。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.