From 98d23e5489185180411e5cb88d14b2b7a964f55d Mon Sep 17 00:00:00 2001 From: "yying.jin" Date: Thu, 25 Jun 2026 16:33:32 +0800 Subject: [PATCH 1/2] [video_player_videohole] Remove Ecore API - Replace ecore_pipe with GLib GMainContext for thread-safe event dispatching - Remove dependency on Ecore library for main loop event handling - Retain EcoreWl2WindowProxy for window geometry queries (dynamically loaded) Co-Authored-By: Cline SR --- packages/video_player_videohole/CHANGELOG.md | 3 +- packages/video_player_videohole/README.md | 2 +- packages/video_player_videohole/pubspec.yaml | 2 +- .../tizen/src/drm_manager.cc | 73 ++++++++++++++--- .../tizen/src/drm_manager.h | 22 ++++- .../tizen/src/video_player.cc | 80 +++++++++++++++---- .../tizen/src/video_player.h | 21 ++++- 7 files changed, 170 insertions(+), 33 deletions(-) diff --git a/packages/video_player_videohole/CHANGELOG.md b/packages/video_player_videohole/CHANGELOG.md index e8c9f4df8..c01452c7f 100644 --- a/packages/video_player_videohole/CHANGELOG.md +++ b/packages/video_player_videohole/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 0.5.9 * Adds compatibility with `http` 1.0 in example. * Removed unused proxy APIs. +* Remove Ecore API. ## 0.5.8 diff --git a/packages/video_player_videohole/README.md b/packages/video_player_videohole/README.md index 6f1a1e6d0..b640bcc08 100644 --- a/packages/video_player_videohole/README.md +++ b/packages/video_player_videohole/README.md @@ -12,7 +12,7 @@ To use this package, add `video_player_videohole` as a dependency in your `pubsp ```yaml dependencies: - video_player_videohole: ^0.5.8 + video_player_videohole: ^0.5.9 ``` Then you can import `video_player_videohole` in your Dart code: diff --git a/packages/video_player_videohole/pubspec.yaml b/packages/video_player_videohole/pubspec.yaml index c8a5415e1..fefb4b06e 100644 --- a/packages/video_player_videohole/pubspec.yaml +++ b/packages/video_player_videohole/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_videohole description: Flutter plugin for displaying inline video on Tizen TV devices. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/video_player_videohole -version: 0.5.8 +version: 0.5.9 environment: sdk: ">=3.1.0 <4.0.0" diff --git a/packages/video_player_videohole/tizen/src/drm_manager.cc b/packages/video_player_videohole/tizen/src/drm_manager.cc index d2bb54d4d..2794ad368 100644 --- a/packages/video_player_videohole/tizen/src/drm_manager.cc +++ b/packages/video_player_videohole/tizen/src/drm_manager.cc @@ -34,19 +34,31 @@ DrmManager::DrmManager() : drm_type_(DM_TYPE_NONE) { } else { LOG_ERROR("[DrmManager] Fail to dlopen libdrmmanager."); } - license_request_pipe_ = ecore_pipe_add( - [](void *data, void *buffer, unsigned int nbyte) -> void { - auto *self = static_cast(data); - self->ExecuteRequest(); - }, - this); + + // Initialize GMainContext and license request state + main_context_ = std::unique_ptr( + g_main_context_ref_thread_default()); + license_request_state_ = std::make_shared(); + license_request_state_->manager = this; } DrmManager::~DrmManager() { ReleaseDrmSession(); - if (license_request_pipe_) { - ecore_pipe_del(license_request_pipe_); + + // Mark license request state as disposed and cancel pending event source + if (license_request_state_) { + std::lock_guard lock(license_request_state_->mutex); + license_request_state_->disposed = true; + license_request_state_->manager = nullptr; + + if (license_request_state_->pending_source_id != 0) { + g_source_remove(license_request_state_->pending_source_id); + license_request_state_->pending_source_id = 0; + } } + + main_context_.reset(); + if (drm_manager_proxy_) { CloseDrmManagerProxy(drm_manager_proxy_); drm_manager_proxy_ = nullptr; @@ -325,10 +337,49 @@ void DrmManager::RequestLicense(std::string &session_id, std::string &message) { std::move(result_handler)); } +void DrmManager::ScheduleProcessLicenseRequest() { + std::lock_guard lock(license_request_state_->mutex); + + // Check conditions and deduplicate + if (!main_context_ || !license_request_state_ || + license_request_state_->disposed || + license_request_state_->pending_source_id != 0) { + return; + } + + auto *state = + new std::shared_ptr(license_request_state_); + + GSource *source = g_idle_source_new(); + g_source_set_callback( + source, + [](gpointer data) -> gboolean { + auto state = static_cast *>(data); + + // Lock the mutex before checking and accessing manager + std::lock_guard lock((*state)->mutex); + if (!(*state)->disposed && (*state)->manager) { + (*state)->pending_source_id = 0; + (*state)->manager->ExecuteRequest(); + } + return G_SOURCE_REMOVE; + }, + state, + [](gpointer data) { + delete static_cast *>(data); + }); + + license_request_state_->pending_source_id = + g_source_attach(source, main_context_.get()); + g_source_unref(source); +} + void DrmManager::PushLicenseRequestData(DataForLicenseProcess &data) { - std::lock_guard lock(queue_mutex_); - license_request_queue_.push(data); - ecore_pipe_write(license_request_pipe_, nullptr, 0); + { + std::lock_guard lock(queue_mutex_); + license_request_queue_.push(data); + } + ScheduleProcessLicenseRequest(); } void DrmManager::ExecuteRequest() { diff --git a/packages/video_player_videohole/tizen/src/drm_manager.h b/packages/video_player_videohole/tizen/src/drm_manager.h index c8113d96f..3c820a0a0 100644 --- a/packages/video_player_videohole/tizen/src/drm_manager.h +++ b/packages/video_player_videohole/tizen/src/drm_manager.h @@ -5,8 +5,8 @@ #ifndef FLUTTER_PLUGIN_DRM_MANAGER_H_ #define FLUTTER_PLUGIN_DRM_MANAGER_H_ -#include #include +#include #include #include @@ -66,8 +66,26 @@ class DrmManager { std::string license_server_url_; bool initialized_ = false; std::mutex queue_mutex_; - Ecore_Pipe *license_request_pipe_ = nullptr; std::queue license_request_queue_; + + struct GMainContextDeleter { + void operator()(GMainContext *context) const { + g_main_context_unref(context); + } + }; + + // GLib license request dispatch state + struct LicenseRequestState { + std::mutex mutex; + DrmManager *manager = nullptr; + bool disposed = false; + guint pending_source_id = 0; + }; + + std::shared_ptr license_request_state_; + std::unique_ptr main_context_; + + void ScheduleProcessLicenseRequest(); }; #endif // FLUTTER_PLUGIN_DRM_MANAGER_H_ diff --git a/packages/video_player_videohole/tizen/src/video_player.cc b/packages/video_player_videohole/tizen/src/video_player.cc index 37fc21718..359898a11 100644 --- a/packages/video_player_videohole/tizen/src/video_player.cc +++ b/packages/video_player_videohole/tizen/src/video_player.cc @@ -18,19 +18,27 @@ VideoPlayer::VideoPlayer(flutter::BinaryMessenger *messenger, : ecore_wl2_window_proxy_(std::make_unique()), binary_messenger_(messenger), flutter_view_(flutter_view) { - sink_event_pipe_ = ecore_pipe_add( - [](void *data, void *buffer, unsigned int nbyte) -> void { - auto *self = static_cast(data); - self->ExecuteSinkEvents(); - }, - this); + // Initialize GMainContext and event dispatch state + main_context_ = std::unique_ptr( + g_main_context_ref_thread_default()); + event_dispatch_state_ = std::make_shared(); + event_dispatch_state_->player = this; } VideoPlayer::~VideoPlayer() { - if (sink_event_pipe_) { - ecore_pipe_del(sink_event_pipe_); - sink_event_pipe_ = nullptr; + // Mark event dispatch state as disposed and cancel pending event source + if (event_dispatch_state_) { + std::lock_guard lock(event_dispatch_state_->mutex); + event_dispatch_state_->disposed = true; + event_dispatch_state_->player = nullptr; + + if (event_dispatch_state_->pending_source_id != 0) { + g_source_remove(event_dispatch_state_->pending_source_id); + event_dispatch_state_->pending_source_id = 0; + } } + + main_context_.reset(); } void VideoPlayer::ClearUpEventChannel() { @@ -90,14 +98,52 @@ void VideoPlayer::ExecuteSinkEvents() { } } +void VideoPlayer::ScheduleSendPendingEvents() { + std::lock_guard lock(event_dispatch_state_->mutex); + + // Check conditions and deduplicate + if (!main_context_ || !event_dispatch_state_ || + event_dispatch_state_->disposed || + event_dispatch_state_->pending_source_id != 0) { + return; + } + + auto *state = new std::shared_ptr(event_dispatch_state_); + + GSource *source = g_idle_source_new(); + g_source_set_callback( + source, + [](gpointer data) -> gboolean { + auto state = static_cast *>(data); + + // Lock the mutex before checking and accessing player + std::lock_guard lock((*state)->mutex); + if (!(*state)->disposed && (*state)->player) { + (*state)->pending_source_id = 0; + (*state)->player->ExecuteSinkEvents(); + } + return G_SOURCE_REMOVE; + }, + state, + [](gpointer data) { + delete static_cast *>(data); + }); + + event_dispatch_state_->pending_source_id = + g_source_attach(source, main_context_.get()); + g_source_unref(source); +} + void VideoPlayer::PushEvent(flutter::EncodableValue encodable_value) { - std::lock_guard lock(queue_mutex_); - if (event_sink_ == nullptr) { + if (!event_sink_) { LOG_ERROR("[VideoPlayer] event sink is nullptr."); return; } - encodable_event_queue_.push(encodable_value); - ecore_pipe_write(sink_event_pipe_, nullptr, 0); + { + std::lock_guard lock(queue_mutex_); + encodable_event_queue_.push(encodable_value); + } + ScheduleSendPendingEvents(); } void VideoPlayer::SendInitialized() { @@ -197,11 +243,15 @@ void VideoPlayer::SendRestored() { void VideoPlayer::SendError(const std::string &error_code, const std::string &error_message) { - if (event_sink_) { + if (!event_sink_) { + LOG_ERROR("[VideoPlayer] event sink is nullptr."); + return; + } + { std::lock_guard lock(queue_mutex_); error_event_queue_.push(std::make_pair(error_code, error_message)); - ecore_pipe_write(sink_event_pipe_, nullptr, 0); } + ScheduleSendPendingEvents(); } void *VideoPlayer::GetWindowHandle() { diff --git a/packages/video_player_videohole/tizen/src/video_player.h b/packages/video_player_videohole/tizen/src/video_player.h index 8b17f4e48..92a311197 100644 --- a/packages/video_player_videohole/tizen/src/video_player.h +++ b/packages/video_player_videohole/tizen/src/video_player.h @@ -5,10 +5,10 @@ #ifndef FLUTTER_PLUGIN_VIDEO_PLAYER_H_ #define FLUTTER_PLUGIN_VIDEO_PLAYER_H_ -#include #include #include #include +#include #include #include @@ -79,7 +79,25 @@ class VideoPlayer { bool is_restored_ = false; private: + struct GMainContextDeleter { + void operator()(GMainContext *context) const { + g_main_context_unref(context); + } + }; + + // Event dispatch state structure for lifecycle management + struct EventDispatchState { + std::mutex mutex; + VideoPlayer *player = nullptr; + bool disposed = false; + guint pending_source_id = 0; + }; + + std::shared_ptr event_dispatch_state_; + std::unique_ptr main_context_; + void ExecuteSinkEvents(); + void ScheduleSendPendingEvents(); void PushEvent(flutter::EncodableValue encodable_value); std::queue encodable_event_queue_; @@ -87,7 +105,6 @@ class VideoPlayer { std::unique_ptr> event_channel_; std::unique_ptr> event_sink_; - Ecore_Pipe *sink_event_pipe_ = nullptr; }; } // namespace video_player_videohole_tizen From 75ee5f475e9008793c05be42af055fdfc734f4d6 Mon Sep 17 00:00:00 2001 From: "yying.jin" Date: Thu, 25 Jun 2026 18:58:19 +0800 Subject: [PATCH 2/2] fix review issues --- .../tizen/src/drm_manager.cc | 16 +++++--- .../tizen/src/video_player.cc | 37 +++++++++++-------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/video_player_videohole/tizen/src/drm_manager.cc b/packages/video_player_videohole/tizen/src/drm_manager.cc index 2794ad368..3547b6cbd 100644 --- a/packages/video_player_videohole/tizen/src/drm_manager.cc +++ b/packages/video_player_videohole/tizen/src/drm_manager.cc @@ -355,12 +355,16 @@ void DrmManager::ScheduleProcessLicenseRequest() { source, [](gpointer data) -> gboolean { auto state = static_cast *>(data); - - // Lock the mutex before checking and accessing manager - std::lock_guard lock((*state)->mutex); - if (!(*state)->disposed && (*state)->manager) { - (*state)->pending_source_id = 0; - (*state)->manager->ExecuteRequest(); + DrmManager *manager = nullptr; + { + std::lock_guard lock((*state)->mutex); + if (!(*state)->disposed && (*state)->manager) { + (*state)->pending_source_id = 0; + manager = (*state)->manager; + } + } + if (manager) { + manager->ExecuteRequest(); } return G_SOURCE_REMOVE; }, diff --git a/packages/video_player_videohole/tizen/src/video_player.cc b/packages/video_player_videohole/tizen/src/video_player.cc index 359898a11..724011822 100644 --- a/packages/video_player_videohole/tizen/src/video_player.cc +++ b/packages/video_player_videohole/tizen/src/video_player.cc @@ -43,7 +43,10 @@ VideoPlayer::~VideoPlayer() { void VideoPlayer::ClearUpEventChannel() { is_initialized_ = false; - event_sink_ = nullptr; + { + std::lock_guard lock(queue_mutex_); + event_sink_ = nullptr; + } if (event_channel_) { event_channel_->SetStreamHandler(nullptr); } @@ -115,12 +118,16 @@ void VideoPlayer::ScheduleSendPendingEvents() { source, [](gpointer data) -> gboolean { auto state = static_cast *>(data); - - // Lock the mutex before checking and accessing player - std::lock_guard lock((*state)->mutex); - if (!(*state)->disposed && (*state)->player) { - (*state)->pending_source_id = 0; - (*state)->player->ExecuteSinkEvents(); + VideoPlayer *player = nullptr; + { + std::lock_guard lock((*state)->mutex); + if (!(*state)->disposed && (*state)->player) { + (*state)->pending_source_id = 0; + player = (*state)->player; + } + } + if (player) { + player->ExecuteSinkEvents(); } return G_SOURCE_REMOVE; }, @@ -135,12 +142,12 @@ void VideoPlayer::ScheduleSendPendingEvents() { } void VideoPlayer::PushEvent(flutter::EncodableValue encodable_value) { - if (!event_sink_) { - LOG_ERROR("[VideoPlayer] event sink is nullptr."); - return; - } { std::lock_guard lock(queue_mutex_); + if (!event_sink_) { + LOG_ERROR("[VideoPlayer] event sink is nullptr."); + return; + } encodable_event_queue_.push(encodable_value); } ScheduleSendPendingEvents(); @@ -243,12 +250,12 @@ void VideoPlayer::SendRestored() { void VideoPlayer::SendError(const std::string &error_code, const std::string &error_message) { - if (!event_sink_) { - LOG_ERROR("[VideoPlayer] event sink is nullptr."); - return; - } { std::lock_guard lock(queue_mutex_); + if (!event_sink_) { + LOG_ERROR("[VideoPlayer] event sink is nullptr."); + return; + } error_event_queue_.push(std::make_pair(error_code, error_message)); } ScheduleSendPendingEvents();