Skip to content

[video_player_videohole] Migrating from Platform Channels to Dart FFI - #1073

Open
gin7773 wants to merge 37 commits into
flutter-tizen:masterfrom
gin7773:videohole/ffi_migration
Open

[video_player_videohole] Migrating from Platform Channels to Dart FFI#1073
gin7773 wants to merge 37 commits into
flutter-tizen:masterfrom
gin7773:videohole/ffi_migration

Conversation

@gin7773

@gin7773 gin7773 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Main changes:

  • Migrated from Platform Channels to Dart FFI.
  • Replaced EventChannel with FFI port for event callbacks.
  • Added JSON serialization for complex parameters.

Threading note

After migrating from Platform Channels to Dart FFI, some native calls are invoked synchronously from the Flutter UI thread rather than through the previous platform-channel handler path. The ecore_wl2 display path is marked with a TODO because those APIs are not thread-safe and will be revisited during the planned ecore-to-GLib migration.

@gin7773
gin7773 marked this pull request as draft July 28, 2026 10:38
gin7773 added 6 commits July 29, 2026 18:01
- Add nlohmann/json single-header library to tizen/third_party/
- Replace handwritten JSON parser with nlohmann/json in ParseJsonMap()
- Simplify ParseCreateMessage() to use json library directly
- Add EncodableValueFromJson() helper for JSON to EncodableValue conversion

This change improves JSON parsing reliability by supporting:
- Escape characters in strings
- Nested objects and arrays
- Unicode characters
- Proper error handling with parse_error exceptions

Co-Authored-By: Cline SR
- Add UnregisterAllPlayerEventPorts() function in video_player.cc
- Add ffi_unregister_all_player_event_ports() FFI wrapper
- Add Dart bindings in ffi_messages.g.dart
- Call unregisterAllPlayerEventPorts() in init() to clean up ports on hot restart

This fix prevents Dart port leaks when the Dart VM is restarted
(e.g., hot restart during development) while the native process continues.

Co-Authored-By: Cline SR
- Fix FFI event port symbol name mismatch (ffi_register_dart_port)
- Call Prepare() after RestorePlayer for two-phase initialization
- Return true from Play() when already playing (idempotent)
- Remove duplicate play() call in restored event handler

Co-Authored-By: Cline SR
@xiaowei-guan
xiaowei-guan self-requested a review July 30, 2026 09:24
@gin7773
gin7773 marked this pull request as ready for review August 5, 2026 08:19
@gin7773
gin7773 requested a review from JSUYA August 5, 2026 08:19
@gin7773
gin7773 requested a review from seungsoo47 August 5, 2026 08:20
Comment thread packages/video_player_videohole/tizen/src/video_player_tizen_plugin.cc Outdated
Comment thread packages/video_player_videohole/tizen/src/video_player.cc Outdated
Comment thread packages/video_player_videohole/tizen/src/media_player.cc
gin7773 added 3 commits August 5, 2026 18:45
- Move all FFI function implementations from video_player_tizen_plugin.cc to ffi_messages.cc
- Add ffi_prepare() function that was missing
- Use dependency injection pattern (ffi_set_plugin_registrar) for clean boundaries
- Keep video_player_tizen_plugin.cc minimal (plugin registration only)

Co-Authored-By: Cline SR
>
> 1. Fix potential deadlock in PostEventToDart
>    - Copy port number under lock, then release lock BEFORE calling Dart_PostCObject_DL
>    - Use explicit scope block to ensure lock is released immediately
>    - Prevents deadlock if Dart_PostCObject_DL blocks or callbacks into native code
>
> 2. Remove dead code (player_index variable)
>    - player_index in video_player.cc is no longer used
>    - ID generation moved to media_player.cc (player_id_counter)
>
> Co-Authored-By: Cline SR
Comment thread packages/video_player_videohole/lib/src/video_player_tizen.dart
Comment thread packages/video_player_videohole/tizen/src/media_player.cc
@JSUYA

