代码拉取完成,页面将自动刷新
同步操作将从 src-openEuler/mpv 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
From 3c5fe76f7c31deaaa7cc211dd29b3db5cb866326 Mon Sep 17 00:00:00 2001
From: peijiankang <peijiankang@kylinos.cn>
Date: Tue, 9 Apr 2024 14:37:35 +0800
Subject: [PATCH] add opengl api for mpv-0.35
---
libmpv/client.h | 53 ++++++
libmpv/mpv.def | 7 +
libmpv/opengl_cb.h | 339 ++++++++++++++++++++++++++++++++++++++
libmpv/qthelper.hpp | 386 ++++++++++++++++++++++++++++++++++++++++++++
player/client.c | 125 ++++++++++++++
player/command.c | 3 +
player/playloop.c | 4 +
wscript_build.py | 2 +-
8 files changed, 918 insertions(+), 1 deletion(-)
create mode 100644 libmpv/opengl_cb.h
create mode 100644 libmpv/qthelper.hpp
diff --git a/libmpv/client.h b/libmpv/client.h
index fb10e5e..afb7510 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -1276,6 +1276,35 @@ typedef enum mpv_event_id {
* start of the program), while the property behaves correctly.
*/
MPV_EVENT_IDLE = 11,
+ /**
+ * Playback was paused. This indicates the user pause state.
+ *
+ * The user pause state is the state the user requested (changed with the
+ * "pause" property). There is an internal pause state too, which is entered
+ * if e.g. the network is too slow (the "core-idle" property generally
+ * indicates whether the core is playing or waiting).
+ *
+ * This event is sent whenever any pause states change, not only the user
+ * state. You might get multiple events in a row while these states change
+ * independently. But the event ID sent always indicates the user pause
+ * state.
+ *
+ * If you don't want to deal with this, use mpv_observe_property() on the
+ * "pause" property and ignore MPV_EVENT_PAUSE/UNPAUSE. Likewise, the
+ * "core-idle" property tells you whether video is actually playing or not.
+ *
+ * @deprecated The event is redundant with mpv_observe_property() as
+ * mentioned above, and might be removed in the far future.
+ */
+ MPV_EVENT_PAUSE = 12,
+ /**
+ * Playback was unpaused. See MPV_EVENT_PAUSE for not so obvious details.
+ *
+ * @deprecated The event is redundant with mpv_observe_property() as
+ * explained in the MPV_EVENT_PAUSE comments, and might be
+ * removed in the far future.
+ */
+ MPV_EVENT_UNPAUSE = 13,
/**
* Sent every time after a video frame is displayed. Note that currently,
* this will be sent in lower frequency if there is no video, or playback
@@ -1877,6 +1906,30 @@ MPV_EXPORT int mpv_hook_continue(mpv_handle *ctx, uint64_t id);
*/
MPV_EXPORT int mpv_get_wakeup_pipe(mpv_handle *ctx);
+/**
+ * @deprecated use render.h
+ */
+typedef enum mpv_sub_api {
+ /**
+ * For using mpv's OpenGL renderer on an external OpenGL context.
+ * mpv_get_sub_api(MPV_SUB_API_OPENGL_CB) returns mpv_opengl_cb_context*.
+ * This context can be used with mpv_opengl_cb_* functions.
+ * Will return NULL if unavailable (if OpenGL support was not compiled in).
+ * See opengl_cb.h for details.
+ *
+ * @deprecated use render.h
+ */
+ MPV_SUB_API_OPENGL_CB = 1
+} mpv_sub_api;
+
+/**
+ * This is used for additional APIs that are not strictly part of the core API.
+ * See the individual mpv_sub_api member values.
+ *
+ * @deprecated use render.h
+ */
+void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api);
+
#endif
#ifdef __cplusplus
diff --git a/libmpv/mpv.def b/libmpv/mpv.def
index 232490d..2b0808d 100644
--- a/libmpv/mpv.def
+++ b/libmpv/mpv.def
@@ -21,6 +21,7 @@ mpv_get_property
mpv_get_property_async
mpv_get_property_osd_string
mpv_get_property_string
+mpv_get_sub_api
mpv_get_time_us
mpv_get_wakeup_pipe
mpv_hook_add
@@ -28,6 +29,12 @@ mpv_hook_continue
mpv_initialize
mpv_load_config_file
mpv_observe_property
+mpv_opengl_cb_draw
+mpv_opengl_cb_init_gl
+mpv_opengl_cb_report_flip
+mpv_opengl_cb_render
+mpv_opengl_cb_set_update_callback
+mpv_opengl_cb_uninit_gl
mpv_render_context_create
mpv_render_context_free
mpv_render_context_get_info
diff --git a/libmpv/opengl_cb.h b/libmpv/opengl_cb.h
new file mode 100644
index 0000000..6820681
--- /dev/null
+++ b/libmpv/opengl_cb.h
@@ -0,0 +1,339 @@
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MPV_CLIENT_API_OPENGL_CB_H_
+#define MPV_CLIENT_API_OPENGL_CB_H_
+
+#include "client.h"
+
+#if !MPV_ENABLE_DEPRECATED
+#error "This header and all API provided by it is deprecated. Use render.h instead."
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *
+ * Overview
+ * --------
+ *
+ * Warning: this API is deprecated. A very similar API is provided by render.h
+ * and render_gl.h. The deprecated API is emulated with the new API.
+ *
+ * This API can be used to make mpv render into a foreign OpenGL context. It
+ * can be used to handle video display.
+ *
+ * The renderer needs to be explicitly initialized with mpv_opengl_cb_init_gl(),
+ * and then video can be drawn with mpv_opengl_cb_draw(). The user thread can
+ * be notified by new frames with mpv_opengl_cb_set_update_callback().
+ *
+ * You can output and embed video without this API by setting the mpv "wid"
+ * option to a native window handle (see "Embedding the video window" section
+ * in the client.h header). In general, using the opengl-cb API is recommended,
+ * because window embedding can cause various issues, especially with GUI
+ * toolkits and certain platforms.
+ *
+ * OpenGL interop
+ * --------------
+ *
+ * This assumes the OpenGL context lives on a certain thread controlled by the
+ * API user. The following functions require access to the OpenGL context:
+ * mpv_opengl_cb_init_gl
+ * mpv_opengl_cb_draw
+ * mpv_opengl_cb_uninit_gl
+ *
+ * The OpenGL context is indirectly accessed through the OpenGL function
+ * pointers returned by the get_proc_address callback in mpv_opengl_cb_init_gl.
+ * Generally, mpv will not load the system OpenGL library when using this API.
+ *
+ * Only "desktop" OpenGL version 2.1 and later and OpenGL ES version 2.0 and
+ * later are supported. With OpenGL 2.1, the GL_ARB_texture_rg is required. The
+ * renderer was written for the OpenGL 3.x core profile, with additional support
+ * for OpenGL 2.1 and OpenGL ES 2.0.
+ *
+ * Note that some hardware decoding interop API (as set with the "hwdec" option)
+ * may actually access some sort of host API, such as EGL.
+ *
+ * OpenGL state
+ * ------------
+ *
+ * OpenGL has a large amount of implicit state. All the mpv functions mentioned
+ * above expect that the OpenGL state is reasonably set to OpenGL standard
+ * defaults. Likewise, mpv will attempt to leave the OpenGL context with
+ * standard defaults. The following state is excluded from this:
+ *
+ * - the glViewport state
+ * - the glScissor state (but GL_SCISSOR_TEST is in its default value)
+ * - glBlendFuncSeparate() state (but GL_BLEND is in its default value)
+ * - glClearColor() state
+ * - mpv may overwrite the callback set with glDebugMessageCallback()
+ * - mpv always disables GL_DITHER at init
+ *
+ * Messing with the state could be avoided by creating shared OpenGL contexts,
+ * but this is avoided for the sake of compatibility and interoperability.
+ *
+ * On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to
+ * create OpenGL objects. You will have to do the same. This ensures that
+ * objects created by mpv and the API users don't clash. Also, legacy state
+ * must be either in its defaults, or not interfere with core state.
+ *
+ * Threading
+ * ---------
+ *
+ * The mpv_opengl_cb_* functions can be called from any thread, under the
+ * following conditions:
+ * - only one of the mpv_opengl_cb_* functions can be called at the same time
+ * (unless they belong to different mpv cores created by mpv_create())
+ * - for functions which need an OpenGL context (see above) the OpenGL context
+ * must be "current" in the current thread, and it must be the same context
+ * as used with mpv_opengl_cb_init_gl()
+ * - never can be called from within the callbacks set with
+ * mpv_set_wakeup_callback() or mpv_opengl_cb_set_update_callback()
+ *
+ * Context and handle lifecycle
+ * ----------------------------
+ *
+ * Video initialization will fail if the OpenGL context was not initialized yet
+ * (with mpv_opengl_cb_init_gl()). Likewise, mpv_opengl_cb_uninit_gl() will
+ * disable video.
+ *
+ * When the mpv core is destroyed (e.g. via mpv_terminate_destroy()), the OpenGL
+ * context must have been uninitialized. If this doesn't happen, undefined
+ * behavior will result.
+ *
+ * Hardware decoding
+ * -----------------
+ *
+ * Hardware decoding via opengl_cb is fully supported, but requires some
+ * additional setup. (At least if direct hardware decoding modes are wanted,
+ * instead of copying back surface data from GPU to CPU RAM.)
+ *
+ * While "normal" mpv loads the OpenGL hardware decoding interop on demand,
+ * this can't be done with opengl_cb for internal technical reasons. Instead,
+ * it loads them by default, even if hardware decoding is not going to be used.
+ * In older mpv releases, this had to be done by setting the
+ * "opengl-hwdec-interop" or "hwdec-preload" options before calling
+ * mpv_opengl_cb_init_gl(). You can still use the newer "gpu-hwdec-interop"
+ * option to prevent loading of interop, or to load only a specific interop.
+ *
+ * There may be certain requirements on the OpenGL implementation:
+ * - Windows: ANGLE is required (although in theory GL/DX interop could be used)
+ * - Intel/Linux: EGL is required, and also a glMPGetNativeDisplay() callback
+ * must be provided (see sections below)
+ * - nVidia/Linux: Both GLX and EGL should work (GLX is required if vdpau is
+ * used, e.g. due to old drivers.)
+ * - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL)
+ * - iOS: EAGL is required (EAGLContext.currentContext returning non-nil)
+ *
+ * Once these things are setup, hardware decoding can be enabled/disabled at
+ * any time by setting the "hwdec" property.
+ *
+ * Special windowing system interop considerations
+ * ------------------------------------------------
+ *
+ * In some cases, libmpv needs to have access to the windowing system's handles.
+ * This can be a pointer to a X11 "Display" for example. Usually this is needed
+ * only for hardware decoding.
+ *
+ * You can communicate these handles to libmpv by adding a pseudo-OpenGL
+ * extension "GL_MP_MPGetNativeDisplay" to the additional extension string when
+ * calling mpv_opengl_cb_init_gl(). The get_proc_address callback should resolve
+ * a function named "glMPGetNativeDisplay", which has the signature:
+ *
+ * void* GLAPIENTRY glMPGetNativeDisplay(const char* name)
+ *
+ * See below what names are defined. Usually, libmpv will use the native handle
+ * up until mpv_opengl_cb_uninit_gl() is called. If the name is not anything
+ * you know/expected, return NULL from the function.
+ */
+
+// Legacy - not supported anymore.
+struct mpv_opengl_cb_window_pos {
+ int x; // left coordinates of window (usually 0)
+ int y; // top coordinates of window (usually 0)
+ int width; // width of GL window
+ int height; // height of GL window
+};
+
+// Legacy - not supported anymore.
+struct mpv_opengl_cb_drm_params {
+ // DRM fd (int). set this to -1 if invalid.
+ int fd;
+
+ // currently used crtc id
+ int crtc_id;
+
+ // currently used connector id
+ int connector_id;
+
+ // pointer to the drmModeAtomicReq that is being used for the renderloop.
+ // This atomic request pointer should be usually created at every renderloop.
+ struct _drmModeAtomicReq *atomic_request;
+};
+
+/**
+ * nVidia/Linux via VDPAU requires GLX, which does not have this problem (the
+ * GLX API can return the current X11 Display).
+ *
+ * Windowing system interop on MS win32
+ * ------------------------------------
+ *
+ * You should use ANGLE, and make sure your application and libmpv are linked
+ * to the same ANGLE DLLs. libmpv will pick the device context (needed for
+ * hardware decoding) from the current ANGLE EGL context.
+ */
+
+/**
+ * Opaque context, returned by mpv_get_sub_api(MPV_SUB_API_OPENGL_CB).
+ *
+ * A context is bound to the mpv_handle it was retrieved from. The context
+ * will always be the same (for the same mpv_handle), and is valid until the
+ * mpv_handle it belongs to is released.
+ */
+typedef struct mpv_opengl_cb_context mpv_opengl_cb_context;
+
+typedef void (*mpv_opengl_cb_update_fn)(void *cb_ctx);
+typedef void *(*mpv_opengl_cb_get_proc_address_fn)(void *fn_ctx, const char *name);
+
+/**
+ * Set the callback that notifies you when a new video frame is available, or
+ * if the video display configuration somehow changed and requires a redraw.
+ * Similar to mpv_set_wakeup_callback(), you must not call any mpv API from
+ * the callback, and all the other listed restrictions apply (such as not
+ * exiting the callback by throwing exceptions).
+ *
+ * @param callback callback(callback_ctx) is called if the frame should be
+ * redrawn
+ * @param callback_ctx opaque argument to the callback
+ */
+void mpv_opengl_cb_set_update_callback(mpv_opengl_cb_context *ctx,
+ mpv_opengl_cb_update_fn callback,
+ void *callback_ctx);
+
+/**
+ * Initialize the mpv OpenGL state. This retrieves OpenGL function pointers via
+ * get_proc_address, and creates OpenGL objects needed by mpv internally. It
+ * will also call APIs needed for rendering hardware decoded video in OpenGL,
+ * according to the mpv "hwdec" option.
+ *
+ * You must free the associated state at some point by calling the
+ * mpv_opengl_cb_uninit_gl() function. Not doing so may result in memory leaks
+ * or worse.
+ *
+ * @param exts optional _additional_ extension string, can be NULL
+ * @param get_proc_address callback used to retrieve function pointers to OpenGL
+ * functions. This is used for both standard functions
+ * and extension functions. (The extension string is
+ * checked whether extensions are really available.)
+ * The callback will be called from this function only
+ * (it is not stored and never used later).
+ * Usually, GL context APIs do this for you (e.g. with
+ * glXGetProcAddressARB or wglGetProcAddress), but
+ * some APIs do not always return pointers for all
+ * standard functions (even if present); in this case
+ * you have to compensate by looking up these functions
+ * yourself.
+ * @param get_proc_address_ctx arbitrary opaque user context passed to the
+ * get_proc_address callback
+ * @return error code (same as normal mpv_* API), including but not limited to:
+ * MPV_ERROR_UNSUPPORTED: the OpenGL version is not supported
+ * (or required extensions are missing)
+ * MPV_ERROR_INVALID_PARAMETER: the OpenGL state was already initialized
+ */
+int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
+ mpv_opengl_cb_get_proc_address_fn get_proc_address,
+ void *get_proc_address_ctx);
+
+/**
+ * Render video. Requires that the OpenGL state is initialized.
+ *
+ * The video will use the full provided framebuffer. Options like "panscan" are
+ * applied to determine which part of the video should be visible and how the
+ * video should be scaled. You can change these options at runtime by using the
+ * mpv property API.
+ *
+ * The renderer will reconfigure itself every time the output rectangle/size
+ * is changed. (If you want to do animations, it might be better to do the
+ * animation on a FBO instead.)
+ *
+ * This function implicitly pulls a video frame from the internal queue and
+ * renders it. If no new frame is available, the previous frame is redrawn.
+ * The update callback set with mpv_opengl_cb_set_update_callback() notifies
+ * you when a new frame was added.
+ *
+ * @param fbo The framebuffer object to render on. Because the renderer might
+ * manage multiple FBOs internally for the purpose of video
+ * postprocessing, it will always bind and unbind FBOs itself. If
+ * you want mpv to render on the main framebuffer, pass 0.
+ * @param w Width of the framebuffer. This is either the video size if the fbo
+ * parameter is 0, or the allocated size of the texture backing the
+ * fbo. The renderer will always use the full size of the fbo.
+ * @param h Height of the framebuffer. Same as with the w parameter, except
+ * that this parameter can be negative. In this case, the video
+ * frame will be rendered flipped.
+ * @return 0
+ */
+int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h);
+
+/**
+ * Deprecated. Use mpv_opengl_cb_draw(). This function is equivalent to:
+ *
+ * int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+ * { return mpv_opengl_cb_draw(ctx, fbo, vp[2], vp[3]); }
+ *
+ * vp[0] and vp[1] used to have a meaning, but are ignored in newer versions.
+ *
+ * This function will be removed in the future without version bump (this API
+ * was never marked as stable).
+ */
+int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]);
+
+/**
+ * Tell the renderer that a frame was flipped at the given time. This is
+ * optional, but can help the player to achieve better timing.
+ *
+ * Note that calling this at least once informs libmpv that you will use this
+ * function. If you use it inconsistently, expect bad video playback.
+ *
+ * If this is called while no video or no OpenGL is initialized, it is ignored.
+ *
+ * @param time The mpv time (using mpv_get_time_us()) at which the flip call
+ * returned. If 0 is passed, mpv_get_time_us() is used instead.
+ * Currently, this parameter is ignored.
+ * @return error code
+ */
+int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time);
+
+/**
+ * Destroy the mpv OpenGL state.
+ *
+ * If video is still active (e.g. a file playing), video will be disabled
+ * forcefully.
+ *
+ * Calling this multiple times is ok.
+ *
+ * @return error code
+ */
+int mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* else #if MPV_ENABLE_DEPRECATED */
+
+#endif
diff --git a/libmpv/qthelper.hpp b/libmpv/qthelper.hpp
new file mode 100644
index 0000000..3af86e3
--- /dev/null
+++ b/libmpv/qthelper.hpp
@@ -0,0 +1,386 @@
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MPV_CLIENT_API_QTHELPER_H_
+#define MPV_CLIENT_API_QTHELPER_H_
+
+#include <mpv/client.h>
+
+#if !MPV_ENABLE_DEPRECATED
+#error "This helper is deprecated. Copy it into your project instead."
+#else
+
+/**
+ * Note: these helpers are provided for convenience for C++/Qt applications.
+ * This is based on the public API in client.h, and it does not encode any
+ * knowledge that is not known or guaranteed outside of the C client API. You
+ * can even copy and modify this code as you like, or implement similar things
+ * for other languages.
+ */
+
+#include <cstring>
+
+#include <QVariant>
+#include <QString>
+#include <QList>
+#include <QHash>
+#include <QSharedPointer>
+#include <QMetaType>
+
+namespace mpv {
+namespace qt {
+
+// Wrapper around mpv_handle. Does refcounting under the hood.
+class Handle
+{
+ struct container {
+ container(mpv_handle *h) : mpv(h) {}
+ ~container() { mpv_terminate_destroy(mpv); }
+ mpv_handle *mpv;
+ };
+ QSharedPointer<container> sptr;
+public:
+ // Construct a new Handle from a raw mpv_handle with refcount 1. If the
+ // last Handle goes out of scope, the mpv_handle will be destroyed with
+ // mpv_terminate_destroy().
+ // Never destroy the mpv_handle manually when using this wrapper. You
+ // will create dangling pointers. Just let the wrapper take care of
+ // destroying the mpv_handle.
+ // Never create multiple wrappers from the same raw mpv_handle; copy the
+ // wrapper instead (that's what it's for).
+ static Handle FromRawHandle(mpv_handle *handle) {
+ Handle h;
+ h.sptr = QSharedPointer<container>(new container(handle));
+ return h;
+ }
+
+ // Return the raw handle; for use with the libmpv C API.
+ operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; }
+};
+
+static inline QVariant node_to_variant(const mpv_node *node)
+{
+ switch (node->format) {
+ case MPV_FORMAT_STRING:
+ return QVariant(QString::fromUtf8(node->u.string));
+ case MPV_FORMAT_FLAG:
+ return QVariant(static_cast<bool>(node->u.flag));
+ case MPV_FORMAT_INT64:
+ return QVariant(static_cast<qlonglong>(node->u.int64));
+ case MPV_FORMAT_DOUBLE:
+ return QVariant(node->u.double_);
+ case MPV_FORMAT_NODE_ARRAY: {
+ mpv_node_list *list = node->u.list;
+ QVariantList qlist;
+ for (int n = 0; n < list->num; n++)
+ qlist.append(node_to_variant(&list->values[n]));
+ return QVariant(qlist);
+ }
+ case MPV_FORMAT_NODE_MAP: {
+ mpv_node_list *list = node->u.list;
+ QVariantMap qmap;
+ for (int n = 0; n < list->num; n++) {
+ qmap.insert(QString::fromUtf8(list->keys[n]),
+ node_to_variant(&list->values[n]));
+ }
+ return QVariant(qmap);
+ }
+ default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
+ return QVariant();
+ }
+}
+
+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;
+ }
+};
+
+/**
+ * RAII wrapper that calls mpv_free_node_contents() on the pointer.
+ */
+struct node_autofree {
+ mpv_node *ptr;
+ node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
+ ~node_autofree() { mpv_free_node_contents(ptr); }
+};
+
+#if MPV_ENABLE_DEPRECATED
+
+/**
+ * Return the given property as mpv_node converted to QVariant, or QVariant()
+ * on error.
+ *
+ * @deprecated use get_property() instead
+ *
+ * @param name the property name
+ */
+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);
+}
+
+/**
+ * Set the given property as mpv_node converted from the QVariant argument.
+
+ * @deprecated use set_property() instead
+ */
+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());
+}
+
+/**
+ * Set the given option as mpv_node converted from the QVariant argument.
+ *
+ * @deprecated use set_property() instead
+ */
+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());
+}
+
+/**
+ * mpv_command_node() equivalent. Returns QVariant() on error (and
+ * unfortunately, the same on success).
+ *
+ * @deprecated use command() instead
+ */
+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);
+}
+
+#endif
+
+/**
+ * This is used to return error codes wrapped in QVariant for functions which
+ * return QVariant.
+ *
+ * You can use get_error() or is_error() to extract the error status from a
+ * QVariant value.
+ */
+struct ErrorReturn
+{
+ /**
+ * enum mpv_error value (or a value outside of it if ABI was extended)
+ */
+ int error;
+
+ ErrorReturn() : error(0) {}
+ explicit ErrorReturn(int err) : error(err) {}
+};
+
+/**
+ * Return the mpv error code packed into a QVariant, or 0 (success) if it's not
+ * an error value.
+ *
+ * @return error code (<0) or success (>=0)
+ */
+static inline int get_error(const QVariant &v)
+{
+ if (!v.canConvert<ErrorReturn>())
+ return 0;
+ return v.value<ErrorReturn>().error;
+}
+
+/**
+ * Return whether the QVariant carries a mpv error code.
+ */
+static inline bool is_error(const QVariant &v)
+{
+ return get_error(v) < 0;
+}
+
+/**
+ * Return the given property as mpv_node converted to QVariant, or QVariant()
+ * on error.
+ *
+ * @param name the property name
+ * @return the property value, or an ErrorReturn with the error code
+ */
+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);
+}
+
+/**
+ * Set the given property as mpv_node converted from the QVariant argument.
+ *
+ * @return mpv error code (<0 on error, >= 0 on success)
+ */
+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());
+}
+
+/**
+ * mpv_command_node() equivalent.
+ *
+ * @param args command arguments, with args[0] being the command name as string
+ * @return the property value, or an ErrorReturn with the error code
+ */
+static inline QVariant command(mpv_handle *ctx, const QVariant &args)
+{
+ node_builder node(args);
+ mpv_node res;
+ int err = mpv_command_node(ctx, node.node(), &res);
+ if (err < 0)
+ return QVariant::fromValue(ErrorReturn(err));
+ node_autofree f(&res);
+ return node_to_variant(&res);
+}
+
+}
+}
+
+Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)
+
+#endif /* else #if MPV_ENABLE_DEPRECATED */
+
+#endif
diff --git a/player/client.c b/player/client.c
index dd81cdf..3f0306d 100644
--- a/player/client.c
+++ b/player/client.c
@@ -83,6 +83,7 @@ struct mp_client_api {
int num_custom_protocols;
struct mpv_render_context *render_context;
+ struct mpv_opengl_cb_context *gl_cb_ctx;
};
struct observe_property {
@@ -2075,6 +2076,8 @@ static const char *const event_table[] = {
[MPV_EVENT_END_FILE] = "end-file",
[MPV_EVENT_FILE_LOADED] = "file-loaded",
[MPV_EVENT_IDLE] = "idle",
+ [MPV_EVENT_PAUSE] = "pause",
+ [MPV_EVENT_UNPAUSE] = "unpause",
[MPV_EVENT_TICK] = "tick",
[MPV_EVENT_CLIENT_MESSAGE] = "client-message",
[MPV_EVENT_VIDEO_RECONFIG] = "video-reconfig",
@@ -2153,6 +2156,128 @@ mp_client_api_acquire_render_context(struct mp_client_api *ca)
return res;
}
+// Emulation of old opengl_cb API.
+
+#include "libmpv/opengl_cb.h"
+#include "libmpv/render_gl.h"
+
+struct mpv_opengl_cb_context {
+ struct mp_client_api *client_api;
+ mpv_opengl_cb_update_fn callback;
+ void *callback_ctx;
+};
+
+static mpv_opengl_cb_context *opengl_cb_get_context(mpv_handle *ctx)
+{
+ pthread_mutex_lock(&ctx->clients->lock);
+ mpv_opengl_cb_context *cb = ctx->clients->gl_cb_ctx;
+ if (!cb) {
+ cb = talloc_zero(NULL, struct mpv_opengl_cb_context);
+ cb->client_api = ctx->clients;
+ cb->client_api->gl_cb_ctx = cb;
+ }
+ pthread_mutex_unlock(&ctx->clients->lock);
+ return cb;
+}
+
+void mpv_opengl_cb_set_update_callback(mpv_opengl_cb_context *ctx,
+ mpv_opengl_cb_update_fn callback,
+ void *callback_ctx)
+{
+ // This was probably supposed to be thread-safe, but we don't care. It's
+ // compatibility code, and if you have problems, use the new API.
+ if (ctx->client_api->render_context) {
+ mpv_render_context_set_update_callback(ctx->client_api->render_context,
+ callback, callback_ctx);
+ }
+ // Nasty thing: could set this even while not initialized, so we need to
+ // preserve it.
+ ctx->callback = callback;
+ ctx->callback_ctx = callback_ctx;
+}
+
+int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
+ mpv_opengl_cb_get_proc_address_fn get_proc_address,
+ void *get_proc_address_ctx)
+{
+ if (ctx->client_api->render_context)
+ return MPV_ERROR_INVALID_PARAMETER;
+
+ // mpv_render_context_create() only calls mp_client_get_global() on it.
+ mpv_handle dummy = {.mpctx = ctx->client_api->mpctx};
+
+ mpv_render_param params[] = {
+ {MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL},
+ {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &(mpv_opengl_init_params){
+ .get_proc_address = get_proc_address,
+ .get_proc_address_ctx = get_proc_address_ctx,
+ }},
+ // Hack for explicit legacy hwdec loading. We really want to make it
+ // impossible for proper render API users to trigger this.
+ {(mpv_render_param_type)-1, ctx->client_api->mpctx->global},
+ {0}
+ };
+ int err = mpv_render_context_create(&ctx->client_api->render_context,
+ &dummy, params);
+ if (err >= 0) {
+ mpv_render_context_set_update_callback(ctx->client_api->render_context,
+ ctx->callback, ctx->callback_ctx);
+ }
+ return err;
+}
+
+int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h)
+{
+ if (!ctx->client_api->render_context)
+ return MPV_ERROR_INVALID_PARAMETER;
+
+ mpv_render_param params[] = {
+ {MPV_RENDER_PARAM_OPENGL_FBO, &(mpv_opengl_fbo){
+ .fbo = fbo,
+ .w = w,
+ .h = abs(h),
+ }},
+ {MPV_RENDER_PARAM_FLIP_Y, &(int){h < 0}},
+ {0}
+ };
+ return mpv_render_context_render(ctx->client_api->render_context, params);
+}
+
+int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
+{
+ if (!ctx->client_api->render_context)
+ return MPV_ERROR_INVALID_PARAMETER;
+
+ mpv_render_context_report_swap(ctx->client_api->render_context);
+ return 0;
+}
+
+int mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context *ctx)
+{
+ if (ctx->client_api->render_context)
+ mpv_render_context_free(ctx->client_api->render_context);
+ ctx->client_api->render_context = NULL;
+ return 0;
+}
+
+int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+{
+ return mpv_opengl_cb_draw(ctx, fbo, vp[2], vp[3]);
+}
+
+void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api)
+{
+ if (!ctx->mpctx->initialized)
+ return NULL;
+ void *res = NULL;
+ switch (sub_api) {
+ case MPV_SUB_API_OPENGL_CB:
+ res = opengl_cb_get_context(ctx);
+ break;
+ default:;
+ }
+ return res;
+}
// stream_cb
struct mp_custom_protocol {
diff --git a/player/command.c b/player/command.c
index 2e6b987..cf71b9d 100644
--- a/player/command.c
+++ b/player/command.c
@@ -6606,6 +6606,9 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
if (co)
mp_notify_property(mpctx, co->name);
+ if (opt_ptr == &opts->pause)
+ mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
+
if (self_update)
return;
diff --git a/player/playloop.c b/player/playloop.c
index 91badf0..27b5da6 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -180,6 +180,10 @@ void set_pause_state(struct MPContext *mpctx, bool user_pause)
} else {
(void)get_relative_time(mpctx); // ignore time that passed during pause
}
+
+ // For some reason, these events are supposed to be sent even if only
+ // the internal pause state changed (and "pause" property didn't)... OK.
+ mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
}
update_core_idle_state(mpctx);
diff --git a/wscript_build.py b/wscript_build.py
index 16c8cf0..3235afb 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -737,7 +737,7 @@ def build(ctx):
PRIV_LIBS = get_deps(),
)
- headers = ["client.h", "render.h",
+ headers = ["client.h", "qthelper.hpp", "opengl_cb.h", "render.h",
"render_gl.h", "stream_cb.h"]
for f in headers:
ctx.install_as(ctx.env.INCLUDEDIR + '/mpv/' + f, 'libmpv/' + f)
--
2.41.0
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。