使用FFmpeg的C++编码视频丢帧且无法在QuickTime播放求助
Hey there! Let’s tackle these two common FFmpeg encoding headaches you’re dealing with—lost frames and QuickTime compatibility. I’ve run into both of these myself, so let’s break down what’s going on and how to fix them.
1. Missing the Final Frame (10 Frames Encoded → 9 Output)
What’s Causing It
Most video encoders (especially those that use B-frames for compression) cache frames internally to optimize prediction. When you stop sending input frames, the encoder still has that last set of cached frames stuck in its pipeline—you never trigger it to flush them out. That’s why you’re short one frame.
How to Fix It
After sending all your actual video frames to the encoder, you need to send a null frame to signal "no more input," then keep receiving packets until the encoder runs dry. Here’s how to add that to your code:
// After you’ve sent all your source frames to avcodec_send_frame() // Step 1: Signal the encoder to flush cached frames if (avcodec_send_frame(codec_ctx, nullptr) < 0) { // Handle error (log it, clean up, etc.) return -1; } // Step 2: Receive and write all remaining packets AVPacket pkt = {0}; av_init_packet(&pkt); int ret; while ((ret = avcodec_receive_packet(codec_ctx, &pkt)) == 0) { // Write the flushed packet to your output file if (av_interleaved_write_frame(fmt_ctx, &pkt) < 0) { // Handle write error av_packet_unref(&pkt); break; } av_packet_unref(&pkt); } // Ignore EAGAIN (encoder needs more input, which we don’t have) and EOF if (ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) { // Handle unexpected error }
This ensures every frame you sent gets processed and written to the output.
2. Black Screen in QuickTime (Plays Fine in MPV/VLC)
QuickTime is notoriously picky about video format compliance—MPV and VLC are way more forgiving. The issue almost always boils down to one of these four common mistakes:
Fix 1: Enable Global Headers for MP4
QuickTime requires the codec’s global header to be stored at the start of the MP4 file. Without it, it can’t properly initialize the decoder.
Add this line before opening the encoder:
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
Then, when setting up your output stream, make sure to copy the codec parameters correctly (including the extradata from the encoder):
AVStream* out_stream = avformat_new_stream(fmt_ctx, nullptr); if (!out_stream) { /* Handle error */ } // Copy codec parameters from encoder context to output stream if (avcodec_parameters_from_context(out_stream->codecpar, codec_ctx) < 0) { /* Handle error */ }
Fix 2: Use YUV420P Pixel Format
QuickTime only supports the YUV420P planar pixel format for H.264 video. If you’re feeding the encoder RGB, YUV444P, or any other format, it’ll render as black.
Use sws_scale to convert your input frames to YUV420P before sending them to the encoder. Here’s a quick snippet to set up the scaler:
struct SwsContext* sws_ctx = sws_getContext( input_width, input_height, input_pix_fmt, output_width, output_height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr ); // Convert your input frame to YUV420P sws_scale( sws_ctx, input_frame->data, input_frame->linesize, 0, input_height, output_frame->data, output_frame->linesize );
Fix 3: Correct Timebase and PTS/DTS Values
QuickTime relies on accurate timestamps to sync and render frames. Incorrect PTS/DTS values will cause it to fail to display content.
- Set the encoder’s timebase to match your frame rate (e.g., 30fps = 1/30):
codec_ctx->time_base = av_make_q(1, 30); // Replace 30 with your frame rate - Calculate the PTS for each frame correctly (use
av_rescale_qto convert to the encoder’s timebase):// frame_index starts at 0 for your first frame output_frame->pts = av_rescale_q(frame_index, av_make_q(1, 30), codec_ctx->time_base);
Fix 4: Ensure the First Frame Is a Keyframe
QuickTime needs the first frame to be an I-frame (keyframe) to start decoding. If your first frame is a P/B frame, it’ll show a black screen.
Force the first frame to be a keyframe:
if (frame_index == 0) { output_frame->key_frame = 1; output_frame->pict_type = AV_PICTURE_TYPE_I; }
You can also set a GOP size to ensure regular keyframes (e.g., one keyframe every 30 frames):
codec_ctx->gop_size = 30;
Final Checks
After making these changes, recompile and test:
- Verify the frame count again with
ffprobe -show_frames -pretty $VIDEO | grep -F '[FRAME]' | wc -l - Test the output in QuickTime—if it still shows black, double-check your pixel format conversion and timestamp calculations (those are the most common culprits).
内容的提问来源于stack exchange,提问作者Oliver Dain

