Qt音视频开发19-vlc内核各种事件通知

news2024/11/15 14:02:37

一、前言

对于使用第三方的sdk库做开发,除了基本的操作函数接口外,还希望通过事件机制拿到消息通知,比如当前播放进度、音量值变化、静音变化、文件长度、播放结束等,有了这些才是完整的播放功能,在vlc中要拿到各种事件需要先通过libvlc_event_attach订阅事件,不需要的时候用libvlc_event_detach取消订阅即可,函数中第二个参数指定是何种事件类型,常用的事件类型有下列:

  • 文件长度变化=libvlc_MediaPlayerLengthChanged
  • 播放进度变化=libvlc_MediaPlayerTimeChanged
  • 音量大小变化=libvlc_MediaPlayerAudioVolume
  • 设置静音状态=libvlc_MediaPlayerMuted
  • 取消静音状态=libvlc_MediaPlayerUnmuted
  • 抓图信号通知=libvlc_MediaPlayerSnapshotTaken
  • 文件打开信号=libvlc_MediaPlayerOpening
  • 文件播放信号=libvlc_MediaPlayerPlaying
  • 暂停播放信号=libvlc_MediaPlayerPaused
  • 停止播放信号=libvlc_MediaPlayerStopped
  • 文件播放结束=libvlc_MediaPlayerEndReached

二、效果图

在这里插入图片描述

三、体验地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_demo/bin_linux_video。

四、相关代码

void VlcHelper::attachEvents(libvlc_event_manager_t *vlcEvent, VlcThread *thread)
{
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerLengthChanged, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerTimeChanged, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerAudioVolume, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerMuted, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerUnmuted, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerSnapshotTaken, handleEvents, thread);

    //libvlc_event_attach(vlcEvent, libvlc_MediaPlayerPositionChanged, handleEvents, thread);
    //libvlc_event_attach(vlcEvent, libvlc_MediaParsedChanged, handleEvents, thread);
    //libvlc_event_attach(vlcEvent, libvlc_MediaPlayerOpening, handleEvents, thread);
    //libvlc_event_attach(vlcEvent, libvlc_MediaPlayerPlaying, handleEvents, thread);
    //libvlc_event_attach(vlcEvent, libvlc_MediaPlayerPaused, handleEvents, thread);
    //libvlc_event_attach(vlcEvent, libvlc_MediaPlayerStopped, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerEndReached, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerEncounteredError, handleEvents, thread);
    libvlc_event_attach(vlcEvent, libvlc_MediaPlayerVout, handleEvents, thread);
}

void VlcHelper::detachEvents(libvlc_event_manager_t *vlcEvent, VlcThread *thread)
{
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerLengthChanged, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerTimeChanged, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerAudioVolume, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerMuted, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerUnmuted, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerSnapshotTaken, handleEvents, thread);

    //libvlc_event_detach(vlcEvent, libvlc_MediaPlayerPositionChanged, handleEvents, thread);
    //libvlc_event_detach(vlcEvent, libvlc_MediaParsedChanged, handleEvents, thread);
    //libvlc_event_detach(vlcEvent, libvlc_MediaPlayerOpening, handleEvents, thread);
    //libvlc_event_detach(vlcEvent, libvlc_MediaPlayerPlaying, handleEvents, thread);
    //libvlc_event_detach(vlcEvent, libvlc_MediaPlayerPaused, handleEvents, thread);
    //libvlc_event_detach(vlcEvent, libvlc_MediaPlayerStopped, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerEndReached, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerEncounteredError, handleEvents, thread);
    libvlc_event_detach(vlcEvent, libvlc_MediaPlayerVout, handleEvents, thread);
}

