Qt audio and video development 16-mpv universal interface

I. Introduction

The previous articles talked about decoding and playback, video storage, reading and control, and event subscription in turn. In fact, the realization of these functions is inseparable from the encapsulated universal interface. When I first called some settings, I found that The parameter is not easy to implement, originally it needs to be processed with mpv_node, and how to convert to mpv_node in Qt requires special processing. Later, I saw the official demo example on the open source homepage, and directly encapsulated multiple interfaces with qt (https: //github.com/mpv-player/mpv-examples/tree/master/libmpv), see the comments inside are in English, it is estimated that they should be officially provided, the parameters passed in are all supporting QVariant, so compatibility is It's super powerful. Many different types of data parameters can be passed in. Thank you again for the official demo. In addition to the QWidget version, the official demo also has a qml version. It also provides an opengl version. If you are interested, you can down Let's take a look, but the demo is relatively simple. It doesn't demonstrate all the functions. It only demonstrates the most basic functions such as the playback video progress control, which is far from a complete video player.

The main interfaces are as follows:

  1. General get property interface function get_property_variant
  2. General setting property interface function set_property_variant
  3. Universal setting parameter interface function set_option_variant
  4. General execution command interface function command_variant

2. Features

  1. Multi-threaded real-time playing video stream + local video, etc.
  2. Support windows+linux+mac.
  3. Multi-threaded display images, not stuck in the main interface.
  4. Reconnect the webcam automatically.
  5. Can set whether to save to file and file name.
  6. You can drag files directly to the mpvwidget control to play.
  7. Support common video streams such as h265 video stream + rtmp.
  8. Can pause and resume playing.
  9. Support storage of single video files and timing storage of video files.
  10. Customize the top floating bar, send a click signal notification, and set whether to enable it.
  11. You can set the screen stretch fill or equal proportion fill.
  12. Can take screenshots (original pictures) and screenshots of videos.
  13. The video file stores MP4 files.
  14. Supports hard decoding such as qsv, dxva2, d3d11va, etc.

Three, renderings

Insert picture description here

Four, related sites

  1. Domestic site: https://gitee.com/feiyangqingyun/QWidgetDemo
  2. International site: https://github.com/feiyangqingyun/QWidgetDemo
  3. Personal homepage: https://blog.csdn.net/feiyangqingyun
  4. Zhihu Homepage: https://www.zhihu.com/people/feiyangqingyun/
  5. Experience address: https://blog.csdn.net/feiyangqingyun/article/details/97565652

Five, the core code

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());
}

Guess you like

Origin blog.csdn.net/feiyangqingyun/article/details/108196946