JSUYA commented Aug 11, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 97da2d750e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/video_player_videohole/lib/src/video_player_tizen.dart
Comment thread packages/video_player_videohole/lib/src/video_player_tizen.dart Outdated
Comment thread packages/video_player_videohole/tizen/src/ffi_messages.cc Outdated
Comment thread packages/video_player_videohole/tizen/src/video_player_tizen_plugin.cc Outdated
Comment thread packages/video_player_videohole/lib/src/ffi_messages.g.dart Outdated
Fix the following issues from PR flutter-tizen#1073 reviews:

1. Error event handling missing
   - Add 'error' event branch in _ensureEventPortRegistered()
   - Call controller.addError(PlatformException(...)) for error events

2. Seek failure return value not propagated
   - ffi_seek_to() now returns player->SeekTo(...) ? 0 : -1

3. Plugin lifecycle management missing
   - Add VideoPlayerTizenPlugin class inheriting flutter::Plugin
   - Call ffi_dispose_all_players() in destructor
   - Add ffi_dispose_all_players() function to clean up players and unregister port

4. Dart API DL initialization check missing
   - Only set _apiDlInitialized if native initialization returns 0
   - Throw exception on failure

5. Event port rebinding issue
   - Change _eventPort from static to instance variable
   - Each VideoPlayerTizen instance has its own port

Co-Authored-By: Cline SR
Comment on lines +422 to +433
static int64_t g_legacy_dart_port = -1;
static std::mutex g_legacy_dart_port_mutex;

void ffi_register_event_port(int64_t port) {
std::lock_guard<std::mutex> lock(g_legacy_dart_port_mutex);
g_legacy_dart_port = port;
}

void ffi_unregister_event_port() {
std::lock_guard<std::mutex> lock(g_legacy_dart_port_mutex);
g_legacy_dart_port = -1;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are these used? They don't look to be in actual use, and
g_legacy_dart_port also looks to be invalid code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been deleted.

@@ -0,0 +1,745 @@
// Copyright 2023 Samsung Electronics Co., Ltd. All rights reserved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2026 ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return true;
}

bool MediaPlayer::StopAndDestroy() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StopAndDestroy() early-returns when player_stop (line 711) or player_unprepare (line 718) fails. On the Dispose/destructor path that failure now leaks the native player handle — the old destructor unconditionally reached player_destroy. For teardown, please follow the repo's "LOG_ERROR + continue" convention so player_destroy (and player_ = nullptr) is always reached, instead of aborting mid-teardown.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return event_dispatch_state_->disposed;
}
return true; // If no event_dispatch_state_, consider as disposed
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does isDisposed() work as intended?
After ~VideoPlayer() is called std::shared_ptr event_dispatch_state_; is valid?

and OnPrepared (line 918) is the only callback missing the guard — please align it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Use FFI for initialization (synchronous call)
final int result = _ffiApi.initialize();
if (result != 0) {
throw Exception('FFI initialize failed with code: $result');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing plain Exception on FFI failure changes the app-visible error contract — the previous pigeon implementation (and sibling plugins) throw PlatformException, and the event stream already delivers PlatformException after the earlier review fix. Please unify the method paths on PlatformException (code + message) so existing on PlatformException handlers keep working.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return _api.seekTo(
PositionMessage(playerId: playerId, position: position.inMilliseconds),
);
Future<void> seekTo(int playerId, Duration position) async {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there are some changes from the existing behavior.
seekTo()'s Future now completes when the seek starts (empty callback in ffi_seek_to), not when it finishes as before — so post-await position reads can be stale and rapid consecutive seeks can fail; please complete it via a seekCompleted event through the existing port.
Please check this

Comment on lines +85 to +87
// Phase 2: Register global Dart port (only needs to be done once)
// Note: registerDartPort is now a no-op since we use global port
// The port is already registered in _ensureEventPortRegistered()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment valid?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been deleted.

@JSUYA

JSUYA commented Aug 19, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd3e299820

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/video_player_videohole/tizen/src/ffi_messages.h Outdated
Comment thread packages/video_player_videohole/tizen/src/media_player.cc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants