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..3547b6cbd 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,53 @@ 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); + 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; + }, + 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..724011822 100644 --- a/packages/video_player_videohole/tizen/src/video_player.cc +++ b/packages/video_player_videohole/tizen/src/video_player.cc @@ -18,24 +18,35 @@ 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() { is_initialized_ = false; - event_sink_ = nullptr; + { + std::lock_guard lock(queue_mutex_); + event_sink_ = nullptr; + } if (event_channel_) { event_channel_->SetStreamHandler(nullptr); } @@ -90,14 +101,56 @@ void VideoPlayer::ExecuteSinkEvents() { } } -void VideoPlayer::PushEvent(flutter::EncodableValue encodable_value) { - std::lock_guard lock(queue_mutex_); - if (event_sink_ == nullptr) { - LOG_ERROR("[VideoPlayer] event sink is nullptr."); +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; } - encodable_event_queue_.push(encodable_value); - ecore_pipe_write(sink_event_pipe_, nullptr, 0); + + 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); + 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; + }, + 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_) { + LOG_ERROR("[VideoPlayer] event sink is nullptr."); + return; + } + encodable_event_queue_.push(encodable_value); + } + ScheduleSendPendingEvents(); } void VideoPlayer::SendInitialized() { @@ -197,11 +250,15 @@ void VideoPlayer::SendRestored() { void VideoPlayer::SendError(const std::string &error_code, const std::string &error_message) { - if (event_sink_) { + { 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)); - 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