You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FFmpeg录屏首2秒异常帧问题:如何清理AVFormatContext缓存包?

FFmpeg录制视频前2秒异常帧问题解决

我使用FFmpeg编写视频录制类时遇到异常:录制生成的视频前1秒为黑屏帧,第2秒为单帧重复25次,后续播放正常。若录制10秒视频,最终文件时长变为12秒,多出来的2秒就是前面的异常部分。

相关代码

头文件与全局变量

#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include <thread>

extern "C" {
#include <libavformat/avformat.h>
}
bool recording;

const std::string recordFileName = "video.avi";

std::string camera_address;
std::thread recordingThread;

AVFormatContext* inputFormatContext;
AVOutputFormat* outputFormat;
AVFormatContext* outputFormatContext;
AVStream* outputVideoStream;

启动录制函数

bool VideoRecorder::start_record()
{
    if (!recording) {
        AVDictionary* options = nullptr;
        av_dict_set(&options, "rtsp_transport", "udp", 0);

        if (avformat_open_input(&inputFormatContext, camera_address.c_str(), nullptr, &options) != 0) {
            std::cerr << "Error opening input stream." << std::endl;
            return false;
        }

        if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
            std::cerr << "Error finding stream information." << std::endl;
            avformat_close_input(&inputFormatContext);
            return false;
        }

        // Open output file AVI
        if (avformat_alloc_output_context2(&outputFormatContext, outputFormat, "avi", recordFileName.c_str()) < 0) {
            std::cerr << "Error allocating output context." << std::endl;
            avformat_close_input(&inputFormatContext);
            return false;
        }

        // Add a new video stream to the output file
        outputVideoStream = avformat_new_stream(outputFormatContext, nullptr);
        if (!outputVideoStream) {
            std::cerr << "Error creating output video stream." << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return false;
        }

        if (avcodec_parameters_copy(outputVideoStream->codecpar, inputFormatContext->streams[0]->codecpar) < 0) {
            std::cerr << "Error copying codec parameters." << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return false;
        }
        // Open the output file for writing
        if (avio_open(&outputFormatContext->pb, recordFileName.c_str(), AVIO_FLAG_WRITE) < 0) {
            std::cerr << "Error opening output file for writing." << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return false;
        }
        // Write output file header
        if (avformat_write_header(outputFormatContext, nullptr) < 0) {
            std::cerr << "Error writing output file header." << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return false;
        }

        recording = true;
        recordingThread = std::thread(&VideoRecorder::record_video, this);
        std::cout << "Recording started." << std::endl;
        return true;
    }
    return false;
}

帧录制函数

bool VideoRecorder::record_video()
{
    if (recording) {
        AVPacket packet;

        while (recording && av_read_frame(inputFormatContext, &packet) >= 0) {
            AVStream* in_stream = inputFormatContext->streams[packet.stream_index];
            AVStream* out_stream = outputFormatContext->streams[0];

            // Adjustment of DTS and PTS to ensure increasing monoticity
            if (packet.pts != AV_NOPTS_VALUE) {
                packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            }

            if (packet.dts != AV_NOPTS_VALUE) {
                packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            }

            packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);

            packet.stream_index = 0;
            av_interleaved_write_frame(outputFormatContext, &packet);

            av_packet_unref(&packet);
        }


        return true;
    }

    std::cerr << "Error recording video. Recording not started or already stopped." << std::endl;
    return false;
}

异常原因

已确认额外的2秒异常帧来自avformat_open_input(&inputFormatContext, camera_address.c_str(), nullptr, &options)和avformat_find_stream_info(inputFormatContext, nullptr)的执行过程,这两个函数耗时约2-3秒,期间产生的数据包被缓存到了inputFormatContext中,后续录制时被写入了输出文件。

已尝试的解决方案(均无效)

  • 调用avformat_flush(inputFormatContext);清理缓存
  • 循环读取并丢弃缓存包:while (av_read_frame(inputFormatContext, &packet) >= 0) { av_packet_unref(&packet); }
  • 将前2秒内容写入临时文件,不写入最终输出
  • 调用FFmpeg命令裁剪前2秒(终端执行有效,但代码中调用无效),裁剪代码如下:
void VideoRecorder::cut_video() {
    std::string outputFileName = "cut_" + recordFileName;
    std::string command = "ffmpeg -ss 2 -i " + recordFileName + " -c copy " + outputFileName;

    int result = std::system(command.c_str());

    if (result == 0) {
        std::cout << "Video cut successfully." << std::endl;
        
        std::remove(recordFileName.c_str());
        std::cout << "Original video file deleted." << std::endl;
    } else {
        std::cerr << "Error cutting video." << std::endl;
    }
}
  • 校验packet的dts和pts值,丢弃异常数据包

需求

需要一种能有效移除视频前2秒异常帧的方案,同时希望了解如何正确清理AVFormatContext中的缓存数据包。

内容的提问来源于stack exchange,提问作者Jorge Augusto Wilchen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:44:57