#ifndef MPV_PLAYER_H_ #define MPV_PLAYER_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../shared/mpv/mpv_player_common.h" // Forward declaration for Flutter types struct _FlValue; namespace mpv { /// Callback function type for mpv events. /// Note: FlValue* is passed from the global namespace, not mpv namespace. using EventCallback = std::function; /// Callback for requesting a redraw (called from mpv render update thread). using RedrawCallback = std::function; /// Wrapper for libmpv that handles initialization, OpenGL rendering, /// commands, properties, and event dispatching. class MpvPlayer { public: /// |audio_only| runs mpv as a music core with video disabled entirely: /// no render context is ever created (InitRenderContext must not be /// called) and no GL/EGL state is touched. explicit MpvPlayer(bool audio_only = false); ~MpvPlayer(); /// Initializes the mpv instance and configures options. /// Does NOT create the render context — call InitRenderContext() later /// when an OpenGL context is available. /// @return true if initialization succeeded. bool Initialize(); /// Creates the mpv OpenGL render context. /// Must be called with a valid GL context current (e.g., from FlTextureGL::populate). /// Fails on audio-only players. /// @return true if render context creation succeeded. bool InitRenderContext(); /// Returns true if the render context has been created. bool HasRenderContext() const { return mpv_gl_ != nullptr; } /// Returns the isolated EGL display used for mpv rendering. EGLDisplay GetEglDisplay() const { return egl_display_; } /// Returns the isolated EGL context used for mpv rendering. EGLContext GetEglContext() const { return egl_context_; } /// 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); } /// 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; } /// Queues an mpv command without waiting for completion. void Command(const std::vector& args); /// Callback types for async mpv requests. using StatusCallback = plezy::mpv_common::StatusCallback; using CommandCallback = StatusCallback; using GetPropertyCallback = plezy::mpv_common::GetPropertyCallback; /// Executes an mpv command asynchronously to prevent UI blocking. void CommandAsync(const std::vector& args, CommandCallback callback); /// Sets an mpv property by name. void SetProperty(const std::string& name, const std::string& value); /// Sets an mpv property asynchronously. void SetPropertyAsync(const std::string& name, const std::string& value, StatusCallback callback); /// Gets an mpv property value asynchronously. void GetPropertyAsync(const std::string& name, GetPropertyCallback callback); /// Observes an mpv property for changes. void ObserveProperty(const std::string& name, const std::string& format, int id); /// Renders a frame to the specified FBO. void Render(int width, int height, int fbo = 0); /// Reports that the mouse has moved. void ReportMouseMove(int x, int y); /// Sets the event callback for property changes and events. void SetEventCallback(EventCallback callback); /// Sets the redraw callback (called when mpv has a new frame ready). void SetRedrawCallback(RedrawCallback callback); /// Returns true if a redraw is needed. bool NeedsRedraw() const { return needs_redraw_.load(); } /// Clears the redraw flag. void ClearRedrawFlag() { needs_redraw_.store(false); } /// Sets the MPV log message level (e.g., "warn", "v", "debug"). void SetLogLevel(const std::string& level); private: class CallbackContext { public: class Lease { public: Lease() = default; Lease(const Lease&) = delete; Lease& operator=(const Lease&) = delete; Lease(Lease&& other) noexcept; Lease& operator=(Lease&& other) noexcept; ~Lease(); explicit operator bool() const { return player_ != nullptr; } MpvPlayer* player() const { return player_; } private: friend class CallbackContext; Lease(CallbackContext* context, MpvPlayer* player); void Release(); CallbackContext* context_ = nullptr; MpvPlayer* player_ = nullptr; }; explicit CallbackContext(MpvPlayer* player); ~CallbackContext(); Lease Acquire(); void DetachAndWait(); GMainContext* main_context() const { return main_context_; } private: void ReleaseLease(); std::mutex mutex_; std::condition_variable quiescent_; MpvPlayer* player_; size_t in_flight_ = 0; GMainContext* main_context_; }; struct SourceCallbackData; friend class MpvPlayerLifecycleTestPeer; /// MPV event wakeup callback (called from mpv thread). static void OnMpvWakeup(void* ctx); /// MPV render update callback (called when frame is ready). static void OnMpvRenderUpdate(void* ctx); static gboolean DispatchWakeupSource(gpointer data); static gboolean DispatchRedrawSource(gpointer data); static gboolean DispatchRecoverySource(gpointer data); static void DestroySourceCallbackData(gpointer data); void ScheduleWakeupSource(); void ScheduleRedrawSource(); void ScheduleRecoverySource(); void RemoveTrackedSources(); /// Processes pending mpv events. bool ProcessEvents(); /// Handles a single mpv event. void HandleMpvEvent(mpv_event* event); /// Sends a property change notification. void SendPropertyChange(const char* name, mpv_node* data); /// Sends an event notification. void SendEvent(const std::string& name, ::_FlValue* data = nullptr); void MaybeRunAudioRecovery(); void TryAudioReload(const char* reason, int attempt); void EnsureAudioRecoveryTimer(); void LogRecovery(const std::string& text); void SetHDREnabled(bool enabled, StatusCallback callback = nullptr); /// Helper to convert mpv_node to FlValue. ::_FlValue* NodeToFlValue(mpv_node* node); const bool audio_only_; mpv_handle* mpv_ = nullptr; mpv_render_context* mpv_gl_ = nullptr; // Isolated EGL context for mpv rendering (not shared with Flutter) EGLDisplay egl_display_ = EGL_NO_DISPLAY; EGLContext egl_context_ = EGL_NO_CONTEXT; std::atomic needs_redraw_{false}; std::atomic disposed_{false}; EventCallback event_callback_; RedrawCallback redraw_callback_; std::mutex callback_mutex_; plezy::mpv_common::AudioRecoveryState audio_recovery_; plezy::mpv_common::AsyncRequestRegistry pending_requests_; plezy::mpv_common::PropertyObservationRegistry observed_properties_; bool hdr_enabled_ = true; // All player-carrying sources are attached to CallbackContext::main_context() // and protected by source_mutex_. std::shared_ptr callback_context_; std::mutex source_mutex_; guint wakeup_source_id_ = 0; guint redraw_source_id_ = 0; guint recovery_source_id_ = 0; }; } // namespace mpv #endif // MPV_PLAYER_H_