From a61fca0588b1b31744a6934fde16074eb4e2f10d Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:40:52 +0100 Subject: [PATCH] perf: compact [propId, value] wire format for property events --- .../plezy/exoplayer/ExoPlayerPlugin.kt | 24 +++++--- .../com/edde746/plezy/mpv/MpvPlayerPlugin.kt | 16 +++--- ios/Runner/MpvPlayer/MpvPlayerPlugin.swift | 15 +++-- lib/mpv/player/player_android.dart | 13 +++++ lib/mpv/player/player_base.dart | 56 +++++++++++++------ lib/mpv/player/player_native.dart | 24 ++++---- linux/runner/mpv/mpv_player.cc | 23 +++++--- linux/runner/mpv/mpv_player.h | 5 +- linux/runner/mpv/mpv_plugin.cc | 8 ++- macos/Runner/MpvPlayer/MpvPlayerCore.swift | 39 +++++++++++++ macos/Runner/MpvPlayer/MpvPlayerPlugin.swift | 15 +++-- windows/runner/mpv/mpv_player.cpp | 23 +++++--- windows/runner/mpv/mpv_player.h | 6 +- windows/runner/mpv/mpv_plugin.cpp | 15 +++-- windows/runner/mpv/mpv_plugin.h | 2 +- 15 files changed, 193 insertions(+), 91 deletions(-) diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt index 44726539..6720e204 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt @@ -29,6 +29,7 @@ class ExoPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, private var usingMpvFallback: Boolean = false private var activity: Activity? = null private var activityBinding: ActivityPluginBinding? = null + private val nameToId = mutableMapOf() // FlutterPlugin @@ -119,6 +120,7 @@ class ExoPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, "getStats" -> handleGetStats(result) "getPlayerType" -> result.success(if (usingMpvFallback) "mpv" else "exoplayer") "setSubtitleStyle" -> handleSetSubtitleStyle(call, result) + "observeProperty" -> handleObserveProperty(call, result) else -> result.notImplemented() } } @@ -413,6 +415,19 @@ class ExoPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, result.success(null) } + private fun handleObserveProperty(call: MethodCall, result: MethodChannel.Result) { + val name = call.argument("name") + val id = call.argument("id") + + if (name == null || id == null) { + result.error("INVALID_ARGS", "Missing 'name' or 'id'", null) + return + } + + nameToId[name] = id + result.success(null) + } + private fun handleSetSubtitleStyle(call: MethodCall, result: MethodChannel.Result) { val fontSize = call.argument("fontSize")?.toFloat() ?: 55f val textColor = call.argument("textColor") ?: "#FFFFFF" @@ -508,13 +523,8 @@ class ExoPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, // ExoPlayerDelegate override fun onPropertyChange(name: String, value: Any?) { - eventSink?.success( - mapOf( - "type" to "property", - "name" to name, - "value" to value - ) - ) + val propId = nameToId[name] ?: return + eventSink?.success(listOf(propId, value)) } override fun onEvent(name: String, data: Map?) { diff --git a/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerPlugin.kt b/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerPlugin.kt index 6511155e..3fd96766 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerPlugin.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerPlugin.kt @@ -25,6 +25,7 @@ class MpvPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, private var playerCore: MpvPlayerCore? = null private var activity: Activity? = null private var activityBinding: ActivityPluginBinding? = null + private val nameToId = mutableMapOf() // FlutterPlugin @@ -175,12 +176,14 @@ class MpvPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, private fun handleObserveProperty(call: MethodCall, result: MethodChannel.Result) { val name = call.argument("name") val format = call.argument("format") + val id = call.argument("id") - if (name == null || format == null) { - result.error("INVALID_ARGS", "Missing 'name' or 'format'", null) + if (name == null || format == null || id == null) { + result.error("INVALID_ARGS", "Missing 'name', 'format', or 'id'", null) return } + nameToId[name] = id playerCore?.observeProperty(name, format) result.success(null) } @@ -272,13 +275,8 @@ class MpvPlayerPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, // MpvPlayerDelegate override fun onPropertyChange(name: String, value: Any?) { - eventSink?.success( - mapOf( - "type" to "property", - "name" to name, - "value" to value - ) - ) + val propId = nameToId[name] ?: return + eventSink?.success(listOf(propId, value)) } override fun onEvent(name: String, data: Map?) { diff --git a/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift b/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift index d8bb9945..cb74ad5c 100644 --- a/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift +++ b/ios/Runner/MpvPlayer/MpvPlayerPlugin.swift @@ -9,6 +9,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD private var playerCore: MpvPlayerCore? private var eventSink: FlutterEventSink? private weak var registrar: FlutterPluginRegistrar? + private var nameToId: [String: Int] = [:] // MARK: - FlutterPlugin Registration @@ -162,11 +163,13 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD private func handleObserveProperty(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let args = call.arguments as? [String: Any], let name = args["name"] as? String, - let format = args["format"] as? String else { - result(FlutterError(code: "INVALID_ARGS", message: "Missing 'name' or 'format' argument", details: nil)) + let format = args["format"] as? String, + let id = args["id"] as? Int else { + result(FlutterError(code: "INVALID_ARGS", message: "Missing 'name', 'format', or 'id' argument", details: nil)) return } + nameToId[name] = id playerCore?.observeProperty(name, format: format) result(nil) } @@ -220,13 +223,9 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD func onPropertyChange(name: String, value: Any?) { guard let eventSink = eventSink else { return } - var event: [String: Any] = ["type": "property", "name": name] - - if let value = value { - event["value"] = value + if let propId = nameToId[name] { + eventSink([propId, value as Any]) } - - eventSink(event) } func onEvent(name: String, data: [String: Any]?) { diff --git a/lib/mpv/player/player_android.dart b/lib/mpv/player/player_android.dart index 7850bc21..ab0d76ed 100644 --- a/lib/mpv/player/player_android.dart +++ b/lib/mpv/player/player_android.dart @@ -51,6 +51,19 @@ class PlayerAndroid extends PlayerBase { if (!initialized) { throw Exception('Failed to initialize ExoPlayer'); } + + // Register property observers so the plugin knows propId mappings + await observeProperty('time-pos', 'double'); + await observeProperty('duration', 'double'); + await observeProperty('pause', 'flag'); + await observeProperty('paused-for-cache', 'flag'); + await observeProperty('track-list', 'string'); + await observeProperty('eof-reached', 'flag'); + await observeProperty('volume', 'double'); + await observeProperty('speed', 'double'); + await observeProperty('aid', 'string'); + await observeProperty('sid', 'string'); + await observeProperty('demuxer-cache-time', 'double'); } catch (e) { errorController.add('Initialization failed: $e'); rethrow; diff --git a/lib/mpv/player/player_base.dart b/lib/mpv/player/player_base.dart index 6b73ae9d..dd8c60fb 100644 --- a/lib/mpv/player/player_base.dart +++ b/lib/mpv/player/player_base.dart @@ -37,7 +37,11 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player { StreamSubscription? _eventSubscription; bool _disposed = false; - DateTime? _lastPositionEmit; + final _throttleSw = Stopwatch()..start(); + int _lastEmitMs = 0; + int _positionMs = 0; + int _nextPropId = 0; + final Map _propIdToName = {}; /// Whether the player has been initialized. /// Subclasses should set this to true after initialization. @@ -90,16 +94,31 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player { ); } + /// Observes a property on the native player and assigns it a compact propId + /// for efficient event channel communication. + @protected + Future observeProperty(String name, String format) async { + final propId = _nextPropId++; + _propIdToName[propId] = name; + await methodChannel.invokeMethod('observeProperty', { + 'name': name, + 'format': format, + 'id': propId, + }); + } + void _handleEvent(dynamic event) { - if (event is! Map) return; - - final type = event['type'] as String?; - final name = event['name'] as String?; - - if (type == 'property' && name != null) { - handlePropertyChange(name, event['value']); - } else if (type == 'event' && name != null) { - handlePlayerEvent(name, event['data'] as Map?); + if (event is List && event.length == 2) { + final name = _propIdToName[event[0]]; + if (name != null) { + handlePropertyChange(name, event[1]); + } + } else if (event is Map) { + final type = event['type'] as String?; + final name = event['name'] as String?; + if (type == 'event' && name != null) { + handlePlayerEvent(name, event['data'] as Map?); + } } } @@ -127,14 +146,15 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player { case 'time-pos': if (value is num) { - final position = Duration(milliseconds: (value * 1000).toInt()); - _state = _state.copyWith(position: position); - // Throttle stream emissions to ~4Hz (250ms) to reduce listener/rebuild pressure. - // _state is always updated above so synchronous reads stay current. - final now = DateTime.now(); - if (_lastPositionEmit == null || now.difference(_lastPositionEmit!).inMilliseconds >= 250) { - _lastPositionEmit = now; - positionController.add(position); + _positionMs = (value * 1000).round(); + // Only allocate Duration + copyWith + emit at ~4Hz (250ms). + // Raw int is stored every tick so synchronous reads via _positionMs stay current. + final nowMs = _throttleSw.elapsedMilliseconds; + if (nowMs - _lastEmitMs >= 250) { + _lastEmitMs = nowMs; + final pos = Duration(milliseconds: _positionMs); + _state = _state.copyWith(position: pos); + positionController.add(pos); } } break; diff --git a/lib/mpv/player/player_native.dart b/lib/mpv/player/player_native.dart index a45c9539..e982064d 100644 --- a/lib/mpv/player/player_native.dart +++ b/lib/mpv/player/player_native.dart @@ -42,26 +42,22 @@ class PlayerNative extends PlayerBase { await _configureSubtitleFonts(); // Subscribe to MPV properties - await _observeProperty('time-pos', 'double'); - await _observeProperty('duration', 'double'); - await _observeProperty('pause', 'flag'); - await _observeProperty('paused-for-cache', 'flag'); - await _observeProperty('track-list', (Platform.isAndroid || Platform.isWindows) ? 'string' : 'node'); - await _observeProperty('eof-reached', 'flag'); - await _observeProperty('volume', 'double'); - await _observeProperty('speed', 'double'); - await _observeProperty('aid', 'string'); - await _observeProperty('sid', 'string'); + await observeProperty('time-pos', 'double'); + await observeProperty('duration', 'double'); + await observeProperty('pause', 'flag'); + await observeProperty('paused-for-cache', 'flag'); + await observeProperty('track-list', (Platform.isAndroid || Platform.isWindows) ? 'string' : 'node'); + await observeProperty('eof-reached', 'flag'); + await observeProperty('volume', 'double'); + await observeProperty('speed', 'double'); + await observeProperty('aid', 'string'); + await observeProperty('sid', 'string'); } catch (e) { errorController.add('Initialization failed: $e'); rethrow; } } - Future _observeProperty(String name, String format) async { - await methodChannel.invokeMethod('observeProperty', {'name': name, 'format': format}); - } - /// Configures subtitle fonts for libass support. /// Provides a comprehensive Unicode font (Go Noto) with CJK coverage to ensure /// proper rendering of non-Latin characters in subtitles. diff --git a/linux/runner/mpv/mpv_player.cc b/linux/runner/mpv/mpv_player.cc index 3c7a214d..2e4f4980 100644 --- a/linux/runner/mpv/mpv_player.cc +++ b/linux/runner/mpv/mpv_player.cc @@ -249,7 +249,8 @@ std::string MpvPlayer::GetProperty(const std::string& name) { } void MpvPlayer::ObserveProperty(const std::string& name, - const std::string& format) { + const std::string& format, + int id) { if (disposed_ || !mpv_) return; // Check if already observing. @@ -257,6 +258,8 @@ void MpvPlayer::ObserveProperty(const std::string& name, return; } + name_to_id_[name] = id; + mpv_format mpv_fmt = MPV_FORMAT_NONE; if (format == "string") { mpv_fmt = MPV_FORMAT_STRING; @@ -515,22 +518,24 @@ FlValue* MpvPlayer::NodeToFlValue(mpv_node* node) { } void MpvPlayer::SendPropertyChange(const char* name, mpv_node* data) { - FlValue* event_map = fl_value_new_map(); - fl_value_set_string_take(event_map, "type", fl_value_new_string("property")); - fl_value_set_string_take(event_map, "name", - fl_value_new_string(name ? name : "")); + if (!name) return; + auto it = name_to_id_.find(name); + if (it == name_to_id_.end()) return; + + FlValue* list = fl_value_new_list(); + fl_value_append_take(list, fl_value_new_int(it->second)); if (data) { - fl_value_set_string_take(event_map, "value", NodeToFlValue(data)); + fl_value_append_take(list, NodeToFlValue(data)); } else { - fl_value_set_string_take(event_map, "value", fl_value_new_null()); + fl_value_append_take(list, fl_value_new_null()); } std::lock_guard lock(callback_mutex_); if (event_callback_) { - event_callback_(event_map); + event_callback_(list); } - fl_value_unref(event_map); + fl_value_unref(list); } void MpvPlayer::SendEvent(const std::string& name, FlValue* data) { diff --git a/linux/runner/mpv/mpv_player.h b/linux/runner/mpv/mpv_player.h index 6746706f..0b6ca69a 100644 --- a/linux/runner/mpv/mpv_player.h +++ b/linux/runner/mpv/mpv_player.h @@ -71,7 +71,9 @@ class MpvPlayer { /// Changes will be reported via the event callback. /// @param name Property name to observe. /// @param format Format type ("string", "flag", "int64", "double", "node"). - void ObserveProperty(const std::string& name, const std::string& format); + /// @param id Property ID assigned by Dart for compact event encoding. + void ObserveProperty(const std::string& name, const std::string& format, + int id); /// Renders a frame to the current OpenGL context. /// Must be called from the GTK render callback. @@ -133,6 +135,7 @@ class MpvPlayer { uint64_t next_reply_userdata_ = 1; std::map observed_properties_; + std::map name_to_id_; // Pending async commands: request_id -> callback std::map pending_commands_; diff --git a/linux/runner/mpv/mpv_plugin.cc b/linux/runner/mpv/mpv_plugin.cc index f49196de..0145b876 100644 --- a/linux/runner/mpv/mpv_plugin.cc +++ b/linux/runner/mpv/mpv_plugin.cc @@ -411,6 +411,7 @@ static void mpv_plugin_handle_method_call(FlMethodChannel* channel, } else { FlValue* name_value = fl_value_lookup_string(args, "name"); FlValue* format_value = fl_value_lookup_string(args, "format"); + FlValue* id_value = fl_value_lookup_string(args, "id"); if (name_value == nullptr || fl_value_get_type(name_value) != FL_VALUE_TYPE_STRING) { @@ -420,9 +421,14 @@ static void mpv_plugin_handle_method_call(FlMethodChannel* channel, fl_value_get_type(format_value) != FL_VALUE_TYPE_STRING) { response = FL_METHOD_RESPONSE(fl_method_error_response_new( "INVALID_ARGS", "Missing 'format'", nullptr)); + } else if (id_value == nullptr || + fl_value_get_type(id_value) != FL_VALUE_TYPE_INT) { + response = FL_METHOD_RESPONSE(fl_method_error_response_new( + "INVALID_ARGS", "Missing 'id'", nullptr)); } else { self->player->ObserveProperty(fl_value_get_string(name_value), - fl_value_get_string(format_value)); + fl_value_get_string(format_value), + static_cast(fl_value_get_int(id_value))); response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); } } diff --git a/macos/Runner/MpvPlayer/MpvPlayerCore.swift b/macos/Runner/MpvPlayer/MpvPlayerCore.swift index c3b4d5ed..a9143d2f 100644 --- a/macos/Runner/MpvPlayer/MpvPlayerCore.swift +++ b/macos/Runner/MpvPlayer/MpvPlayerCore.swift @@ -98,11 +98,48 @@ class MpvPlayerCore: NSObject { return false } + // Register for fullscreen notifications to avoid MoltenVK swapchain crash + let nc = NotificationCenter.default + nc.addObserver(self, selector: #selector(windowWillEnterFullScreen), + name: NSWindow.willEnterFullScreenNotification, object: window) + nc.addObserver(self, selector: #selector(windowDidEnterFullScreen), + name: NSWindow.didEnterFullScreenNotification, object: window) + nc.addObserver(self, selector: #selector(windowWillExitFullScreen), + name: NSWindow.willExitFullScreenNotification, object: window) + nc.addObserver(self, selector: #selector(windowDidExitFullScreen), + name: NSWindow.didExitFullScreenNotification, object: window) + isInitialized = true print("[MpvPlayerCore] Initialized successfully with MPV") return true } + // MARK: - Fullscreen Transition Handling + + @objc private func windowWillEnterFullScreen(_ notification: Notification) { + guard mpv != nil else { return } + print("[MpvPlayerCore] willEnterFullScreen — disabling video output") + mpv_set_property_string(mpv, "vid", "no") + } + + @objc private func windowDidEnterFullScreen(_ notification: Notification) { + guard mpv != nil else { return } + print("[MpvPlayerCore] didEnterFullScreen — re-enabling video output") + mpv_set_property_string(mpv, "vid", "auto") + } + + @objc private func windowWillExitFullScreen(_ notification: Notification) { + guard mpv != nil else { return } + print("[MpvPlayerCore] willExitFullScreen — disabling video output") + mpv_set_property_string(mpv, "vid", "no") + } + + @objc private func windowDidExitFullScreen(_ notification: Notification) { + guard mpv != nil else { return } + print("[MpvPlayerCore] didExitFullScreen — re-enabling video output") + mpv_set_property_string(mpv, "vid", "auto") + } + private func setupMpv() -> Bool { guard let metalLayer = metalLayer else { return false } @@ -508,6 +545,8 @@ class MpvPlayerCore: NSObject { // MARK: - Cleanup func dispose() { + NotificationCenter.default.removeObserver(self) + // Cancel any pending async commands pendingCommandsLock.lock() let pending = pendingCommands diff --git a/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift b/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift index a516e470..8b5bf58f 100644 --- a/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift +++ b/macos/Runner/MpvPlayer/MpvPlayerPlugin.swift @@ -9,6 +9,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD private var playerCore: MpvPlayerCore? private var eventSink: FlutterEventSink? private weak var registrar: FlutterPluginRegistrar? + private var nameToId: [String: Int] = [:] // MARK: - FlutterPlugin Registration @@ -162,11 +163,13 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD private func handleObserveProperty(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let args = call.arguments as? [String: Any], let name = args["name"] as? String, - let format = args["format"] as? String else { - result(FlutterError(code: "INVALID_ARGS", message: "Missing 'name' or 'format' argument", details: nil)) + let format = args["format"] as? String, + let id = args["id"] as? Int else { + result(FlutterError(code: "INVALID_ARGS", message: "Missing 'name', 'format', or 'id' argument", details: nil)) return } + nameToId[name] = id playerCore?.observeProperty(name, format: format) result(nil) } @@ -220,13 +223,9 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD func onPropertyChange(name: String, value: Any?) { guard let eventSink = eventSink else { return } - var event: [String: Any] = ["type": "property", "name": name] - - if let value = value { - event["value"] = value + if let propId = nameToId[name] { + eventSink([propId, value as Any]) } - - eventSink(event) } func onEvent(name: String, data: [String: Any]?) { diff --git a/windows/runner/mpv/mpv_player.cpp b/windows/runner/mpv/mpv_player.cpp index 3f36d5d9..5e1d6a34 100644 --- a/windows/runner/mpv/mpv_player.cpp +++ b/windows/runner/mpv/mpv_player.cpp @@ -212,7 +212,8 @@ std::string MpvPlayer::GetProperty(const std::string& name) { } void MpvPlayer::ObserveProperty(const std::string& name, - const std::string& format) { + const std::string& format, + int id) { if (!mpv_) return; // Check if already observing. @@ -220,6 +221,8 @@ void MpvPlayer::ObserveProperty(const std::string& name, return; } + name_to_id_[name] = id; + mpv_format mpv_fmt = MPV_FORMAT_NONE; if (format == "string") { mpv_fmt = MPV_FORMAT_STRING; @@ -418,11 +421,10 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) { } void MpvPlayer::SendPropertyChange(const char* name, mpv_node* data) { - flutter::EncodableMap event; - event[flutter::EncodableValue("type")] = - flutter::EncodableValue("property"); - event[flutter::EncodableValue("name")] = - flutter::EncodableValue(name ? name : ""); + if (!name) return; + + auto it = name_to_id_.find(name); + if (it == name_to_id_.end()) return; flutter::EncodableValue value; if (data) { @@ -445,11 +447,14 @@ void MpvPlayer::SendPropertyChange(const char* name, mpv_node* data) { break; } } - event[flutter::EncodableValue("value")] = value; + + flutter::EncodableList list; + list.push_back(flutter::EncodableValue(it->second)); + list.push_back(value); std::lock_guard lock(callback_mutex_); if (event_callback_) { - event_callback_(event); + event_callback_(flutter::EncodableValue(list)); } } @@ -464,7 +469,7 @@ void MpvPlayer::SendEvent(const std::string& name, std::lock_guard lock(callback_mutex_); if (event_callback_) { - event_callback_(event); + event_callback_(flutter::EncodableValue(event)); } } diff --git a/windows/runner/mpv/mpv_player.h b/windows/runner/mpv/mpv_player.h index 750f020f..98707f16 100644 --- a/windows/runner/mpv/mpv_player.h +++ b/windows/runner/mpv/mpv_player.h @@ -22,7 +22,7 @@ namespace mpv { class MpvPlayer { public: using EventCallback = - std::function; + std::function; MpvPlayer(); ~MpvPlayer(); @@ -53,7 +53,8 @@ class MpvPlayer { std::string GetProperty(const std::string& name); // Observes an mpv property for changes. - void ObserveProperty(const std::string& name, const std::string& format); + void ObserveProperty(const std::string& name, const std::string& format, + int id); // Returns the mpv video window handle. HWND GetHwnd() const { return hwnd_; } @@ -90,6 +91,7 @@ class MpvPlayer { uint64_t next_reply_userdata_ = 1; std::map observed_properties_; + std::map name_to_id_; // Pending async commands: request_id -> callback std::map pending_commands_; diff --git a/windows/runner/mpv/mpv_plugin.cpp b/windows/runner/mpv/mpv_plugin.cpp index 6f07f97a..ea511309 100644 --- a/windows/runner/mpv/mpv_plugin.cpp +++ b/windows/runner/mpv/mpv_plugin.cpp @@ -150,7 +150,7 @@ void MpvPlayerPlugin::HandleMethodCall( if (success) { LogToFile("MPV Plugin: Player initialized successfully"); // Set up event callback. - player_->SetEventCallback([this](const flutter::EncodableMap& event) { + player_->SetEventCallback([this](const flutter::EncodableValue& event) { SendEvent(event); }); @@ -291,6 +291,7 @@ void MpvPlayerPlugin::HandleMethodCall( const auto& map = std::get(*args); auto name_it = map.find(flutter::EncodableValue("name")); auto format_it = map.find(flutter::EncodableValue("format")); + auto id_it = map.find(flutter::EncodableValue("id")); if (name_it == map.end() || !std::holds_alternative(name_it->second)) { @@ -302,9 +303,15 @@ void MpvPlayerPlugin::HandleMethodCall( result->Error("INVALID_ARGS", "Missing 'format'"); return; } + if (id_it == map.end() || + !std::holds_alternative(id_it->second)) { + result->Error("INVALID_ARGS", "Missing 'id'"); + return; + } player_->ObserveProperty(std::get(name_it->second), - std::get(format_it->second)); + std::get(format_it->second), + std::get(id_it->second)); result->Success(); } else if (method == "setVisible") { const auto* args = method_call.arguments(); @@ -382,9 +389,9 @@ void MpvPlayerPlugin::HandleMethodCall( } } -void MpvPlayerPlugin::SendEvent(const flutter::EncodableMap& event) { +void MpvPlayerPlugin::SendEvent(const flutter::EncodableValue& event) { if (event_sink_) { - event_sink_->Success(flutter::EncodableValue(event)); + event_sink_->Success(event); } } diff --git a/windows/runner/mpv/mpv_plugin.h b/windows/runner/mpv/mpv_plugin.h index 45e49655..2f4d2f7a 100644 --- a/windows/runner/mpv/mpv_plugin.h +++ b/windows/runner/mpv/mpv_plugin.h @@ -32,7 +32,7 @@ class MpvPlayerPlugin : public flutter::Plugin { const flutter::MethodCall& method_call, std::unique_ptr> result); - void SendEvent(const flutter::EncodableMap& event); + void SendEvent(const flutter::EncodableValue& event); HWND GetWindow(); HWND GetChildWindow();