diff --git a/0001-add-opengl-api-for-mpv-0.35.patch b/0001-add-opengl-api-for-mpv-0.35.patch new file mode 100644 index 0000000000000000000000000000000000000000..cbf3fb2e7aaa426366d418d9131e28d6736246d5 --- /dev/null +++ b/0001-add-opengl-api-for-mpv-0.35.patch @@ -0,0 +1,1046 @@ +From 3c5fe76f7c31deaaa7cc211dd29b3db5cb866326 Mon Sep 17 00:00:00 2001 +From: peijiankang +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 ++ ++#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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 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(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(node->u.flag)); ++ case MPV_FORMAT_INT64: ++ return QVariant(static_cast(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(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; ++ } ++}; ++ ++/** ++ * 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()) ++ return 0; ++ return v.value().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 + diff --git a/mpv.spec b/mpv.spec index f423fb50a37578a3203fd0ec3d8d570fd01585f3..8f28a36430e002f89812e370b29955534a0a97e6 100644 --- a/mpv.spec +++ b/mpv.spec @@ -1,10 +1,11 @@ Name: mpv Version: 0.35.1 -Release: 1 +Release: 2 Summary: Movie player playing most video formats and DVDs License: GPL-2.0-or-later AND LGPL-2.1-or-later URL: http://mpv.io/ Source0: https://github.com/mpv-player/mpv/archive/v%{version}/%{name}-%{version}.tar.gz +Patch01: 0001-add-opengl-api-for-mpv-0.35.patch BuildRequires: pkgconfig(alsa) BuildRequires: desktop-file-utils @@ -160,6 +161,9 @@ install -Dpm 644 README.md etc/input.conf etc/mpv.conf -t %{buildroot}%{_docdir} %{_libdir}/pkgconfig/mpv.pc %changelog +* Wed Apr 10 2024 peijiankang - 0.35.1-2 +- add 0001-add-opengl-api-for-mpv.patch + * Mon Feb 27 2023 jchzhou - 0.35.1-1 - Update to 0.35.1 - Enabled VA-API support