1047 lines
38 KiB
Diff
1047 lines
38 KiB
Diff
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
|
|
|