How to clean the packets of AVFormatContext?

29 views Asked by At

I'm trying to write a video recorder class with ffmpeg, but video file generate have a issue. In every record, the second 1 of the video is a dark frame, and the second 2 is single frame, repeated 25 times. After that, the video runs normal. If I record a video during 10 seconds, the video file will have 12 seconds (2 first second with this issue).

This is my code to start and record the frames:

#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;
}

I know this extra 2 seconds of frames comes from avformat_open_input(&inputFormatContext, camera_address.c_str(), nullptr, &options) and avformat_find_stream_info(inputFormatContext, nullptr) execution, this fuction execute in around 2-3 seconds, and at this time, wrong packets are writen to inputFormatContext.

I've already tried:

avformat_flush(inputFormatContext);;

while (av_read_frame(inputFormatContext, &packet) >= 0) { av_packet_unref(&packet); };

Write the first 2 seconds in another file;

Cut the first 2 seconds from the file (this works in the terminal, but not in code)

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;
    }
}

;

Verify packet.dts and packet.pts, if wrong values is find, don't write the packet;

None of this solved my problem. I just need a way to remove this first 2 seconds of wrong frames from my video.

0

There are 0 answers