void VlcHelper::handleEvents(const libvlc_event_t *event, void *data)
{
    VlcThread *thread = (VlcThread *)data;
    //在回调事件中更新最后的消息时间
    thread->updateTime();
    //qDebug() << TIMEMS << event->type;

    switch (event->type) {
        case libvlc_MediaPlayerLengthChanged: {
            qint64 duration = event->u.media_player_length_changed.new_length;
            if (duration > 0 && thread->getIsFile()) {
                QMetaObject::invokeMethod(thread, "receiveDuration", Q_ARG(qint64, duration));
            }
        }
        break;

        case libvlc_MediaPlayerTimeChanged: {
            qint64 position = event->u.media_player_time_changed.new_time;
            if (position > 0 && thread->getIsFile()) {
                QMetaObject::invokeMethod(thread, "receivePosition", Q_ARG(qint64, position));
                //如果设置了重复循环播放则快到了文件末尾重新设置位置即可
                if (thread->getPlayRepeat() && (thread->getDuration() - position) < 500) {
                    QMetaObject::invokeMethod(thread, "setPosition", Q_ARG(qint64, 0));
                }
            }
        }
        break;

        case libvlc_MediaPlayerAudioVolume: {
            //这里获取到的音量值是 0.1 - 1.0
            float volume = event->u.media_player_audio_volume.volume * 100;
            QMetaObject::invokeMethod(thread, "receiveVolume", Q_ARG(int, volume));
        }
        break;

        case libvlc_MediaPlayerMuted: {
            bool muted = true;
            QMetaObject::invokeMethod(thread, "receiveMuted", Q_ARG(bool, muted));
        }
        break;

        case libvlc_MediaPlayerUnmuted: {
            bool muted = false;
            QMetaObject::invokeMethod(thread, "receiveMuted", Q_ARG(bool, muted));
        }
        break;

        case libvlc_MediaPlayerSnapshotTaken: {
            QMetaObject::invokeMethod(thread, "snapFinsh");
        }
        break;

        case libvlc_MediaPlayerPositionChanged: {
            float position = event->u.media_player_position_changed.new_position;
            thread->debug("播放进度", QString("进度: %1").arg(position));
        }
        break;

        case libvlc_MediaParsedChanged:
            thread->debug("媒体信息", "");
            break;

        case libvlc_MediaPlayerOpening:
            thread->debug("播放结束", "");
            break;

        case libvlc_MediaPlayerPlaying:
            thread->debug("播放开始", "");
            break;

        case libvlc_MediaPlayerPaused:
            thread->debug("播放暂停", "");
            break;

        case libvlc_MediaPlayerStopped:
            thread->debug("播放停止", "");
            break;

        case libvlc_MediaPlayerEndReached: {
            thread->debug("播放结束", "");
            thread->stop2();
        }
        break;

        case libvlc_MediaPlayerEncounteredError: {
            thread->debug("发生错误", "");
            thread->stop2();
        }
        break;

        case libvlc_MediaPlayerVout: {
            //识别尺寸发生变化
            thread->checkVideoSize();
        }
        break;

        default:
            break;
    }
}

五、功能特点

5.1 基础功能

  1. 支持各种音频视频文件格式,比如mp3、wav、mp4、asf、rm、rmvb、mkv等。
  2. 支持本地摄像头设备,可指定分辨率、帧率。
  3. 支持各种视频流格式,比如rtp、rtsp、rtmp、http等。
  4. 本地音视频文件和网络音视频文件,自动识别文件长度、播放进度、音量大小、静音状态等。
  5. 文件可以指定播放位置、调节音量大小、设置静音状态等。
  6. 支持倍速播放文件,可选0.5倍、1.0倍、2.5倍、5.0倍等速度,相当于慢放和快放。
  7. 支持开始播放、停止播放、暂停播放、继续播放。
  8. 支持抓拍截图,可指定文件路径,可选抓拍完成是否自动显示预览。
  9. 支持录像存储,手动开始录像、停止录像,部分内核支持暂停录像后继续录像,跳过不需要录像的部分。
  10. 支持无感知切换循环播放、自动重连等机制。
  11. 提供播放成功、播放完成、收到解码图片、收到抓拍图片、视频尺寸变化、录像状态变化等信号。
  12. 多线程处理,一个解码一个线程,不卡主界面。

