Qt音视频开发15-mpv事件订阅
2023-09-11 14:18:39 时间
一、前言
在使用libmpv的过程中,通过对mpv事件订阅,可以更准确和准时的得知一些事件,比如文件打开成功,播放状态的改变等,而不需要定时器去读取状态,尤其是打开成功这个事件,如果不采用事件订阅,有时候视频流会卡主一阵子,比如不存在的视频流或者网络不好的情况下,有两种办法可以规避这个情况,在vlc和ffmpeg解码中也是如此,一种方法是将这个打开直接放到线程中执行,本来解码处理就是一个完整的线程类,所以直接通过标志位的更改来在线程中执行初始化,毫无压力不卡主,还有一个办法就是采用事件回调,得到打开成功以后,再去执行其他的处理比如读取视频的宽度高度等信息,这些信息一般都是需要打开文件成功以后才能读取到的。
毫无疑问mpv也支持事件订阅,通过mpv_observe_property函数将需要订阅的属性事件更改加入事件订阅队列,这个函数有四个参数,第一个参数指mpv对象(通过mpv_create产生的),第二个参数指用户数据,如果不需要的话直接填0,一般都不需要,第三个参数指属性名称,至于属性名称是啥叫啥,可以官网查阅手册(http://mpv.io/manual/master/#properties),第四个参数指属性的格式类型。一般来说都会对这几个属性事件的更改订阅:duration(文件长度)、time-pos(当前播放进度)。事件订阅好以后执行mpv_set_wakeup_callback函数设置事件回调函数处理即可。
二、功能特点
- 多线程实时播放视频流+本地视频等。
- 支持windows+linux+mac。
- 多线程显示图像,不卡主界面。
- 自动重连网络摄像头。
- 可设置是否保存到文件以及文件名。
- 可直接拖曳文件到mpvwidget控件播放。
- 支持h265视频流+rtmp等常见视频流。
- 可暂停播放和继续播放。
- 支持存储单个视频文件和定时存储视频文件。
- 自定义顶部悬浮条,发送单击信号通知,可设置是否启用。
- 可设置画面拉伸填充或者等比例填充。
- 可对视频进行截图(原始图片)和截屏。
- 录像文件存储MP4文件。
- 支持qsv、dxva2、d3d11va等硬解码。
三、效果图
四、相关站点
- 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
- 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
- 个人主页:https://blog.csdn.net/feiyangqingyun
- 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
- 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652
五、核心代码
//事件处理
static void handleEvents(mpv_handle *mpvPlayer, mpv_event *event, MpvThread *thread)
{
switch (event->event_id) {
case MPV_EVENT_PROPERTY_CHANGE: {
mpv_event_property *property = (mpv_event_property *)event->data;
QString propertyName = property->name;
mpv_format propertyFormat = property->format;
if (propertyName == "duration") {
//文件总长度
if (propertyFormat == MPV_FORMAT_DOUBLE) {
double time = *(double *)property->data;
uint length = time * 1000;
thread->doEvent(1, QVariantList() << length);
qDebug() << TIMEMS << "文件总长: " << length;
}
} else if (propertyName == "time-pos") {
//当前播放进度
if (propertyFormat == MPV_FORMAT_DOUBLE) {
double time = *(double *)property->data;
uint position = time * 1000;
thread->doEvent(2, QVariantList() << position);
//qDebug() << TIMEMS << "当前时间: " << position;
}
} else {
qDebug() << TIMEMS << propertyName;
if (propertyFormat == MPV_FORMAT_NODE) {
#if 0
QVariant data = qtmpv::node_to_variant((mpv_node *)property->data);
QJsonDocument json = QJsonDocument::fromVariant(data);
qDebug() << TIMEMS << json.toJson().data();
#endif
}
}
break;
}
//获取宽高
case MPV_EVENT_VIDEO_RECONFIG: {
//这里会执行两次,不知道为什么
int dwidth, dheight;
mpv_get_property(mpvPlayer, "dwidth", MPV_FORMAT_INT64, &dwidth);
mpv_get_property(mpvPlayer, "dheight", MPV_FORMAT_INT64, &dheight);
qDebug() << TIMEMS << "dwidth:" << dwidth << "dheight:" << dheight;
break;
}
//打印日志
case MPV_EVENT_LOG_MESSAGE: {
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
QString data = QString("[%1] %2: %3").arg(msg->prefix).arg(msg->level).arg(msg->text);
data.replace("\r", "");
data.replace("\n", "");
//qDebug() << TIMEMS << data;
break;
}
//文件播放结束
case MPV_EVENT_END_FILE: {
thread->doEvent(0, QVariantList());
break;
}
default:
break;
}
}
//订阅事件
static void attachEvents(mpv_handle *mpvPlayer)
{
mpv_observe_property(mpvPlayer, 0, "duration", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpvPlayer, 0, "time-pos", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpvPlayer, 0, "track-list", MPV_FORMAT_NODE);
mpv_observe_property(mpvPlayer, 0, "chapter-list", MPV_FORMAT_NODE);
}
相关文章
- Qt 一张图片显示在其他组件之上
- 在新机器部署Qt+mysql程序
- Qt使用事件过滤器时程序崩溃解原因及决办法(Qt中任意对象的事件过滤器都能将某一事件截住过滤掉)
- QT中QThread的各个方法,UI线程关系,事件关系详解(5)
- QT的事件分发、事件过滤器详解
- Qt-事件循环概念及循环函数详解
- C++中运行一个程序的内存分配情况及qt中的内存管理机制
- Qt中事件循环机制详解
- QT中父子窗口事件传递与事件过滤器
- 【QT】Qt 中 Json 的操作
- 【QT】测试Qt是否能连接上数据库
- Qt编写自定义控件4-旋转仪表盘
- Qt编写输入法终极版V2018
- Qt 6中的输入事件
- QT in VS 多语言实现(中英文切换,每个步骤都有截图,只有UTF8才能让Qt语言家正确读取。先qApp->removeTranslator,然后installTranslator,每个类都要写上槽函数RetranslateUI)
- Qt之图形(QPainter的基本绘图)
- Qt之国际化
- 从Qt到PyQt
- Qt编程之对QGraphicsItem点击右键弹出菜单
- VS2010使用静态编译的qt库(Qt 5)
- IAC-IMX8MM-Kit开发板qt远程调试环境搭建
- QT源码之Qt信号槽机制与事件机制的联系
- Qt QMediaPlayer(铺满整个窗口)