返回

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

主要接口如下:

  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等硬解码。
效果图
核心代码
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(v.type()) == static_cast(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 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 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());
}

相关知识