5.2 特色功能

  1. 同时支持多种解码内核,包括qmedia内核(Qt4/Qt5/Qt6)、ffmpeg内核(ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5)、vlc内核(vlc2/vlc3)、mpv内核(mpv1/mp2)、海康sdk、easyplayer内核等。
  2. 非常完善的多重基类设计,新增一种解码内核只需要实现极少的代码量,就可以应用整套机制。
  3. 同时支持多种画面显示策略,自动调整(原始分辨率小于显示控件尺寸则按照原始分辨率大小显示,否则等比例缩放)、等比例缩放(永远等比例缩放)、拉伸填充(永远拉伸填充)。所有内核和所有视频显示模式下都支持三种画面显示策略。
  4. 同时支持多种视频显示模式,句柄模式(传入控件句柄交给对方绘制控制)、绘制模式(回调拿到数据后转成QImage用QPainter绘制)、GPU模式(回调拿到数据后转成yuv用QOpenglWidget绘制)。
  5. 支持多种硬件加速类型,ffmpeg可选dxva2、d3d11va等,mpv可选auto、dxva2、d3d11va,vlc可选any、dxva2、d3d11va。不同的系统环境有不同的类型选择,比如linux系统有vaapi、vdpau,macos系统有videotoolbox。
  6. 解码线程和显示窗体分离,可指定任意解码内核挂载到任意显示窗体,动态切换。
  7. 支持共享解码线程,默认开启并且自动处理,当识别到相同的视频地址,共享一个解码线程,在网络视频环境中可以大大节约网络流量以及对方设备的推流压力。国内顶尖视频厂商均采用此策略。这样只要拉一路视频流就可以共享到几十个几百个通道展示。
  8. 自动识别视频旋转角度并绘制,比如手机上拍摄的视频一般是旋转了90度的,播放的时候要自动旋转处理,不然默认是倒着的。
  9. 自动识别视频流播放过程中分辨率的变化,在视频控件上自动调整尺寸。比如摄像机可以在使用过程中动态配置分辨率,当分辨率改动后对应视频控件也要做出同步反应。
  10. 音视频文件无感知自动切换循环播放,不会出现切换期间黑屏等肉眼可见的切换痕迹。
  11. 视频控件同时支持任意解码内核、任意画面显示策略、任意视频显示模式。
  12. 视频控件悬浮条同时支持句柄、绘制、GPU三种模式,非绝对坐标移来移去。
  13. 本地摄像头设备支持指定设备名称、分辨率、帧率进行播放。
  14. 录像文件同时支持打开的视频文件、本地摄像头、网络视频流等。
  15. 瞬间响应打开和关闭,无论是打开不存在的视频或者网络流,探测设备是否存在,读取中的超时等待,收到关闭指令立即中断之前的操作并响应。
  16. 支持打开各种图片文件,支持本地音视频文件拖曳播放。
  17. 视频控件悬浮条自带开始和停止录像切换、声音静音切换、抓拍截图、关闭视频等功能。
  18. 音频组件支持声音波形值数据解析,可以根据该值绘制波形曲线和柱状声音条,默认提供了声音振幅信号。
  19. 各组件中极其详细的打印信息提示,尤其是报错信息提示,封装的统一打印格式。针对现场复杂的设备环境测试极其方便有用,相当于精确定位到具体哪个通道哪个步骤出错。
  20. 代码框架和结构优化到最优,性能强悍,持续迭代更新升级。
  21. 源码支持Qt4、Qt5、Qt6,兼容所有版本。

5.3 视频控件

  1. 可动态添加任意多个osd标签信息,标签信息包括名字、是否可见、字号大小、文本文字、文本颜色、标签图片、标签坐标、标签格式(文本、日期、时间、日期时间、图片)、标签位置(左上角、左下角、右上角、右下角、居中、自定义坐标)。
  2. 可动态添加任意多个图形信息,这个非常有用,比如人工智能算法解析后的图形区域信息直接发给视频控件即可。图形信息支持任意形状,直接绘制在原始图片上,采用绝对坐标。
  3. 图形信息包括名字、边框大小、边框颜色、背景颜色、矩形区域、路径集合、点坐标集合等。
  4. 每个图形信息都可指定三种区域中的一种或者多种,指定了的都会绘制。
  5. 内置悬浮条控件,悬浮条位置支持顶部、底部、左侧、右侧。
  6. 悬浮条控件参数包括边距、间距、背景透明度、背景颜色、文本颜色、按下颜色、位置、按钮图标代码集合、按钮名称标识集合、按钮提示信息集合。
  7. 悬浮条控件一排工具按钮可自定义,通过结构体参数设置,图标可选图形字体还是自定义图片。
  8. 悬浮条按钮内部实现了录像切换、抓拍截图、静音切换、关闭视频等功能,也可以自行在源码中增加自己对应的功能。
  9. 悬浮条按钮对应实现了功能的按钮,有对应图标切换处理,比如录像按钮按下后会切换到正在录像中的图标,声音按钮切换后变成静音图标,再次切换还原。
  10. 悬浮条按钮单击后都用名称唯一标识作为信号发出,可以自行关联响应处理。
  11. 悬浮条空白区域可以显示提示信息,默认显示当前视频分辨率大小,可以增加帧率、码流大小等信息。
  12. 视频控件参数包括边框大小、边框颜色、焦点颜色、背景颜色(默认透明)、文字颜色(默认全局文字颜色)、填充颜色(视频外的空白处填充黑色)、背景文字、背景图片(如果设置了图片优先取图片)、是否拷贝图片、缩放显示模式(自动调整、等比例缩放、拉伸填充)、视频显示模式(句柄、绘制、GPU)、启用悬浮条、悬浮条尺寸(横向为高度、纵向为宽度)、悬浮条位置(顶部、底部、左侧、右侧)。

5.4 内核vlc

  1. 支持各种音视频文件、本地摄像头设备,各种视频流网络流。
  2. 支持开始播放、暂停播放、继续播放、停止播放、设置播放进度、倍速播放。
  3. 可设置音量、静音切换、抓拍图片、录像存储。
  4. 自动提取专辑信息比如标题、艺术家、专辑、专辑封面,自动显示专辑封面。
  5. 事件回调机制响应文件长度变化、播放进度变化、音量大小变化、静音状态变化、分辨率变化等。
  6. 不用重新编译源码,实现句柄模式下响应鼠标键盘信息。
  7. 不用重新编译源码,实现录像文件存储。手动开启录像和停止录像,录像文件名称自动调整为设置的名称。
  8. 句柄模式显示悬浮条和osd标签控件,非绝对坐标move来move去这种垃圾方式。
  9. 支持硬解码any、dxva2、d3d11va等,性能极高尤其是大分辨率比如4K视频。
  10. 同时支持vlc2、vlc3版本,全部做了兼容处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ImportError: Can not find the shared library: libhdfs3.so解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…

WSL2通过OpenCV调用并展示本机摄像头的RTSP视频流

文章目录前言安装 CMake安装 OpenCV 和 FFmpeg启动 Windows 本机的 RTSP 视频流查看本机摄像头设备开始推流开放本机防火墙用 OpenCV 接收视频流结果展示前言 本篇博客的由来如上图哈哈&#xff0c;WSL2 相关安装教程可以参考我之前的博客&#xff1a;Win11安装WSL2和Nvidia驱动…

如果我只有一个奔腾CPU,怎么加速推理神经网络?

前言 有人说当下的AI热潮不过是算力堆砌的产物。现在层出不穷的各种大模型大训练集&#xff0c;使用复杂精致的技术在排行榜上不断刷新分数&#xff0c;这些人似乎忘了一件事情&#xff0c;AI模型最终是要落地的&#xff0c;是要用的&#xff0c;如果不能普及开去那和在象牙塔…

2023年最新软著申请流程(一):软件著作权说明、国家版权官网的账号注册与实名认证

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/129230460 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

如何使用Cliam测试云端环境IAM权限安全

关于Cliam Cliam是一款针对云端安全的测试工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松枚举目标云端环境的IAM权限。当前版本的Cliam支持下列云端环境&#xff1a;AWS、Azure、GCP和Oracle。 Cliam同时也是一个云端权限识别工具&#xff0c;该工具是一…

Mapper代理开发——书接MaBatis的简单使用

在这个mybatis的普通使用中依旧存在硬编码问题,虽然静态语句比原生jdbc都写更少了但是还是要写&#xff0c;Mapper就是用来解决原生方式中的硬编码还有简化后期执行SQL UserMapper是一个接口&#xff0c;里面有很多方法&#xff0c;都是一一和配置文件里面的sql语句的id名称所对…

HEC-HMS和HEC-RAS快速入门、防洪评价报告编制及洪水建模、洪水危险性评价等应用

目录 ①HEC-RAS一维、二维建模方法及实践技术应用 ②HEC-HMS水文模型实践技术应用 ③新导则下的防洪评价报告编制方法及洪水建模实践技术应用 ④基于ArcGIS水文分析、HEC-RAS模拟技术在洪水危险性及风险评估 ⑤山洪径流过程模拟及洪水危险性评价 ①HEC-RAS一维、二维建模方…

Torch同时训练多个模型

20230302 引言 在进行具体的研究时&#xff0c;利用Torch进行编程&#xff0c;考虑到是不是能够同时训练两个模型呢&#xff1f;&#xff01;而且利用其中一个模型的输出来辅助另外一个模型进行学习。这一点&#xff0c;在我看来应该是很简单的&#xff0c;例如GAN网络同时训…

docker安装rabbitmq并挂载

