QT + FFMPEG实现基本播放器(三):视频播放实现

QT + FFMPEG实现基本播放器(三):视频播放实现

《QT + FFMPEG实现基本播放器(二):FFMPEG解码功能实现》中,将所有avPacket放入到了全局的一个结构体的队列当中 g_MedieInfo.m_VideoPacketQueue.push(avPacket);

因此可以实现视频的播放线程,从队列当中取出数据,从而可以将avPacket解码成原始的数据,从而将其转为QImage,发送到VideoWidget中进行显示:

VideoThread继承QThread,因此主要实现run函数

void VideoThread::run()
{
    qDebug()<<"Start Video Thread";

    while(m_IsRuning)
    {

        if( g_MedieInfo.m_VideoPacketQueue.empty())
        {
            msleep(1);
            continue;
        }

        AVPacket avPacket;
        g_MedieInfo.m_VideoPacketQueue.wait_and_pop(avPacket);


        //按解码顺序发送packet,将视频文件中的packet序列依次发送给解码器。发送packet的顺序如IPBBPBB
        int ret = avcodec_send_packet(g_MedieInfo.avFormatContext->streams[g_MedieInfo.videoStreamIndex]->codec, &avPacket);
        if (ret != 0)
        {
            qDebug()<<__func__<<__LINE__<<"send packet error";
            continue;
        }

        //按显示顺序输出frame,frame输出顺序是按pts递增的顺序。pts是解码时间戳。pts与dts不一致的问题
        //由解码器进行了处理,用户程序不必关心。从解码器接收frame的顺序如IBBPBBP
        //解码器中会缓存一定数量的帧,一个新的解码动作启动后,向解码器送入好几个packet解码器才会输出第一个packet
        //因为解码时帧之间有信赖关系,例如IPB三个帧被送入解码器后,B帧解码需要依赖I帧和P帧
        //所以在B帧输出前,I帧和P帧必须存在于解码器中而不能删除
        ret = avcodec_receive_frame(g_MedieInfo.avFormatContext->streams[g_MedieInfo.videoStreamIndex]->codec,vidioFrameRaw);
        if (ret < 0)
        {
            if (ret == AVERROR(EAGAIN))
            {
                continue;
            }

            if (ret == AVERROR_EOF)
            {
                qDebug()<<__func__<<__LINE__<<"receive frame error";
            }
        }


        //解码后的视频帧数据保存在vidioFrameRaw变量中,然后经过swscale函数转换后,将视频帧数据保存在vidioFrameConvert变量中
        sws_scale(g_MedieInfo.swsContext, (const uint8_t *const *)vidioFrameRaw->data, vidioFrameRaw->linesize, 0, videoHeight, vidioFrameConvert->data, vidioFrameConvert->linesize);

        //根据之前设置的输出图片格式,将原始数据转成一张图片
        QImage image(vidioFrameConvert->data[0], videoWidth, videoHeight, QImage::Format_RGB32);

        if (!image.isNull())
        {
            //将该QImage数据通过receiveImage信号发出
            emit ReceiveImage(image);
        }


        //根据帧率进行延时
        usleep(1000*1000/g_MedieInfo.m_FrameRate);

        av_packet_unref(&avPacket);
        av_freep(&avPacket);
    }

    qDebug()<<"Stop Video Thread";
}

其中需要注意的是,对于视频的播放,需要根据视频的帧率进行适当的延时,比如30帧的视频的含义为1秒钟播放30帧的画面,因此对于每一帧的播放都需要延时1000*1000/30 = 33333.333 us ,如果不进行延时的话,那么视频将会很快的播放完毕。根据这个原理,我们可以对延时时间进行处理,以达到倍速播放的目的。

使用emit ReceiveImage(image);发出的ReceiveImage信号,将会在VideoWidget中接收,然后转到对应的槽函数UpdataImage

void VideoWidget::UpdataImage(const QImage &image)
{
    m_Image = image;
    this->update();
}

通过调用update函数将会对页面进行刷新,从而将该QImage显示到VideoWidget上,从而实现视频播放的功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值