• Qt音视频开发1-vlc解码播放


    一、前言

    最开始接触视频监控这块的时候,用的就是vlc作为解码的内核,主要是因为vlc使用简单方便,直接传入一个句柄即可,简单几行代码就可以实现一个视频流播放,很适合初学者使用,也推荐初学者用qt+vlc来做播放器,提供的接口还是非常友好的,而且门类特别多,想要获取媒体文件的各种信息比如宽高,设置宽高比等,直接调用接口函数传入参数就能设置。

    所有用vlc做视频监控解码的人都会遇到一个问题,那就是鼠标事件被接管拦截了,不能识别鼠标事件,比如双击最大化等,这就很憋屈了,明明很好用的一个东西,怎么突然之间鼠标事件也识别不到了呢,网上一搜一大把,主要有三个解决办法。

    1. 修改vlc源码,重新编译,替换动态库文件。
    2. 全局鼠标钩子拦截鼠标消息进行处理。
    3. 设置句柄以后直接将控件/接受视频渲染的控件禁用掉。

    最终采用方法3,实现起来简单快速,修改vlc源码的编译工作量太大了,毕竟vlc依赖一大堆的插件,用vlc的人一般都是初学者半吊子,哪里有能力去编译一遍vlc哦。

    二、功能特点

    1. 多线程实时播放视频流和本地视频。
    2. 支持windows+linux+mac,支持vlc2和vlc3。
    3. 多线程显示图像,不卡主界面。
    4. 自动重连网络摄像头。
    5. 可设置边框大小即偏移量和边框颜色。
    6. 可设置是否绘制OSD标签即标签文本或图片和标签位置。
    7. 可设置两种OSD位置和风格。
    8. 可设置是否保存到文件以及文件名。
    9. 可直接拖曳文件到vlcwidget控件播放。
    10. 支持h265视频流+rtmp等常见视频流。
    11. 可暂停播放和继续播放。
    12. 支持回调模式和句柄两种模式。
    13. 支持线程读取进度等信息和事件回调两种处理模式。
    14. 自动将当前播放位置和音量大小是否静音以信号发出去。
    15. 提供接口设置播放位置和音量及设置静音。
    16. 支持存储单个视频文件和定时存储视频文件。
    17. 自定义顶部悬浮条,发送单击信号通知,可设置是否启用。

    三、效果图

    四、相关站点

    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

    五、核心代码

    bool VlcThread::init()
    {
        //判断该摄像机是否能联通
        if (checkConn && isRtsp) {
            if (!checkUrl(url, checkTime)) {
                return false;
            }
        }
    
        QFileInfo info(url);
        suffix = info.suffix();
    
        //设置拓展名
        if (url.startsWith("dshow://")) {
            suffix = "dshow";
        } else if (url.startsWith("rtsp")) {
            suffix = "rtsp";
        } else if (url.startsWith("rtmp")) {
            suffix = "rtmp";
        } else if (url.startsWith("http")) {
            suffix = "http";
        }
    
        const char *tempArg = "";
        if (suffix == "h264") {
            tempArg = "--demux=h264";
        } else if (suffix == "h265") {
            tempArg = "--demux=h265";
        }
    
        const char *vlc_args[9] = {"-I", "dummy", "--no-osd", "--no-stats", "--ignore-config", "--no-video-on-top", "--no-video-title-show", "--no-snapshot-preview", tempArg};
        vlcInst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
        if (vlcInst == NULL) {
            return false;
        }
    
        if (isRtsp || suffix == "dshow") {
            vlcMedia = libvlc_media_new_location(vlcInst, url.toUtf8().constData());
        } else {
            //windows上需要替换文件路径
            QString url = this->url;
            url = QDir::toNativeSeparators(url);
            vlcMedia = libvlc_media_new_path(vlcInst, url.toUtf8().constData());
        }
    
        if (vlcMedia == NULL) {
            return false;
        }
    
        //媒体播放对象
        vlcPlayer = libvlc_media_player_new_from_media(vlcMedia);
        if (vlcPlayer == NULL) {
            return false;
        }
    
        //创建事件管理器
        if (callbackevent) {
            vlcEvent = libvlc_media_player_event_manager(vlcPlayer);
            libvlc_event_new(vlcEvent, this);
        }
    
        //回调方式和句柄方式两种分别处理
        if (callback) {
            callbackData = new CallbackData;
            callbackData->thread = this;
            callbackData->pixels = new uchar[bufferWidth * bufferHeight * 4];
            memset(callbackData->pixels, 0, bufferWidth * bufferHeight * 4);
    
            int width = callbackData->thread->getBufferWidth();
            int height = callbackData->thread->getBufferHeight();
    
            //设置回调拿到每帧数据
            libvlc_video_set_callbacks(vlcPlayer, lock, unlock, display, callbackData);
            //设置每帧格式 RV32-Format_RGB32  RGBA-Format_RGBA8888 YUYV I420
            libvlc_video_set_format(vlcPlayer, "RV32", width, height, width * 4);
        } else {
            //设置播放句柄
            if (playWidget == NULL) {
                return false;
            }
    
    #if defined(Q_OS_WIN)
            libvlc_media_player_set_hwnd(vlcPlayer, (void *)playWidget->winId());
    #elif defined(Q_OS_LINUX)
            libvlc_media_player_set_xwindow(vlcPlayer, playWidget->winId());
    #elif defined(Q_OS_MAC)
            libvlc_media_player_set_nsobject(vlcPlayer, (void *)playWidget->winId());
    #endif
    
            //禁用句柄鼠标消息
            QTimer::singleShot(1000, this, SLOT(disableHwnd()));
        }
    
        //设置硬件加速 none auto any d3d11va dxva2
        setOption(QString(":avcodec-hw=%1").arg(hardware));
        //设置通信协议 tcp udp
        setOption(QString(":rtsp-%1").arg(transport));
        //设置缓存时间 默认500毫秒
        setOption(QString(":network-caching=%1").arg(caching));
        //:rtsp-frame-buffer-size=1000000
    
        //设置宽度高度,本地USB摄像头需要单独设置
        if (suffix == "dshow") {
            setOption(QString(":dshow-size=%1*%2").arg(videoWidth).arg(videoHeight));
        } else {
            setSize(videoWidth, videoHeight);
        }
    
        //设置保存文件
        this->initSave();
        //打开播放
        libvlc_media_player_play(vlcPlayer);
    
        //qDebug() << TIMEMS << "init vlc finsh";
        return true;
    }
  • 相关阅读:
    并发编程三要素:原子性,有序性,可见性
    【华为云技术分享】【Python成长之路】来聊聊多线程的几位“辅助”
    【华为云技术分享】根因分析
    【华为云技术分享】浅谈服务化和微服务化(下)
    【华为云技术分享】浅谈服务化和微服务化(上)
    【华为云技术分享】STM32 GPIO的原理、特性、选型和配置
    【华为云技术分享】快速理解spark-on-k8s中的external-shuffle-service
    【华为云技术分享】如何设计高质量软件-领域驱动设计DDD(Domain-Driven Design)学习心得
    【华为云技术分享】唐老师带你秒懂大数据,以及Spark和Flink在干啥咧
    【华为云技术分享】昇腾AI处理器软件栈--总览
  • 原文地址:https://www.cnblogs.com/chinasoft/p/15291398.html
Copyright © 2020-2023  润新知