一、前言

前面几篇文章,依次讲了解码播放、录像存储、读取和控制、事件订阅等,其实这些功能的实现都离不开封装的通用的接口,最开始本人去调用一些设置的时候,发现多参数的不好实现,原来需要用mpv_node处理,而Qt中如何转成mpv_node需要特殊的处理才行,后来在开源主页看到了官方提供的demo例子,直接用qt封装好了多个接口(https://github.com/mpv-player/mpv-examples/tree/master/libmpv),看里面的注释是英文的,估计应该是官方提供的,传入的参数都是支持QVariant的,这样兼容性就超级强大了,多种不同类型的数据参数都可以传入进去,再次感谢官方的demo,官方的demo除了有QWidget的外还有qml的版本,同时还提供了opengl版本,各位有兴趣都可以down下来看看,不过demo比较简单就是,并没有演示所有的功能,只演示了最基础的功能比如播放视频进度控制等,离一个完整的视频播放器差十万八千里不止。

主要接口如下:

  1. 通用获取属性接口函数 get_property_variant
  2. 通用设置属性接口函数 set_property_variant
  3. 通用设置参数接口函数 set_option_variant
  4. 通用执行命令接口函数 command_variant

二、功能特点

  1. 多线程实时播放视频流+本地视频等。
  2. 支持windows+linux+mac。
  3. 多线程显示图像,不卡主界面。
  4. 自动重连网络摄像头。
  5. 可设置是否保存到文件以及文件名。
  6. 可直接拖曳文件到mpvwidget控件播放。
  7. 支持h265视频流+rtmp等常见视频流。
  8. 可暂停播放和继续播放。
  9. 支持存储单个视频文件和定时存储视频文件。
  10. 自定义顶部悬浮条,发送单击信号通知,可设置是否启用。
  11. 可设置画面拉伸填充或者等比例填充。
  12. 可对视频进行截图(原始图片)和截屏。
  13. 录像文件存储MP4文件。
  14. 支持qsv、dxva2、d3d11va等硬解码。

三、效果图

四、相关站点

  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

五、核心代码

struct node_builder {
node_builder(const QVariant &v) {
set(&node_, v);
}
~node_builder() {
free_node(&node_);
}
mpv_node *node() {
return &node_;
}
private:
Q_DISABLE_COPY(node_builder)
mpv_node node_;
mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) {
dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY;
mpv_node_list *list = new mpv_node_list();
dst->u.list = list;
if (!list) {
goto err;
}
list->values = new mpv_node[num]();
if (!list->values) {
goto err;
}
if (is_map) {
list->keys = new char *[num]();
if (!list->keys) {
goto err;
}
}
return list;
err:
free_node(dst);
return NULL;
}
char *dup_qstring(const QString &s) {
QByteArray b = s.toUtf8();
char *r = new char[b.size() + 1];
if (r) {
std::memcpy(r, b.data(), b.size() + 1);
}
return r;
}
bool test_type(const QVariant &v, QMetaType::Type t) {
// The Qt docs say: "Although this function is declared as returning
// "QVariant::Type(obsolete), the return value should be interpreted
// as QMetaType::Type."
// So a cast really seems to be needed to avoid warnings (urgh).
return static_cast<int>(v.type()) == static_cast<int>(t);
}
void set(mpv_node *dst, const QVariant &src) {
if (test_type(src, QMetaType::QString)) {
dst->format = MPV_FORMAT_STRING;
dst->u.string = dup_qstring(src.toString());
if (!dst->u.string) {
goto fail;
}
} else if (test_type(src, QMetaType::Bool)) {
dst->format = MPV_FORMAT_FLAG;
dst->u.flag = src.toBool() ? 1 : 0;
} else if (test_type(src, QMetaType::Int) ||
test_type(src, QMetaType::LongLong) ||
test_type(src, QMetaType::UInt) ||
test_type(src, QMetaType::ULongLong)) {
dst->format = MPV_FORMAT_INT64;
dst->u.int64 = src.toLongLong();
} else if (test_type(src, QMetaType::Double)) {
dst->format = MPV_FORMAT_DOUBLE;
dst->u.double_ = src.toDouble();
} else if (src.canConvert<QVariantList>()) {
QVariantList qlist = src.toList();
mpv_node_list *list = create_list(dst, false, qlist.size());
if (!list) {
goto fail;
}
list->num = qlist.size();
for (int n = 0; n < qlist.size(); n++) {
set(&list->values[n], qlist[n]);
}
} else if (src.canConvert<QVariantMap>()) {
QVariantMap qmap = src.toMap();
mpv_node_list *list = create_list(dst, true, qmap.size());
if (!list) {
goto fail;
}
list->num = qmap.size();
for (int n = 0; n < qmap.size(); n++) {
list->keys[n] = dup_qstring(qmap.keys()[n]);
if (!list->keys[n]) {
free_node(dst);
goto fail;
}
set(&list->values[n], qmap.values()[n]);
}
} else {
goto fail;
}
return;
fail:
dst->format = MPV_FORMAT_NONE;
}
void free_node(mpv_node *dst) {
switch (dst->format) {
case MPV_FORMAT_STRING:
delete[] dst->u.string;
break;
case MPV_FORMAT_NODE_ARRAY:
case MPV_FORMAT_NODE_MAP: {
mpv_node_list *list = dst->u.list;
if (list) {
for (int n = 0; n < list->num; n++) {
if (list->keys) {
delete[] list->keys[n];
}
if (list->values) {
free_node(&list->values[n]);
}
}
delete[] list->keys;
delete[] list->values;
}
delete list;
break;
}
default:
;
}
dst->format = MPV_FORMAT_NONE;
}
}; struct node_autofree {
mpv_node *ptr;
node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
~node_autofree() {
mpv_free_node_contents(ptr);
}
}; static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
{
mpv_node node;
if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) {
return QVariant();
}
node_autofree f(&node);
return node_to_variant(&node);
} static inline int set_property_variant(mpv_handle *ctx, const QString &name,
const QVariant &v)
{
node_builder node(v);
return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
} static inline int set_option_variant(mpv_handle *ctx, const QString &name,
const QVariant &v)
{
node_builder node(v);
return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
} static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
{
node_builder node(args);
mpv_node res;
if (mpv_command_node(ctx, node.node(), &res) < 0) {
return QVariant();
}
node_autofree f(&res);
return node_to_variant(&res);
} static inline QVariant get_property(mpv_handle *ctx, const QString &name)
{
mpv_node node;
int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
if (err < 0) {
return QVariant::fromValue(ErrorReturn(err));
}
node_autofree f(&node);
return node_to_variant(&node);
} static inline int set_property(mpv_handle *ctx, const QString &name,
const QVariant &v)
{
node_builder node(v);
return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
}

