赞
踩
这篇博客是基于上篇博客的:
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;
}
至此,这个代码就讲完了。
转封装问题基本就都讲完了,下一篇博客开始将音视频编码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。