fix(native): bound cross-platform lifecycle ownership

This commit is contained in:
edde746
2026-07-24 03:56:40 +02:00
parent 09656fa4d3
commit 9f2e050797
98 changed files with 12908 additions and 1599 deletions
+59 -6
View File
@@ -32,6 +32,45 @@ using EventCallback = std::function<void(::_FlValue*)>;
/// Callback for requesting a redraw (called from mpv render update thread).
using RedrawCallback = std::function<void()>;
// Linux-runner-internal teardown boundary. A render context may only be
// released while its EGL context is current; the batch retains the shared mpv
// handle until every render/context pair has been safely released.
struct NativeRenderTeardownResource {
mpv_render_context* render = nullptr;
EGLDisplay display = EGL_NO_DISPLAY;
EGLContext context = EGL_NO_CONTEXT;
};
struct NativeRenderTeardownBatch {
std::vector<NativeRenderTeardownResource> resources;
mpv_handle* handle = nullptr;
std::shared_ptr<void> callback_keep_alive;
};
struct NativeRenderTeardownOperations {
std::function<bool(EGLDisplay, EGLContext)> make_current;
std::function<bool(EGLDisplay)> release_current;
std::function<bool(EGLDisplay, EGLContext)> destroy_context;
std::function<void(mpv_render_context*)> free_render;
std::function<void(mpv_handle*)> terminate_handle;
};
// Attempts one teardown pass. Failed resources remain owned by |batch| for a
// later retry, and |handle| is never terminated while any resource remains.
bool TryReleaseNativeRenderTeardown(NativeRenderTeardownBatch& batch, const NativeRenderTeardownOperations& operations);
// Releases render contexts retained by a failed initialization attempt. A
// false result must block another render-context creation on the same core.
bool TryReleaseRetainedNativeRenderContexts(
std::vector<NativeRenderTeardownResource>& resources, const NativeRenderTeardownOperations& operations);
#ifdef PLEZY_MPV_PLAYER_LIFECYCLE_TEST
// Focused-test boundary for exercising the process-lifetime teardown queue
// without invoking real EGL or libmpv resources.
void ConfigureNativeRenderTeardownQueueForTesting(NativeRenderTeardownOperations operations);
void EnqueueNativeRenderTeardownForTesting(NativeRenderTeardownBatch batch);
#endif
/// Wrapper for libmpv that handles initialization, OpenGL rendering,
/// commands, properties, and event dispatching.
class MpvPlayer {
@@ -55,26 +94,26 @@ class MpvPlayer {
bool InitRenderContext();
/// Returns true if the render context has been created.
bool HasRenderContext() const { return mpv_gl_ != nullptr; }
bool HasRenderContext() const;
/// Returns the isolated EGL display used for mpv rendering.
EGLDisplay GetEglDisplay() const { return egl_display_; }
EGLDisplay GetEglDisplay() const;
/// Returns the isolated EGL context used for mpv rendering.
EGLContext GetEglContext() const { return egl_context_; }
EGLContext GetEglContext() const;
/// Disposes mpv and releases resources.
void Dispose();
/// Returns true if mpv is initialized (has both mpv handle and render
/// context; audio-only players never have a render context).
bool IsInitialized() const { return mpv_ != nullptr && (audio_only_ || mpv_gl_ != nullptr); }
bool IsInitialized() const;
/// Returns true if this player has been disposed.
bool IsDisposed() const { return disposed_.load(); }
/// Returns true if mpv handle exists (even without render context).
bool HasMpvHandle() const { return mpv_ != nullptr; }
bool HasMpvHandle() const;
/// Queues an mpv command without waiting for completion.
void Command(const std::vector<std::string>& args);
@@ -120,6 +159,10 @@ class MpvPlayer {
/// Sets the MPV log message level (e.g., "warn", "v", "debug").
void SetLogLevel(const std::string& level);
/// Retries process-owned native teardown work on the managed EGL teardown
/// thread. Primarily useful before creating another render context.
static void RetryPendingNativeTeardown();
private:
class CallbackContext {
public:
@@ -149,6 +192,7 @@ class MpvPlayer {
Lease Acquire();
void DetachAndWait();
void WaitUntilDetached();
GMainContext* main_context() const { return main_context_; }
private:
@@ -193,13 +237,20 @@ class MpvPlayer {
/// Sends an event notification.
void SendEvent(const std::string& name, ::_FlValue* data = nullptr);
void MaybeRunAudioRecovery();
void TryAudioReload(const char* reason, int attempt);
void TryAudioReload(const char* reason, int attempt, uint64_t request_generation);
void EnsureAudioRecoveryTimer();
void LogRecovery(const std::string& text);
void SetHDREnabled(bool enabled, StatusCallback callback = nullptr);
struct NodeConversionBudget {
size_t remaining_entries;
size_t remaining_bytes;
};
/// Helper to convert mpv_node to FlValue.
::_FlValue* NodeToFlValue(mpv_node* node);
::_FlValue* NodeToFlValue(mpv_node* node, size_t depth, NodeConversionBudget* budget);
bool ConvertNodeString(const char* input, NodeConversionBudget* budget, std::string* result);
const bool audio_only_;
mpv_handle* mpv_ = nullptr;
@@ -208,6 +259,8 @@ class MpvPlayer {
// Isolated EGL context for mpv rendering (not shared with Flutter)
EGLDisplay egl_display_ = EGL_NO_DISPLAY;
EGLContext egl_context_ = EGL_NO_CONTEXT;
std::vector<NativeRenderTeardownResource> retained_render_contexts_;
mutable std::mutex native_mutex_;
std::atomic<bool> needs_redraw_{false};
std::atomic<bool> disposed_{false};