Audio and video interception based on FFMPEG (C++Qt version)

Keywords: C++ Qt

Audio and video interception based on FFMPEG (C++Qt version)

This blog is based on the previous blog:

https://blog.csdn.net/liyuanbhu/article/details/121744275

The last blog realized the file encapsulation conversion. We add a little more function on this basis. The implementation can arbitrarily intercept audio and video within a period of time.

Here is the code:

QlyAVFormatContext inFile, outFile;
inFile.openFile(QString("D:\\AV36_1.avi"));
inFile.dumpFormat();
QSet<AVMediaType> type;    type << AVMEDIA_TYPE_VIDEO << AVMEDIA_TYPE_AUDIO;
QVector<QlyAVStream> inStreams = inFile.findStreams(type);

qDebug() << inStreams[0].m_stream->time_base;
outFile.createFile(QString(), QString("D:\\AV36_1-qt-15.mkv"));
outFile.setStreams(inStreams);
outFile.writeHeader();

inFile.seekFrame(10.0, -1, AVSEEK_FLAG_BACKWARD);

QlyAVPacket pkt;
while(inFile.readFrame(pkt, type))
{
    AVRational in_tb = inFile.rawStream(pkt.streamIndex())->time_base;
    if(pkt.compare_ts(25.0) >= 0)
    {
        pkt.unref();
        break;
    }
    pkt.adjustTime(10.0);
    outFile.writeFrame(pkt, in_tb, true);
    pkt.unref();
}
outFile.writeTrailer();

This code intercepts audio and video from 10s to 25s. That is, I intercepted 15s of audio and video. I won't explain this code from the beginning, just talk about the difference between this code and the last blog code. First, a line of code is added:

inFile.seekFrame(10.0, -1, AVSEEK_FLAG_BACKWARD);

seekFrame is defined as follows:

/**
     * @brief seekFrame Frame pointer of mobile media file
     * @param time      Time in seconds
     * @param stream_index -1 Indicates that it is not limited to a stream
     * @param seekFlag  Can be AVSEEK_FLAG_BACKWARD
     *                        AVSEEK_FLAG_BYTE
     *                        AVSEEK_FLAG_ANY
     *                        AVSEEK_FLAG_FRAME
     * @return true Indicates found, false indicates error
     */
bool QlyAVFormatContext::seekFrame(double time, int stream_index, int seekFlag)
{
    int64_t timestamp = 0;
    if(stream_index == -1)
    {
        timestamp = time * AV_TIME_BASE;
    }
    else
    {
        AVStream *in_stream = pFormatCtx->streams[stream_index];
        timestamp =  time / av_q2d(in_stream->time_base);
    }
    errorcode = av_seek_frame(pFormatCtx, stream_index, timestamp, seekFlag);
    return (errorcode >= 0);
}

You can see that AV is actually called internally_ seek_ Frame() function. However, for ease of use, time is in seconds, and it is double, that is, it can distinguish finer time units (such as milliseconds and microseconds).

In addition, AV in the code_ Q2D can convert AVRational to floating point numbers. It is convenient for us to calculate.

timestamp = time * AV_TIME_BASE;

This line of code is worth talking about, AV_TIME_BASE is the default time unit of ffmpeg, which indicates how many parts a second is divided into. If we do not specify a specific flow, we use this time unit. In the current ffmpeg version, AV_TIME_BASE =1000000. That is, the basic unit of time is microseconds. If we specify a flow, we need to use the time unit of that flow. So:

timestamp =  time / av_q2d(in_stream->time_base);

Let's talk about another function:

if(pkt.compare_ts(25.0) >= 0)

compare_ The TS () function compares the time of the current frame with the time represented by the function parameters.

/**
     * @brief compare_ts
     * @param timestamp
     * @return  -1 Indicates that the time of the current frame is less than timestamp, 1 indicates greater than, and 0 indicates equal
     */
    int compare_ts(double timestamp)

The implementation code is very simple, using av_compare_ts() function:

int QlyAVPacket::compare_ts(double timestamp)
{
    AVRational av_time_base_q = {1, AV_TIME_BASE};
    return av_compare_ts(m_packet.pts, m_timeBase, timestamp * AV_TIME_BASE, av_time_base_q);
}

I don't use AV here_ TIME_ BASE_ Q. Because AV_ TIME_ BASE_ The definition of Q does not conform to the syntax of C + + (it conforms to the syntax of C language). No way, I made an AV myself_ time_ base_ q.

Further down, there is another place to explain:

pkt.adjustTime(10.0);

This function is to move the time forward to 10s. Otherwise, the time we see when playing with the player does not start from 0. This is a function implementation, and the code is also very simple. To be honest, the name of the adjustTime function is not very good. But I didn't think of a better name. If you have a more appropriate name, you can leave me a message.

void QlyAVPacket::adjustTime(double timestamp)
{
    int64_t ts = timestamp * m_timeBase.den / m_timeBase.num;
    m_packet.pts = m_packet.pts - ts;
    m_packet.dts = m_packet.dts - ts;
}

At this point, the code is finished.

The problem of sub encapsulation is basically finished. The next blog starts to encode audio and video.

Posted by Centrek on Mon, 06 Dec 2021 19:30:31 -0800