• Qt音视频开发16-mpv通用接口


    一、前言

    前面几篇文章,依次讲了解码播放、录像存储、读取和控制、事件订阅等,其实这些功能的实现都离不开封装的通用的接口,最开始本人去调用一些设置的时候,发现多参数的不好实现,原来需要用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());
    }
    
  • 相关阅读:
    链家网各城市二手房价格
    mitmproxy 配置
    Python操作APP -- Appium-Python-Client
    Appium连接模拟器
    adb server version (xx) doesn't match this client (xx); killing...
    Appnium 环境搭建
    KeyError: 'xxx does not support field: _id'
    Python执行JS -- PyExecJS库
    Python -- CSV文件读写
    Git的基本使用 -- 分支管理
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13553177.html
Copyright © 2020-2023  润新知