播放器如何实现跳转
实现思路
1. 检查是否需要跳转 (m_isSeek)
在解复用循环中,程序首先检查 m_isSeek 标志位是否被设置。如果该标志位被设置为 1,表示用户请求了前进或后退操作。
2. 计算跳转目标 (seekTarget)
当检测到需要跳转时,代码计算出跳转目标时间戳 seekTarget。这个时间戳是用户希望跳转到的目标时间点,并且单位是微秒(AV_TIME_BASE 是 FFmpeg 中定义的时间基准,表示 1 秒对应的时间单位数)。
3. 执行跳转 (av_seek_frame)
跳转到指定位置是通过 av_seek_frame 函数实现的。这里使用了 AVSEEK_FLAG_BACKWARD 标志,这意味着 FFmpeg 会跳转到最接近的关键帧位置。
ret = av_seek_frame(m_fmtCtx, -1, seekTarget, AVSEEK_FLAG_BACKWARD);
m_fmtCtx: 输入的格式上下文,包含了流的格式信息。-1: 表示跳转到所有流的最近关键帧,而不仅限于某个流。seekTarget: 目标时间戳。AVSEEK_FLAG_BACKWARD: 跳转到关键帧。
如果跳转失败,代码会输出错误信息并退出函数。
4. 刷新队列
在成功跳转之后,程序会清空音频包队列和视频包队列(m_audioPacketQueue 和 m_vedioPacketQueue),以确保之前缓存的数据不会影响到跳转后的解码和播放。
packetQueueFlush(&m_audioPacketQueue);
packetQueueFlush(&m_vedioPacketQueue);
同时,将音频和视频的跳转标志位 m_audSeek 和 m_viSeek 设置为 1,表示当前流已经跳转过。
5. 继续解复用
在执行完跳转操作并清理队列后,m_isSeek 被重置为 0,表明跳转操作已经完成。程序继续正常的解复用过程,通过 Audio::AudioFFmpeg::GetAudio()->Read() 函数读取下一个数据包,并将其分发到对应的音频或视频队列中。
6. 同步播放
在实际播放中,程序需要根据解码后的时间戳同步音视频流,以确保用户在前进或后退之后能够准确播放所跳转到的内容。这部分逻辑可能在解码阶段或渲染阶段进行。
代码
void AVDecoder::Decoder::demux(std::shared_ptr<void> par)
{
AVPacket *AVpkt = av_packet_alloc();
int ret = -1;
int num = 0;
for (;;)
{
// qDebug()<<("nums=%d\n", num++);
//|| m_vedioPacketQueue.size >= m_maxPacketQueueSize
if (m_audioPacketQueue.size >= m_maxPacketQueueSize || m_vedioPacketQueue.size >= m_maxPacketQueueSize)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
if (m_isSeek)
{
int64_t seekTarget = m_seekTarget * AV_TIME_BASE;
ret = av_seek_frame(m_fmtCtx, -1, seekTarget, AVSEEK_FLAG_BACKWARD);
if (ret < 0)
{
qDebug()<<("av_seek_frame error\n");
return;
}
else
{
packetQueueFlush(&m_audioPacketQueue);
packetQueueFlush(&m_vedioPacketQueue);
m_audSeek = 1;
m_viSeek = 1;
}
m_isSeek = 0;
}
AVpkt = Audio::AudioFFmpeg::GetAudio()->Read();
if (AVpkt == nullptr)
{
break;
}
if (AVpkt->stream_index == Audio::AudioFFmpeg::GetAudio()->audioStreamIndex)
{
// 插入音频包队列
pushPacket(&m_audioPacketQueue, AVpkt);
}
else if (AVpkt->stream_index == VedioFFmpeg::Get()->VedioStream)
{
// 插入视频包队列
pushPacket(&m_vedioPacketQueue, AVpkt);
}
else
{
av_packet_unref(AVpkt);
}
// av_usleep(50);
}
// 释放pkt
av_packet_free(&AVpkt);
if (!m_exit)
{
while (m_audioFrameQueue.size)
QThread::msleep(50);
exit();
qDebug()<<("demux of thread exit\n");
}
// qDebug()<<(" video queue of len:%d\n", AVDecoder::Decoder::getDecoder()->m_vedioPacketQueue.size);
// qDebug()<<(" audio queue of len:%d\n", AVDecoder::Decoder::getDecoder()->m_audioPacketQueue.size);
}

浙公网安备 33010602011771号