Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/video_player_videohole/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion packages/video_player_videohole/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player_videohole/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
77 changes: 66 additions & 11 deletions packages/video_player_videohole/tizen/src/drm_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<DrmManager *>(data);
self->ExecuteRequest();
},
this);

// Initialize GMainContext and license request state
main_context_ = std::unique_ptr<GMainContext, GMainContextDeleter>(
g_main_context_ref_thread_default());
license_request_state_ = std::make_shared<DrmManager::LicenseRequestState>();
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<std::mutex> 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;
Expand Down Expand Up @@ -325,10 +337,53 @@ void DrmManager::RequestLicense(std::string &session_id, std::string &message) {
std::move(result_handler));
}

void DrmManager::ScheduleProcessLicenseRequest() {
std::lock_guard<std::mutex> 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<LicenseRequestState>(license_request_state_);

GSource *source = g_idle_source_new();
g_source_set_callback(
source,
[](gpointer data) -> gboolean {
auto state = static_cast<std::shared_ptr<LicenseRequestState> *>(data);
DrmManager *manager = nullptr;
{
std::lock_guard<std::mutex> 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<std::shared_ptr<LicenseRequestState> *>(data);
});
Comment thread
gin7773 marked this conversation as resolved.

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<std::mutex> lock(queue_mutex_);
license_request_queue_.push(data);
ecore_pipe_write(license_request_pipe_, nullptr, 0);
{
std::lock_guard<std::mutex> lock(queue_mutex_);
license_request_queue_.push(data);
}
ScheduleProcessLicenseRequest();
}

void DrmManager::ExecuteRequest() {
Expand Down
22 changes: 20 additions & 2 deletions packages/video_player_videohole/tizen/src/drm_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#ifndef FLUTTER_PLUGIN_DRM_MANAGER_H_
#define FLUTTER_PLUGIN_DRM_MANAGER_H_

#include <Ecore.h>
#include <flutter/method_channel.h>
#include <glib.h>

#include <mutex>
#include <queue>
Expand Down Expand Up @@ -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<DataForLicenseProcess> 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<LicenseRequestState> license_request_state_;
std::unique_ptr<GMainContext, GMainContextDeleter> main_context_;

void ScheduleProcessLicenseRequest();
};

#endif // FLUTTER_PLUGIN_DRM_MANAGER_H_
93 changes: 75 additions & 18 deletions packages/video_player_videohole/tizen/src/video_player.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,35 @@ VideoPlayer::VideoPlayer(flutter::BinaryMessenger *messenger,
: ecore_wl2_window_proxy_(std::make_unique<EcoreWl2WindowProxy>()),
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<VideoPlayer *>(data);
self->ExecuteSinkEvents();
},
this);
// Initialize GMainContext and event dispatch state
main_context_ = std::unique_ptr<GMainContext, GMainContextDeleter>(
g_main_context_ref_thread_default());
event_dispatch_state_ = std::make_shared<VideoPlayer::EventDispatchState>();
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<std::mutex> 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<std::mutex> lock(queue_mutex_);
event_sink_ = nullptr;
}
if (event_channel_) {
event_channel_->SetStreamHandler(nullptr);
}
Expand Down Expand Up @@ -90,14 +101,56 @@ void VideoPlayer::ExecuteSinkEvents() {
}
}

void VideoPlayer::PushEvent(flutter::EncodableValue encodable_value) {
std::lock_guard<std::mutex> lock(queue_mutex_);
if (event_sink_ == nullptr) {
LOG_ERROR("[VideoPlayer] event sink is nullptr.");
void VideoPlayer::ScheduleSendPendingEvents() {
std::lock_guard<std::mutex> 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<EventDispatchState>(event_dispatch_state_);

GSource *source = g_idle_source_new();
g_source_set_callback(
source,
[](gpointer data) -> gboolean {
auto state = static_cast<std::shared_ptr<EventDispatchState> *>(data);
VideoPlayer *player = nullptr;
{
std::lock_guard<std::mutex> 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<std::shared_ptr<EventDispatchState> *>(data);
});
Comment thread
gin7773 marked this conversation as resolved.

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<std::mutex> lock(queue_mutex_);
if (!event_sink_) {
LOG_ERROR("[VideoPlayer] event sink is nullptr.");
return;
}
encodable_event_queue_.push(encodable_value);
}
ScheduleSendPendingEvents();
}
Comment thread
gin7773 marked this conversation as resolved.

void VideoPlayer::SendInitialized() {
Expand Down Expand Up @@ -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<std::mutex> 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();
}
Comment thread
gin7773 marked this conversation as resolved.

void *VideoPlayer::GetWindowHandle() {
Expand Down
21 changes: 19 additions & 2 deletions packages/video_player_videohole/tizen/src/video_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#ifndef FLUTTER_PLUGIN_VIDEO_PLAYER_H_
#define FLUTTER_PLUGIN_VIDEO_PLAYER_H_

#include <Ecore.h>
#include <flutter/encodable_value.h>
#include <flutter/event_channel.h>
#include <flutter_tizen.h>
#include <glib.h>

#include <memory>
#include <mutex>
Expand Down Expand Up @@ -79,15 +79,32 @@ 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<EventDispatchState> event_dispatch_state_;
std::unique_ptr<GMainContext, GMainContextDeleter> main_context_;

void ExecuteSinkEvents();
void ScheduleSendPendingEvents();
void PushEvent(flutter::EncodableValue encodable_value);

std::queue<flutter::EncodableValue> encodable_event_queue_;
std::queue<std::pair<std::string, std::string>> error_event_queue_;
std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>>
event_channel_;
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink_;
Ecore_Pipe *sink_event_pipe_ = nullptr;
};

} // namespace video_player_videohole_tizen
Expand Down
Loading