Qt音视频开发16-mpv通用接口的更多相关文章

  1. WebRTC 音视频开发

    WebRTC 音视频开发 webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译 ...

  2. 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)

    随笔分类 - webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...

  3. Android开发 音视频开发需要了解的专业术语知识

    前言 在摸索一段时间的音视频开发后,越来越发现这个坑的深度真是特别的深. 除了了解Android自带的音视频处理API以外,还得了解一些视频与音频方面的知识.这篇博客就是主要讲解这方面的专业术语.内容 ...

  4. moviepy音视频开发:音频剪辑基类AudioClip

    ☞ ░ 前往老猿Python博文目录 ░ 一.背景知识介绍 1.1.声音三要素: 音调:人耳对声音高低的感觉称为音调(也叫音频).音调主要与声波的频率有关.声波的频率高,则音调也高. 音量:也就是响度 ...

  5. 【秒懂音视频开发】02_Windows开发环境搭建

    音视频开发库的选择 每个主流平台基本都有自己的音视频开发库(API),用以处理音视频数据,比如: iOS:AVFoundation.AudioUnit等 Android:MediaPlayer.Med ...

  6. Android 音视频开发学习思路

    Android 音视频开发这块目前的确没有比较系统的教程或者书籍,网上的博客文章也都是比较零散的.只能通过一点点的学习和积累把这块的知识串联积累起来. 初级入门篇: Android 音视频开发(一) ...

  7. 音视频开发-FFmpeg

    音视频开发是个非常复杂的,庞大的开发话题,初涉其中,先看一下结合 OEIP(开源项目) 新增例子. 可以打开flv,mp4类型文件,以及rtmp协议音视频数据,声音的播放使用SDL. 把采集的麦/声卡 ...

  8. Python音视频开发:消除抖音短视频Logo的图形化工具实现

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解>节介绍了怎么通过Python+Moviepy+OpenCV实现 ...

  9. Python音视频开发:消除抖音短视频Logo和去电视台标

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...

  10. Moviepy音视频开发:视频转gif动画或jpg图片exe图形化工具开发案例

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 老猿之所以学习和研究Moviepy的使用,是因为需要一个将视频转成动画的工具,当时在网上到处搜索查找免费使用工具,结果找了很多自称免费的工具,但转完 ...

随机推荐

  1. 【ZROJ2730】简单题 可持久化分块题解

    Description 给定一棵 \(n\) 个节点的树,每次询问编号为 \([l, r]\) 的点中有多少个是祖先关系. \(n, q \le 10^5\). Solution 直接做的话树上的祖先 ...

  2. Oracle官方自动推荐大内存页脚本hugepages.sh

    #!/bin/bash # # hugepages_settings.sh # # Linux bash script to compute values for the # recommended ...

  3. 云原生周刊:目前的 Kubernetes 开源监控方案有没有缺陷?

    视频推荐 目前的 Kubernetes 开源监控方案有没有缺陷? YouTube 频道 OpenObservability Talks 最新一期视频邀请了 VictoriaMetrics 项目的创始人 ...

  4. python之图片与视频互转

    图片转视频 def image_to_video(image_dir, video_dir, fps): im_list = [i for i in os.listdir(image_dir) if ...

  5. Plain-Det:同时支持多数据集训练的新目标检测 | ECCV'24

    近期在大规模基础模型上的进展引发了对训练高效大型视觉模型的广泛关注.一个普遍的共识是必须聚合大量高质量的带注释数据.然而,鉴于计算机视觉中密集任务(如目标检测和分割)标注的固有挑战,实际的策略是结合并 ...

  6. linux环境nginx配置记录

    nginx环境安装 1.联网下载 pcre压缩包 解压压缩文件使用命令 tar –xvf pcre-8.37.tar.gz ./configure 完成后,回到 pcre 目录下执行 make,最后执 ...

  7. Java常见面试真题之中级进阶(List篇)

    前言 本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!获取一个类Class对象的方式有哪些?ArrayList 和 LinkedList 的区别有哪些?用过 ArrayList 吗?说一下它有 ...

  8. selenium3环境搭建,Firefox与对应的geckodriver, chrome与对应的Chromedriver

    Firefox与对应的geckodriver 火狐下载:http://ftp.mozilla.org/pub/firefox/releases/ geckodriver下载:https://githu ...

  9. pdf.js使用

    百度上很多例子,都是构建之前的! 我们使用pdf.js,最终只需要构建后的内容,大家可以通过这里进行下载: https://pan.baidu.com/s/14J-m-jeHdvn46cPhPXk54 ...

  10. downloadFile:base64数据导出文件,文件下载

    function downloadFile(filename, data){ let DownloadLink = document.createElement('a'); if ( Download ...