基于FFMPEG的音视频截取(C++Qt 版)

基于FFMPEG的音视频截取(C++Qt 版)

这篇博客是基于上篇博客的:

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

上篇博客实现了文件封装转换。我们在这个基础上再加一点功能。实现可以任意截取一段时间范围内的音视频。

下面是代码:

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();

这个代码从10s开始截取音视频,一直截取到25s。也就是截取了 15s 的音视频。我不会从头解释这个代码,只是讲讲这个代码与上篇博客代码的区别。首先,增加了这么一行代码:

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

这里 seekFrame 的定义如下:

/**
     * @brief seekFrame 移动媒体文件的帧指针
     * @param time      时间,以秒为单位
     * @param stream_index -1 表示不局限于某个流
     * @param seekFlag  可以是 AVSEEK_FLAG_BACKWARD
     *                        AVSEEK_FLAG_BYTE
     *                        AVSEEK_FLAG_ANY
     *                        AVSEEK_FLAG_FRAME
     * @return true 表示找到了,false 表示出错
     */
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);
}

可以看到,内部其实是调用了 av_seek_frame() 函数。但是为了使用方便,time 是以秒为单位的,而且是 double 型,也就是可以分辨更精细的时间单位(比如毫秒、微秒)。

另外,代码里的 av_q2d 可以将 AVRational 转换为 浮点数。方便我们运算。

timestamp = time * AV_TIME_BASE;

这行代码值得讲讲,AV_TIME_BASE 是 ffmpeg 默认的时间单位,表示的是1秒分成多少份。如果我们没有指定某一个特定的流,那么就用这个时间单位。在现在的 ffmpeg 版本中,AV_TIME_BASE =1000000。也就是说基本的时间单位是微秒。如果我们制定了某个流,就要用那个流的时间单位。所以:

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

下面再讲讲另一个函数:

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

compare_ts() 函数比较当前帧的时间和函数参数表示的时间的前后关系。

/**
     * @brief compare_ts
     * @param timestamp
     * @return  -1 表示当前帧的时间小于 timestamp, 1 表示大于, 0 表示相等
     */
    int compare_ts(double timestamp)

实现代码很简单,用到了 av_compare_ts() 函数:

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

这里我没有用 AV_TIME_BASE_Q,是因为 AV_TIME_BASE_Q 的定义不符合 C++ 的语法(是符合C 语言语法的)。没办法,我自己搞了个 av_time_base_q。

再往下,还有个地方需要解释:

pkt.adjustTime(10.0);

这个函数是把时间往前调10s。否则用播放器播放时我们看到的时间不是从0开始的。这是函数实现代码也非常简单。说实话,adjustTime 这个函数名起的不太好。不过我也没想到更好的名字。大家要是有更贴切的名字可以给我留言。

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

至此,这个代码就讲完了。

转封装问题基本就都讲完了,下一篇博客开始将音视频编码。

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在 C 语言中使用 FFmpeg 同步音视频可以通过以下步骤实现: 1. 首先,你需要在你的 C 项目中引用 FFmpeg 库。你可以从 FFmpeg 官方网站下载最新的源代码,并按照官方提供的文档进行编译和安装。 2. 导入 FFmpeg 相关的头文件,并链接 FFmpeg 库。在你的 C 代码中,可以使用如下语句导入头文件: ```c #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libavutil/time.h> ``` 同时,你需要在链接时将 FFmpeg 库添加到你的项目中。在编译时,你需要添加 `-lavformat -lavcodec -lavutil` 等选项。 3. 打开音视频文件并解码。使用 `avformat_open_input` 函数打开音视频文件,然后使用 `avformat_find_stream_info` 函数获取音视频流信息。接下来,使用 `avcodec_find_decoder` 函数查找音视频解码器,并打开解码器。 4. 根据音视频帧时间戳进行同步。FFmpeg 解码后的音频和视频帧都带有时间戳信息。你可以使用 `av_read_frame` 函数读取音视频帧数据,并使用 `av_frame_get_best_effort_timestamp` 函数获取帧的时间戳。 5. 同步音视频流。根据音频和视频的时间戳,你可以判断哪个流需要等待或者丢弃帧数据。一种常见的同步方式是使用音频时间戳作为基准,视频帧根据音频时间戳进行调整,保持音视频流同步。 这只是一个简单的示例,具体的实现还需要根据你的需求和项目结构进行调整。请注意,FFmpeg 提供了非常丰富的 API 和功能,你可以根据自己的需求进一步探索和使用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值