播放器如何实现跳转

实现思路

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_audioPacketQueuem_vedioPacketQueue),以确保之前缓存的数据不会影响到跳转后的解码和播放。

packetQueueFlush(&m_audioPacketQueue);
packetQueueFlush(&m_vedioPacketQueue);

同时,将音频和视频的跳转标志位 m_audSeekm_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);
}
posted @ 2024-08-24 10:06  daligh  阅读(94)  评论(0)    收藏  举报