1、拉取镜像 management&#xff1a;表示可以通过web页面管理。 alpine&#xff1a;表示是linux最小版本&#xff0c;不推荐新手安装。 docker pull rabbitmq:management2、创建用于挂载的目录 mkdir -p /mydata/rabbitmq/{data,conf,log} # 创建完成之后要对所创建文件授权…

从菜鸟程序员到高级架构师,竟然是因为这个字final

final实现原理 简介 final关键字&#xff0c;实际的含义就一句话&#xff0c;不可改变。什么是不可改变&#xff1f;就是初始化完成之后就不能再做任何的修改&#xff0c;修饰成员变量的时候&#xff0c;成员变量变成一个常数&#xff1b;修饰方法的时候&#xff0c;方法不允…

23种设计模式之简单工厂模式

一、场景简介 1、引入场景 订餐流程简单描述 食品抽象类,规定食品的基础属性操作 鱼类,鸡肉类食品类扩展 订餐流程类,根据食品名称,加工指定类型食品 模拟客户端预定操作 2、源代码实现 关系图谱 代码实现 /*** 简单工厂模式引入场景*/ public class C01_InScene { p…

【word】论文排版思路

1、 首先把所有中文的字体都按照要求改一下&#xff0c;记住都改成正文的字号和字体&#xff0c;后面再修改标题的&#xff0c;然后再改英文的&#xff0c;不要把顺序弄错了&#xff0c;不然得回头再改标题 然后定位文章里所有英文方法如下&#xff1a; 按CTRLF打开替换对话…

记录--虚拟滚动探索与封装

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 1. 介绍 什么是虚拟滚动&#xff1f;虚拟滚动就是通过js控制大列表中的dom创建与销毁&#xff0c;只创建可视区域dom&#xff0c;非可视区域的dom不创建。这样在渲染大列表中的数据时&#xff0c;只创…

快速生成QR码的方法:教你变成QR Code Master

目录 简介: 具体实现步骤&#xff1a; 一、可以使用Python中的qrcode和tkinter模块来生成QR码。以下是一个简单的例子&#xff0c;演示如何在Tkinter窗口中获取用户输入并使用qrcode生成QR码。 1&#xff09;首先需要安装qrcode模块&#xff0c;可以使用以下命令在终端或命令…

aws batch 理解和使用batch进行批处理计算

文档 Compute Resource Memory ManagementRunning Workload on AWS Batch aws batch 是云上的批处理平台&#xff0c;通过托管环境减少了管理成本。包括配置大量计算资源&#xff0c;更具任务负载优化资源分配。 基本概念 job&#xff08;任务&#xff09;&#xff0c;提交到…

C语言-结构体【详解】

一、 结构体的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量结构的每个成员可以是不同类型的变量 &#xff08;1&#xff09;结构体的声明 写法一&#xff1a; 注&#xff1a; 括号后边的分号不能忘结构体末尾可以不创建变量&#xff0c;在主函数中再创建 struc…

【ChatGPT】sqlachmey 多表连表查询语句

感受下科技带来的魅力&#xff0c;这篇文章是通过ChatGPT自动生成的&#xff0c;不得不说技术强大!!! 在SQLAlchemy中进行多表连接查询可以使用join()方法或join()函数&#xff0c;具体用法如下&#xff1a; join()方法 join()方法可以在SQLAlchemy ORM中的查询中使用。假设…

根据指定函数对DataFrame中各元素进行计算

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】根据指定函数对DataFrame中各元素进行计算以下错误的一项是?import numpy as npimport pandas as pdmyDict{A:[1,2],B:[3,4]}myDfpd.DataFrame(myDict)print(【显示】myDf)print(myDf)print(【…

SMILES标准化方法以及其中的一个坑(手性)

rdkit.Chem.MolToSmiles()方法是用于将RDKit分子对象转换为SMILES字符串的方法。它的参数如下&#xff1a; mol&#xff1a;必需&#xff0c;要转换为SMILES字符串的RDKit分子对象。isomericSmiles&#xff1a;bool类型&#xff0c;是否生成同分异构体SMILES&#xff0c;默认为…

培训班出身的同学简历怎么做?面试要注意哪些?来自资深大厂HR的忠告

目录 1 不少培训班候选人的简历中&#xff0c;缺乏足够的商业项目年限 2 直接描述培训班学习经历会带来的负面影响 3 大龄转行Vs年轻的初级程序员&#xff0c;公司一般会如何选择&#xff1f; 4 经过培训班突击后&#xff0c;可以先面试小公司 5 面试官怎么面试有培训班经历…