zoukankan      html  css  js  c++  java
  • Qt音视频开发10-ffmpeg控制播放

    一、前言

    很多人在用ffmpeg做视频流解码的时候,都会遇到一个问题,如何暂停,如果打开的是本地视频文件,暂停你只需要停止解码即可,但是视频流你会发现根本没用,一旦你停止了解码,下次重新解码的时候,居然还是以前的图片,他是从你最后暂停开始的地方重新解码的,这就懵逼了,为啥呢?我个人的理解是视频流这玩意,一旦你打开了,他就源源不断涌过来,你不处理,他就越来越多,你必须要读取他,从缓冲区拿走这些数据才行,所以如果想要暂停视频流,正确的做法是照常解码,只是不处理和绘制图片就行,说白了其实就是伪暂停,看起来是暂停了,其实后台还在不断的解码中。

    用ffmpeg播放本地文件的时候,如果不加延时,你会发现刷刷几秒钟就播放完了,具体看电脑的性能,性能好的电脑也就几秒钟播放一个5分钟的视频,是不是会觉得很奇怪呢,怎么播放的这么快呢,其实ffmpeg解码只管解码,疯狂解码模式,使命的干,榨干你的CPU或者GPU资源(如果开启了硬解码则走GPU),解码后的每一帧都带有pts dts等信息,需要自己根据这些信息来做延时处理,比如还没到下一帧的时候,你就延时一段时间再去解码,至于延时多久有一个通用的计算方法,在打开流后记住开始的时间。然后解码中取出对应流(视频流或者音频流等)的基准时间time_base,调用av_rescale_q计算出pts时间,然后用av_gettime() - startTime得到当前的时间,用pts_time - now_time得到时间差值,如果是正数,则这个时间就是需要延时的微秒数,注意是微秒数而不是毫秒数哦,直接调用av_usleep来延时即可。

    二、功能特点

    1. 多线程实时播放视频流+本地视频+USB摄像头等。
    2. 支持windows+linux+mac,支持ffmpeg3和ffmpeg4,支持32位和64位。
    3. 多线程显示图像,不卡主界面。
    4. 自动重连网络摄像头。
    5. 可设置边框大小即偏移量和边框颜色。
    6. 可设置是否绘制OSD标签即标签文本或图片和标签位置。
    7. 可设置两种OSD位置和风格。
    8. 可设置是否保存到文件以及文件名。
    9. 可直接拖曳文件到ffmpegwidget控件播放。
    10. 支持h265视频流+rtmp等常见视频流。
    11. 可暂停播放和继续播放。
    12. 支持存储单个视频文件和定时存储视频文件。
    13. 自定义顶部悬浮条,发送单击信号通知,可设置是否启用。
    14. 可设置画面拉伸填充或者等比例填充。
    15. 可设置解码是速度优先、质量优先、均衡处理。
    16. 可对视频进行截图(原始图片)和截屏。
    17. 录像文件存储支持裸流和MP4文件。
    18. 支持qsv、dxva2、d3d11va等硬解码。
    19. 支持opengl绘制视频数据,极低CPU占用。
    20. 支持嵌入式linux,交叉编译即可。

    三、效果图

    四、相关站点

    1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
    2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
    3. 个人主页:https://blog.csdn.net/feiyangqingyun
    4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
    5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

    五、核心代码

    void FFmpegWidget::updateImage(const QImage &image)
    {
        //暂停或者不可见 rtsp视频流需要停止绘制
        if (!this->property("isPause").toBool() && this->isVisible() && thread->isRunning()) {
            //拷贝图片有个好处,当处理器比较差的时候,图片不会产生断层,缺点是占用时间
            //默认QImage类型是浅拷贝,可能正在绘制的时候,那边已经更改了图片的上部分数据
            this->image = copyImage ? image.copy() : image;
            this->update();
        }
    }
    
    void FFmpegWidget::updateFrame(AVFrame *frame)
    {
    #ifdef opengl
        //暂停或者不可见 rtsp视频流需要停止绘制
        if (!this->property("isPause").toBool() && (yuvWidget->isVisible() || nv12Widget->isVisible()) && thread->isRunning()) {
            //采用了硬件加速的直接用nv12渲染,否则采用yuv渲染
            if (thread->getHardware() == "none") {
                yuvWidget->setFrameSize(frame->width, frame->height);
                yuvWidget->updateTextures(frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
            } else {
                nv12Widget->setFrameSize(frame->width, frame->height);
                nv12Widget->updateTextures(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1]);
            }
        }
    #endif
    }
    
    void FFmpegThread::delayTime(int streamIndex, AVPacket *packet)
    {
        //视频流不用延时
        if (isRtsp) {
            return;
        }
    
        //没有视频时长的文件和asf的本地文件采用另外的延时计算
        if (streamIndex == videoStreamIndex) {
            if (interval != 1 || videoTime < 0 || url.toLower().endsWith("asf")) {
                sleepVideo();
                return;
            }
        }
    
        qint64 offset_time = getDelayTime(streamIndex, packet);
        if (offset_time > 0) {
            av_usleep(offset_time);
        }
    }
    
    qint64 FFmpegThread::getDelayTime(int streamIndex, AVPacket *packet)
    {
        AVRational time_base = formatCtx->streams[streamIndex]->time_base;
        AVRational time_base_q = {1, AV_TIME_BASE};
        int64_t pts_time = av_rescale_q(packet->pts, time_base, time_base_q);
        int64_t now_time = av_gettime() - startTime;
        int64_t offset_time = pts_time - now_time;
        return offset_time;
    }
    
  • 相关阅读:
    用Azure VM + Azure Database for MySQL搭建Web服务
    Azure镜像的跨区域复制—Shared Image Gallery(共享映像库)初探
    Azure上几种常见的VM复制操作
    Exchange 2016与国内版O365混合部署(6):混合后的操作和验证
    Exchange 2016与国内版O365混合部署(5):运行AAD Connect及混合部署向导
    Exchange 2016与国内版O365混合部署(4):配置Exchange 公网证书
    Exchange 2016与国内版O365混合部署(3):安装Exchange2016并配置邮件的外网收发
    Exchange 2016与国内版O365混合部署(2):搭建域环境
    Exchange 2016与国内版O365混合部署(1):过程总览
    Cross-Tanant 步骤
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13503388.html
Copyright © 2011-2022 走看看