fix(native): bound cross-platform lifecycle ownership
This commit is contained in:
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user