From 992df91576027af7efdd8bde27ef9182cbf7ce46 Mon Sep 17 00:00:00 2001 From: c1yde3 Date: Mon, 14 Oct 2024 16:30:16 +0800 Subject: [PATCH 001/109] support find add-to-app mode flutter.sdk --- android/build.gradle | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 8f0b13a..3310461 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -92,7 +92,12 @@ def flutterSdkVersion = { file(rootDir.absolutePath + "/local.properties").withInputStream { properties.load(it) } def flutterSdkPath = properties.getProperty("flutter.sdk") if (flutterSdkPath == null) { - flutterSdkPath = System.env.FLUTTER_ROOT // from flutter.groovy + // add-to-app local.properties file located at /.android/local.properties + file(project(":flutter").getProjectDir().getParent() + "/local.properties").withInputStream { properties.load(it) } + flutterSdkPath = properties.getProperty("flutter.sdk") + if (flutterSdkPath == null) { + flutterSdkPath = System.env.FLUTTER_ROOT // from flutter.groovy + } } assert flutterSdkPath != null, "flutter.sdk not set in local.properties" def version = file(flutterSdkPath + "/version") From 9be9b1c80444e9ea72e54e3b1c6128f8a77ff64b Mon Sep 17 00:00:00 2001 From: c1yde3 <1054346243@qq.com> Date: Mon, 14 Oct 2024 17:04:27 +0800 Subject: [PATCH 002/109] change detect order --- android/build.gradle | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 3310461..c96286c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -92,11 +92,12 @@ def flutterSdkVersion = { file(rootDir.absolutePath + "/local.properties").withInputStream { properties.load(it) } def flutterSdkPath = properties.getProperty("flutter.sdk") if (flutterSdkPath == null) { - // add-to-app local.properties file located at /.android/local.properties - file(project(":flutter").getProjectDir().getParent() + "/local.properties").withInputStream { properties.load(it) } - flutterSdkPath = properties.getProperty("flutter.sdk") + flutterSdkPath = System.env.FLUTTER_ROOT // from flutter.groovy + if (flutterSdkPath == null) { - flutterSdkPath = System.env.FLUTTER_ROOT // from flutter.groovy + // add-to-app local.properties file located at /.android/local.properties + file(project(":flutter").getProjectDir().getParent() + "/local.properties").withInputStream { properties.load(it) } + flutterSdkPath = properties.getProperty("flutter.sdk") } } assert flutterSdkPath != null, "flutter.sdk not set in local.properties" From ad3efa4f3bc713456e36a423dc5d6bab2e82c19c Mon Sep 17 00:00:00 2001 From: julienbm Date: Tue, 26 Nov 2024 10:52:40 +0100 Subject: [PATCH 003/109] feat: add player extensions --- lib/src/controller.dart | 21 +++++++++++++++++++++ lib/src/video_player_mdk.dart | 21 ++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index acffc6e..b28c48e 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; import 'package:video_player/video_player.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'media_info.dart'; import 'video_player_mdk.dart' if (dart.library.js_interop) 'video_player_dummy.dart' if (dart.library.html) 'video_player_dummy.dart'; @@ -28,6 +29,11 @@ extension FVPControllerExtensions on VideoPlayerController { return _platform.isLive(textureId); } + /// Get current media info. + MediaInfo? getMediaInfo() { + return _platform.getMediaInfo(textureId); + } + /// set additional properties /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpropertyconst-stdstring-key-const-stdstring-value void setProperty(String name, String value) { @@ -118,6 +124,11 @@ extension FVPControllerExtensions on VideoPlayerController { _platform.setAudioTracks(textureId, value); } + /// Get active audio tracks. + List? getActiveAudioTracks() { + return _platform.getActiveAudioTracks(textureId); + } + /// Set active video tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external video data source via [setExternalVideo] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks @@ -125,6 +136,11 @@ extension FVPControllerExtensions on VideoPlayerController { _platform.setVideoTracks(textureId, value); } + /// Get active video tracks. + List? getActiveVideoTracks() { + return _platform.getActiveVideoTracks(textureId); + } + /// Set active subtitle tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external subtitle data source via [setExternalSubtitle] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks @@ -132,6 +148,11 @@ extension FVPControllerExtensions on VideoPlayerController { _platform.setSubtitleTracks(textureId, value); } + /// Get active subtitle tracks. + List? getActiveSubtitleTracks() { + return _platform.getActiveSubtitleTracks(textureId); + } + /// set an external audio data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalAudio(String uri) { diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index b7258b1..3aae396 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -10,9 +10,11 @@ import 'package:video_player_platform_interface/video_player_platform_interface. import 'package:logging/logging.dart'; import 'fvp_platform_interface.dart'; import 'extensions.dart'; +import 'media_info.dart'; import '../mdk.dart' as mdk; + final _log = Logger('fvp'); class MdkVideoPlayer extends mdk.Player { @@ -349,9 +351,10 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { return _players[textureId]?.isLive ?? false; } - //MediaInfo getMediaInfo() { - // - //} + MediaInfo? getMediaInfo(int textureId) { + return _players[textureId]?.mediaInfo; + } + void setProperty(int textureId, String name, String value) { _players[textureId]?.setProperty(name, value); } @@ -422,14 +425,26 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _players[textureId]?.activeAudioTracks = value; } + List? getActiveAudioTracks(int textureId) { + return _players[textureId]?.activeAudioTracks; + } + void setVideoTracks(int textureId, List value) { _players[textureId]?.activeVideoTracks = value; } + List? getActiveVideoTracks(int textureId) { + return _players[textureId]?.activeVideoTracks; + } + void setSubtitleTracks(int textureId, List value) { _players[textureId]?.activeSubtitleTracks = value; } + List? getActiveSubtitleTracks(int textureId) { + return _players[textureId]?.activeSubtitleTracks; + } + // external track. can select external tracks via setAudioTracks() void setExternalAudio(int textureId, String uri) { _players[textureId]?.setMedia(uri, mdk.MediaType.audio); From 6bcc2812d07831443434c4e22a4c7e1bc7bd1986 Mon Sep 17 00:00:00 2001 From: julienbm Date: Tue, 26 Nov 2024 15:17:16 +0100 Subject: [PATCH 004/109] fix web build --- lib/src/controller.dart | 4 +++- lib/src/media_info_dummy.dart | 4 ++++ lib/src/video_player_dummy.dart | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 lib/src/media_info_dummy.dart diff --git a/lib/src/controller.dart b/lib/src/controller.dart index b28c48e..c5d90a2 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -6,7 +6,9 @@ import 'dart:typed_data'; import 'package:video_player/video_player.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; -import 'media_info.dart'; +import 'media_info.dart' + if (dart.library.js_interop) 'media_info_dummy.dart' + if (dart.library.html) 'media_info_dummy.dart'; import 'video_player_mdk.dart' if (dart.library.js_interop) 'video_player_dummy.dart' if (dart.library.html) 'video_player_dummy.dart'; diff --git a/lib/src/media_info_dummy.dart b/lib/src/media_info_dummy.dart new file mode 100644 index 0000000..6bd3bf1 --- /dev/null +++ b/lib/src/media_info_dummy.dart @@ -0,0 +1,4 @@ +/// A dummy class for web +class MediaInfo { + +} \ No newline at end of file diff --git a/lib/src/video_player_dummy.dart b/lib/src/video_player_dummy.dart index e571d30..3e40405 100644 --- a/lib/src/video_player_dummy.dart +++ b/lib/src/video_player_dummy.dart @@ -1,5 +1,7 @@ import 'dart:typed_data'; +import 'media_info_dummy.dart'; + /// A dummy class for web class MdkVideoPlayerPlatform { static void registerVideoPlayerPlatformsWith({dynamic options}) {} @@ -9,6 +11,8 @@ class MdkVideoPlayerPlatform { return false; } + MediaInfo? getMediaInfo(int textureId) { return null; } + void setProperty(int textureId, String name, String value) {} void setAudioDecoders(int textureId, List value) {} @@ -37,12 +41,25 @@ class MdkVideoPlayerPlatform { void setHue(int textureId, double value) {} void setSaturation(int textureId, double value) {} + void setAudioTracks(int textureId, List value) {} + List? getActiveAudioTracks(int textureId) { + return null; + } + void setVideoTracks(int textureId, List value) {} + List? getActiveVideoTracks(int textureId) { + return null; + } + void setSubtitleTracks(int textureId, List value) {} + List? getActiveSubtitleTracks(int textureId) { + return null; + } + void setExternalAudio(int textureId, String uri) {} void setExternalVideo(int textureId, String uri) {} From 02ad38614abd0b5bffd17b5d32e938bc58175d20 Mon Sep 17 00:00:00 2001 From: Abdelaziz Mahdy Date: Wed, 11 Dec 2024 22:35:06 -0400 Subject: [PATCH 005/109] fix warning on android --- android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java index a32892f..39f9244 100644 --- a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java +++ b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java @@ -93,8 +93,8 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { sp.setCallback( new TextureRegistry.SurfaceProducer.Callback() { @Override - public void onSurfaceCreated() { - Log.d("FvpPlugin", "SurfaceProducer.onSurfaceCreated for textureId " + texId); + public void onSurfaceAvailable() { + Log.d("FvpPlugin", "SurfaceProducer.onSurfaceAvailable for textureId " + texId); final Surface newSurface = sp.getSurface(); surfaces.put(texId, newSurface); // will do nothing if same surface From 2e8d6bf730ecddaa61feaad8f30960fe85e6dee3 Mon Sep 17 00:00:00 2001 From: Abdelaziz Mahdy Date: Thu, 12 Dec 2024 10:00:16 -0400 Subject: [PATCH 006/109] add gradle condition to allow older versions of sdk --- android/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/build.gradle b/android/build.gradle index 8f0b13a..4ca025a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -122,4 +122,10 @@ if (flutterSdkVersionInt < 32400) { preprocessJava(['//// FLUTTER_3.24_BEGIN': '/\\*// FLUTTER_3.24_BEGIN-', '//// FLUTTER_3.24_END': '\\*/// FLUTTER_3.24_END-']) } else { preprocessJava(['/\\*// FLUTTER_3.24_BEGIN-': '//// FLUTTER_3.24_BEGIN', '\\*/// FLUTTER_3.24_END-': '//// FLUTTER_3.24_END']) + /// rename onSurfaceAvailable to onSurfaceCreated if flutterSdkVersionInt < 32700 + if (flutterSdkVersionInt < 32700) { + println 'rename onSurfaceAvailable to onSurfaceCreated' + preprocessJava(['onSurfaceAvailable': 'onSurfaceCreated']) + } } + From 5b44cebaf65107b0087cad491625b922a71c6a1f Mon Sep 17 00:00:00 2001 From: wang-bin Date: Fri, 13 Dec 2024 10:12:14 +0800 Subject: [PATCH 007/109] fix warnings, format flutter analyze lib/src/player.dart: Color.red is deprecated, remove --- lib/fvp.dart | 2 +- lib/src/controller.dart | 2 +- lib/src/media_info_dummy.dart | 4 +--- lib/src/player.dart | 9 +-------- lib/src/video_player_dummy.dart | 4 +++- lib/src/video_player_mdk.dart | 1 - 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/fvp.dart b/lib/fvp.dart index f2b1f12..0dde27f 100644 --- a/lib/fvp.dart +++ b/lib/fvp.dart @@ -8,7 +8,7 @@ import 'src/video_player_mdk.dart' export 'src/controller.dart'; -/// Registers this plugin as the default instance of [VideoPlayerPlatform]. Then your [VideoPlayer] will support all platforms. +/// Registers this plugin as the default instance of VideoPlayerPlatform. Then your [VideoPlayer] will support all platforms. /// If registerWith is not called, the previous(usually official) implementation will be used when available. /// [options] can be diff --git a/lib/src/controller.dart b/lib/src/controller.dart index c5d90a2..7496f2c 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -94,7 +94,7 @@ extension FVPControllerExtensions on VideoPlayerController { } /// Step forward or backward. - /// Step forward if [frame] > 0, backward otherwise. + /// Step forward if [frames] > 0, backward otherwise. Future step({int frames = 1}) async { return _platform.step(textureId, frames); } diff --git a/lib/src/media_info_dummy.dart b/lib/src/media_info_dummy.dart index 6bd3bf1..53314fb 100644 --- a/lib/src/media_info_dummy.dart +++ b/lib/src/media_info_dummy.dart @@ -1,4 +1,2 @@ /// A dummy class for web -class MediaInfo { - -} \ No newline at end of file +class MediaInfo {} diff --git a/lib/src/player.dart b/lib/src/player.dart index d01a67c..7a3acd0 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -173,7 +173,7 @@ class Player { /// Release current texture then create a new one for current [media], and update [textureId]. /// - /// Texture will be created when media is loaded and [mediaInfo.video] is not empty. + /// Texture will be created when media is loaded and mediaInfo.video is not empty. /// If both [width] and [height] are null, texture size is video frame size, otherwise is requested size. Future updateTexture( {int? width, int? height, bool? tunnel, bool? fit}) async { @@ -615,13 +615,6 @@ class Player { Pointer)>()( _player.ref.object, r, g, b, a, Pointer.fromAddress(0)); - /// Set background color. - /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setbackgroundcolorfloat-r-float-g-float-b-float-a-void-vo_opaque--nullptr - void setBackground(ui.Color c) => _player.ref.setBackgroundColor.asFunction< - void Function(Pointer, double, double, double, double, - Pointer)>()(_player.ref.object, c.red / 255, c.green / 255, - c.blue / 255, c.alpha / 255, Pointer.fromAddress(0)); - /// Set a built-in video effect. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-setvideoeffect-effect-const-float-values-void-vo_opaque--nullptr void setVideoEffect(VideoEffect effect, List value) { diff --git a/lib/src/video_player_dummy.dart b/lib/src/video_player_dummy.dart index 3e40405..f3e8291 100644 --- a/lib/src/video_player_dummy.dart +++ b/lib/src/video_player_dummy.dart @@ -11,7 +11,9 @@ class MdkVideoPlayerPlatform { return false; } - MediaInfo? getMediaInfo(int textureId) { return null; } + MediaInfo? getMediaInfo(int textureId) { + return null; + } void setProperty(int textureId, String name, String value) {} diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 3aae396..f367efa 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -14,7 +14,6 @@ import 'media_info.dart'; import '../mdk.dart' as mdk; - final _log = Logger('fvp'); class MdkVideoPlayer extends mdk.Player { From dad3071d7e568b36f1afac8629f5f2cf31128269 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Fri, 13 Dec 2024 13:50:01 +0800 Subject: [PATCH 008/109] ci: build beta channel only stable and beta --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d701202..957bbe7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ on: jobs: macOS: - runs-on: macos-14 + runs-on: macos-15 defaults: run: working-directory: example @@ -47,7 +47,7 @@ jobs: path: example/fvp_example_macos.7z iOS: - runs-on: macos-14 + runs-on: macos-15 defaults: run: working-directory: example @@ -83,10 +83,10 @@ jobs: fail-fast: false matrix: version: ['3.19.x', 'any'] # 3.19 for win7 - channel: ['stable', 'master'] + channel: ['stable', 'beta'] exclude: - version: '3.19.x' - channel: 'master' + channel: 'beta' steps: - uses: actions/checkout@v4 with: @@ -118,10 +118,10 @@ jobs: fail-fast: false matrix: version: ['3.10.x', 'any'] - channel: ['stable', 'master'] + channel: ['stable', 'beta'] exclude: - version: '3.10.x' - channel: 'master' + channel: 'beta' steps: - uses: actions/checkout@v4 with: From 409227dc18208683d006b3c8013188e80e92ab42 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 17 Dec 2024 12:03:57 +0800 Subject: [PATCH 009/109] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a155d2..47af6b8 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ project is create with `flutter create -t plugin --platforms=linux,macos,windows ## How to Use -- Add [fvp](https://pub.dev/packages/fvp) in your pubspec.yaml dependencies: `flutter pub add fvp` +- Add [fvp](https://pub.dev/packages/fvp) in your pubspec.yaml dependencies: `flutter pub add fvp`. Since flutter 3.27, fvp must be a direct dependency in your app's pubspec.yaml. - **(Optional)** Add 2 lines in your video_player examples. Without this step, this plugin will be used for video_player unsupported platforms(windows, linux), official implementation will be used otherwise. ```dart From 7090c3a926b295747cccb1333895417ab8a85584 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 19 Dec 2024 16:12:20 +0800 Subject: [PATCH 010/109] remove video_player: video_player, requires manually registerWith() since flutter 3.27, only 1 implementation will be included(see #192). multiple implementations will conflict and fail to build. now you have to add fvp to your app's direct dependencies. should fix #170, #196. --- README.md | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 47af6b8..39f9e22 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ project is create with `flutter create -t plugin --platforms=linux,macos,windows ## How to Use - Add [fvp](https://pub.dev/packages/fvp) in your pubspec.yaml dependencies: `flutter pub add fvp`. Since flutter 3.27, fvp must be a direct dependency in your app's pubspec.yaml. -- **(Optional)** Add 2 lines in your video_player examples. Without this step, this plugin will be used for video_player unsupported platforms(windows, linux), official implementation will be used otherwise. +- **(Optional for flutter < 3.27 and fvp <= 0.28)** Add 2 lines in your video_player examples. Without this step, this plugin will be used for video_player unsupported platforms(windows, linux), official implementation will be used otherwise. ```dart import 'package:fvp/fvp.dart' as fvp; diff --git a/pubspec.yaml b/pubspec.yaml index 69d52ac..2906081 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,7 +46,7 @@ flutter: # All these are used by the tooling to maintain consistency when # adding or updating assets for this project. plugin: - implements: video_player + #implements: video_player # flutter 3.27+ can only select 1 implementation platforms: android: package: com.mediadevkit.fvp From 4c107cd55abc1704706e3de211cf52cd17b08adc Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 21 Dec 2024 13:59:06 +0800 Subject: [PATCH 011/109] fix resetting VideoPlayerPlatform.instance #170 --- lib/fvp.dart | 2 +- lib/src/video_player_mdk.dart | 5 ++++- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/fvp.dart b/lib/fvp.dart index 0dde27f..2fac509 100644 --- a/lib/fvp.dart +++ b/lib/fvp.dart @@ -47,7 +47,7 @@ void registerWith({dynamic options}) { MdkVideoPlayerPlatform.registerVideoPlayerPlatformsWith(options: options); } -/// Registers this plugin automatically by dart tooling. requires `implements: video_player` and `dartPluginClass: VideoPlayerRegistrant` in pubspec.yaml +/// Registers this plugin automatically by dart tooling. requires `dartPluginClass: VideoPlayerRegistrant` in pubspec.yaml class VideoPlayerRegistrant { static void registerWith() { MdkVideoPlayerPlatform.registerVideoPlayerPlatformsWith(); diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index f367efa..4044c69 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -126,8 +126,11 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { final platforms = options['platforms']; if (platforms is List) { if (!platforms.contains(Platform.operatingSystem)) { - if (_prevImpl != null) { + if (_prevImpl != null && + VideoPlayerPlatform.instance is MdkVideoPlayerPlatform) { // null if it's the 1st time to call registerWith() including current platform + // if current is not MdkVideoPlayerPlatform, another plugin may set instance + // if current is MdkVideoPlayerPlatform, we have to restore instance, _prevImpl is correct and no one changed instance VideoPlayerPlatform.instance = _prevImpl!; } return; diff --git a/pubspec.yaml b/pubspec.yaml index 2906081..00282b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,7 @@ flutter: pluginClass: FvpPlugin sharedDarwinSource: true linux: - dartPluginClass: VideoPlayerRegistrant + dartPluginClass: VideoPlayerRegistrant # auto registered in .dart_tool/flutter_build/dart_plugin_registrant.dart pluginClass: FvpPlugin macos: pluginClass: FvpPlugin From 2e2ad8a42d0cad9fef0de997d957a74242c53e57 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 21 Dec 2024 22:24:52 +0800 Subject: [PATCH 012/109] fix main() is not executed when debugging #125 #183 --- lib/src/video_player_mdk.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 4044c69..79f9bdb 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -163,6 +163,17 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _decoders = vd[Platform.operatingSystem]; } +// delay: ensure log handler is set in main(), blank window if run with debugger. +// registerWith() can be invoked by dart_plugin_registrant.dart before main. when debugging, won't enter main if posting message from native to dart(new native log message) before main? + Future.delayed(const Duration(milliseconds: 0), () { + _setupMdk(); + }); + + _prevImpl ??= VideoPlayerPlatform.instance; + VideoPlayerPlatform.instance = MdkVideoPlayerPlatform(); + } + + static void _setupMdk() { mdk.setLogHandler((level, msg) { if (msg.endsWith('\n')) { msg = msg.substring(0, msg.length - 1); @@ -190,10 +201,6 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _globalOpts?.forEach((key, value) { mdk.setGlobalOption(key, value); }); - - // if VideoPlayerPlatform.instance.runtimeType.toString() != '_PlaceholderImplementation' ? - _prevImpl ??= VideoPlayerPlatform.instance; - VideoPlayerPlatform.instance = MdkVideoPlayerPlatform(); } @override From f857be9c10de2221520b2edb0df65dce90c7cba2 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 23 Dec 2024 12:07:27 +0800 Subject: [PATCH 013/109] v0.29.0 --- CHANGELOG.md | 9 +++++++++ README.md | 2 +- darwin/fvp.podspec | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 135632b..e018586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.29.0 + +* not declared as video_player implementation in pubdec.yaml, fix conflicting with other implementations +* fix app does not launch when debugging +* android: use new onSurfaceAvailable() for flutter 3.27+ +* android: support add-to-app mode +* more VideoPlayerController extensions +* ios: cleanup when detaching from engine + ## 0.28.0 * mixWithOthers for iOS diff --git a/README.md b/README.md index 39f9e22..47af6b8 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ project is create with `flutter create -t plugin --platforms=linux,macos,windows ## How to Use - Add [fvp](https://pub.dev/packages/fvp) in your pubspec.yaml dependencies: `flutter pub add fvp`. Since flutter 3.27, fvp must be a direct dependency in your app's pubspec.yaml. -- **(Optional for flutter < 3.27 and fvp <= 0.28)** Add 2 lines in your video_player examples. Without this step, this plugin will be used for video_player unsupported platforms(windows, linux), official implementation will be used otherwise. +- **(Optional)** Add 2 lines in your video_player examples. Without this step, this plugin will be used for video_player unsupported platforms(windows, linux), official implementation will be used otherwise. ```dart import 'package:fvp/fvp.dart' as fvp; diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index d0faf60..5e81bb0 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.27.0' + s.version = '0.29.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.30.0' + s.dependency 'mdk', '~> 0.30.1' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 00282b9..bccdb74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.28.0 +version: 0.29.0 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 7e57a8d..4ba658f 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.28.0 +project(${PROJECT_NAME} VERSION 0.29.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 725612de558a41242b666868a6bfd2e9225a1a5f Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 30 Dec 2024 21:14:40 +0800 Subject: [PATCH 014/109] fix vo_opaque & snapshot for android fix #206, https://github.com/wang-bin/mdk-sdk/issues/273 --- android/fvp_plugin.cpp | 15 +++++++++++++++ lib/src/callbacks.cpp | 10 ++++++++-- lib/src/callbacks.h | 2 +- lib/src/lib.dart | 6 ++++-- lib/src/player.dart | 39 ++++++++++++++++++++++++++------------- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/android/fvp_plugin.cpp b/android/fvp_plugin.cpp index e7cdcbb..44e59c2 100644 --- a/android/fvp_plugin.cpp +++ b/android/fvp_plugin.cpp @@ -27,6 +27,7 @@ class TexturePlayer final : public mdk::Player int width = 0; int height = 0; jobject surface = nullptr; + void* vo_opaque = nullptr; // can change by TextureRegistry.SurfaceProducer.Callback private: }; @@ -82,6 +83,7 @@ Java_com_mediadevkit_fvp_FvpPlugin_nativeSetSurface(JNIEnv *env, jobject thiz, j player->setProperty("video.decoder", "surface=" + std::to_string((intptr_t)player->surface)); } else { player->updateNativeSurface(surface, w, h); + player->vo_opaque = surface; } players[tex_id] = player; } @@ -111,4 +113,17 @@ MdkIsEmulator() if (strstr(v, "emulator")) return true; return false; +} + +extern "C" +JNIEXPORT void* JNICALL +MdkGetPlayerVid(int64_t tex_id) +{ + if (tex_id < 0) + return nullptr; + if (const auto it = players.find(tex_id); it != players.end()) { + const auto& player = it->second; + return player->vo_opaque; + } + return nullptr; } \ No newline at end of file diff --git a/lib/src/callbacks.cpp b/lib/src/callbacks.cpp index 611a74f..dcaf0ee 100644 --- a/lib/src/callbacks.cpp +++ b/lib/src/callbacks.cpp @@ -428,7 +428,9 @@ FVP_EXPORT bool MdkSeek(int64_t handle, int64_t pos, int64_t seekFlags, void* po }); } -FVP_EXPORT bool MdkSnapshot(int64_t handle, int w, int h, void* post_c_object, int64_t send_port) +extern "C" void* MdkGetPlayerVid(int64_t texId); + +FVP_EXPORT bool MdkSnapshot(int64_t handle, int64_t texId, int w, int h, void* post_c_object, int64_t send_port) { const auto it = players.find(handle); if (it == players.cend()) { @@ -472,6 +474,10 @@ FVP_EXPORT bool MdkSnapshot(int64_t handle, int w, int h, void* post_c_object, i return {}; } return {}; - }); + } +#ifdef __ANDROID__ + , MdkGetPlayerVid(texId) +#endif + ); return true; } diff --git a/lib/src/callbacks.h b/lib/src/callbacks.h index 4357bd9..bc7fc7a 100644 --- a/lib/src/callbacks.h +++ b/lib/src/callbacks.h @@ -25,7 +25,7 @@ FVP_EXPORT void MdkCallbacksUnregisterType(int64_t handle, int type); FVP_EXPORT void MdkCallbacksReplyType(int64_t handle, int type, const void* data); FVP_EXPORT bool MdkPrepare(int64_t handle, int64_t pos, int64_t seekFlag, void* post_c_object, int64_t send_port);// prepare() with a callback to post result to dart to set Completer FVP_EXPORT bool MdkSeek(int64_t handle, int64_t pos, int64_t seekFlag, void* post_c_object, int64_t send_port); -FVP_EXPORT bool MdkSnapshot(int64_t handle, int w, int h, void* post_c_object, int64_t send_port); +FVP_EXPORT bool MdkSnapshot(int64_t handle, int64_t texId, int w, int h, void* post_c_object, int64_t send_port); enum CallbackType { Event, // not a callback, no need to wait for reply diff --git a/lib/src/lib.dart b/lib/src/lib.dart index 026f631..932e9bf 100644 --- a/lib/src/lib.dart +++ b/lib/src/lib.dart @@ -78,8 +78,10 @@ abstract class Libfvp { Bool Function(Int64, Int64, Int64, Pointer, Int64), bool Function(int, int, int, Pointer, int)>('MdkSeek'); static final snapshot = instance.lookupFunction< - Bool Function(Int64, Int, Int, Pointer, Int64), - bool Function(int, int, int, Pointer, int)>('MdkSnapshot'); + Bool Function(Int64, Int64, Int, Int, Pointer, Int64), + bool Function(int, int, int, int, Pointer, int)>('MdkSnapshot'); static final isEmulator = instance .lookupFunction('MdkIsEmulator'); + static final getVid = instance.lookupFunction Function(Int64), + Pointer Function(int)>('MdkGetPlayerVid'); } diff --git a/lib/src/player.dart b/lib/src/player.dart index 7a3acd0..ceb0fbe 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; import 'dart:ffi'; +import 'dart:io'; import 'dart:isolate'; import 'dart:math'; import 'dart:ui' as ui; @@ -577,13 +578,13 @@ class Player { void setVideoSurfaceSize(int width, int height) => _player.ref.setVideoSurfaceSize.asFunction< void Function(Pointer, int, int, Pointer)>()( - _player.ref.object, width, height, Pointer.fromAddress(0)); + _player.ref.object, width, height, _getVid()); void setVideoViewport(double x, double y, double width, double height) => _player.ref.setVideoViewport.asFunction< void Function(Pointer, double, double, double, double, Pointer)>()( - _player.ref.object, x, y, width, height, Pointer.fromAddress(0)); + _player.ref.object, x, y, width, height, _getVid()); /// Set video content aspect ratio. No effect if texture width/height == original video frame width/height. /// [value] can be [ignoreAspectRatio], [keepAspectRatio], [keepAspectRatioCrop] and other desired ratio = width/height @@ -591,7 +592,7 @@ class Player { /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setaspectratiofloat-value-void-vo_opaque--nullptr void setAspectRatio(double value) => _player.ref.setAspectRatio.asFunction< void Function(Pointer, double, Pointer)>()( - _player.ref.object, value, Pointer.fromAddress(0)); + _player.ref.object, value, _getVid()); // TODO: mapPoint( List) @@ -599,21 +600,20 @@ class Player { /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-rotateint-degree-void-vo_opaque--nullptr void rotate(int degree) => _player.ref.rotate .asFunction, int, Pointer)>()( - _player.ref.object, degree, Pointer.fromAddress(0)); + _player.ref.object, degree, _getVid()); /// scale video content. 1.0 is no scale. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-scalefloat-x-float-y-void-vo_opaque--nullptr void scale(double x, double y) => _player.ref.scale.asFunction< void Function(Pointer, double, double, Pointer)>()( - _player.ref.object, x, y, Pointer.fromAddress(0)); + _player.ref.object, x, y, _getVid()); /// Set background color. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setbackgroundcolorfloat-r-float-g-float-b-float-a-void-vo_opaque--nullptr void setBackgroundColor(double r, double g, double b, double a) => _player.ref.setBackgroundColor.asFunction< - void Function(Pointer, double, double, double, double, - Pointer)>()( - _player.ref.object, r, g, b, a, Pointer.fromAddress(0)); + void Function(Pointer, double, double, double, double, + Pointer)>()(_player.ref.object, r, g, b, a, _getVid()); /// Set a built-in video effect. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-setvideoeffect-effect-const-float-values-void-vo_opaque--nullptr @@ -625,7 +625,7 @@ class Player { _player.ref.setVideoEffect.asFunction< void Function( Pointer, int, Pointer, Pointer)>()( - _player.ref.object, effect.rawValue, cv.cast(), Pointer.fromAddress(0)); + _player.ref.object, effect.rawValue, cv.cast(), _getVid()); calloc.free(cv); } @@ -634,13 +634,13 @@ class Player { /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-setcolorspace-value-void-vo_opaque--nullptr void setColorSpace(ColorSpace value) => _player.ref.setColorSpace .asFunction, int, Pointer)>()( - _player.ref.object, value.rawValue, Pointer.fromAddress(0)); + _player.ref.object, value.rawValue, _getVid()); /// Draw the current video frame and return frame timestamp in seconds. /// Usually NOT used in dart. double renderVideo() => _player.ref.renderVideo .asFunction, Pointer)>()( - _player.ref.object, Pointer.fromAddress(0)); + _player.ref.object, _getVid()); /// Take a snapshot for current rendered frame. /// @@ -652,8 +652,13 @@ class Player { _snapshot?.complete(null); } _snapshot = Completer(); - if (!Libfvp.snapshot(nativeHandle, width ?? 0, height ?? 0, - NativeApi.postCObject.cast(), _receivePort.sendPort.nativePort)) { + if (!Libfvp.snapshot( + nativeHandle, + textureId.value ?? -1, + width ?? 0, + height ?? 0, + NativeApi.postCObject.cast(), + _receivePort.sendPort.nativePort)) { _snapshot!.complete(null); } return _snapshot!.future; @@ -727,6 +732,14 @@ class Player { _videoSize.complete(size); } + Pointer _getVid() { + // currently only android vo_opaque is not null, and may change + if (Platform.isAndroid) { + return Libfvp.getVid(textureId.value ?? -1); + } + return Pointer.fromAddress(0); + } + final _player = Libmdk.instance.mdkPlayerAPI_new(); var _pp = calloc>(); From 037796b883395267f9b0f2316db36172a4ef8b10 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 31 Dec 2024 16:39:12 +0800 Subject: [PATCH 015/109] update issue template to ensure log is not truncated --- .github/ISSUE_TEMPLATE/bug_report.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d155488..623c00a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,27 +8,37 @@ assignees: '' --- **Describe the bug** + A clear and concise description of what the bug is. **Expected behavior** + A clear and concise description of what you expected to happen. **Log** + Add -``` +```dart import 'package:logging/logging.dart'; import 'package:intl/intl.dart'; ``` add the following lines before `registerWith()` -``` +```dart Logger.root.level = Level.ALL; final df = DateFormat("HH:mm:ss.SSS"); Logger.root.onRecord.listen((record) { - print('${record.loggerName}.${record.level.name}: ${df.format(record.time)}: ${record.message}'); + debugPrint( + '${record.loggerName}.${record.level.name}: ${df.format(record.time)}: ${record.message}', + wrapWidth: 0x7FFFFFFFFFFFFFFF); }); ``` and + +
+log + ``` -Past log here +PASTE LOG HERE ``` +
From a02b921bb86cdd714a9564e09f7c79047ee9245e Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 21 Jan 2025 21:04:24 +0800 Subject: [PATCH 016/109] ci: add linux arm64 no arm64 in flutter-action --- .github/workflows/build.yml | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 957bbe7..5ee4172 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -146,6 +146,68 @@ jobs: name: fvp-example-linux-${{ matrix.channel }}-${{ matrix.version }} path: example/fvp_example_linux*.tar.xz + + Linux-arm64: + if: false + runs-on: ubuntu-24.04-arm + defaults: + run: + working-directory: example + strategy: + fail-fast: false + matrix: + version: ['any'] + channel: ['stable'] + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + channel: ${{ matrix.channel }} + cache: true + - run: | + sudo apt-get update -y + sudo apt-get install -y cmake clang ninja-build libgtk-3-dev libpulse-dev +# - run: flutter config --enable-linux-desktop + - run: flutter doctor --verbose + - run: flutter pub get + - run: flutter build linux --verbose + - run: mv build/linux/arm64/release/bundle . + - run: cmake -E tar Jcvf fvp_example_linux-arm64-${{ matrix.channel }}-${{ matrix.version }}.tar.xz bundle + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: fvp-example-linux-arm64-${{ matrix.channel }}-${{ matrix.version }} + path: example/fvp_example_linux*.tar.xz + + + Snap-arm64: + runs-on: ubuntu-24.04-arm + defaults: + run: + working-directory: example + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Install dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y cmake clang ninja-build libgtk-3-dev libpulse-dev + sudo snap install flutter --classic + - run: flutter doctor --verbose + - run: flutter pub get + - run: flutter build linux --verbose + - run: mv build/linux/arm64/release/bundle . + - run: cmake -E tar Jcvf fvp_example_linux-arm64-snap.tar.xz bundle + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: fvp-example-linux-arm64-snap + path: example/fvp_example_linux*.tar.xz + Snap: runs-on: ubuntu-latest defaults: From 01c745657c72324f8da9941e56c61f3f35fc3bd8 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 30 Jan 2025 09:25:22 +0800 Subject: [PATCH 017/109] ios: set audio playback category, fix #210 --- darwin/Classes/FvpPlugin.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/Classes/FvpPlugin.mm b/darwin/Classes/FvpPlugin.mm index 21c9395..2fead94 100644 --- a/darwin/Classes/FvpPlugin.mm +++ b/darwin/Classes/FvpPlugin.mm @@ -1,4 +1,4 @@ -// Copyright 2023-2024 Wang Bin. All rights reserved. +// Copyright 2023-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -130,6 +130,8 @@ + (void)registerWithRegistrar:(NSObject*)registrar { auto messenger = registrar.messenger; #else auto messenger = [registrar messenger]; + // Allow audio playback when the Ring/Silent switch is set to silent + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; #endif FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"fvp" binaryMessenger:messenger]; FvpPlugin* instance = [[FvpPlugin alloc] initWithRegistrar:registrar]; From 6a02294fd6a76c0deab2ed7fd19eb251062e6d33 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 18 Feb 2025 13:56:09 +0800 Subject: [PATCH 018/109] android: onSurfaceCleanup --- android/build.gradle | 15 +++++++++------ .../main/java/com/mediadevkit/fvp/FvpPlugin.java | 6 +++--- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index c6a7028..1f06351 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:8.5.0' // for compileSdk 34. default ndk 26.1.10909125 + classpath 'com.android.tools.build:gradle:8.7.0' } } @@ -27,7 +27,7 @@ android { } // Bumping the plugin compileSdkVersion requires all clients of this plugin // to bump the version in their app. - compileSdk 34 + compileSdk 35 // Use the NDK version // declared in /android/app/build.gradle file of the Flutter project. @@ -50,8 +50,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } defaultConfig { @@ -93,7 +93,7 @@ def flutterSdkVersion = { def flutterSdkPath = properties.getProperty("flutter.sdk") if (flutterSdkPath == null) { flutterSdkPath = System.env.FLUTTER_ROOT // from flutter.groovy - + if (flutterSdkPath == null) { // add-to-app local.properties file located at /.android/local.properties file(project(":flutter").getProjectDir().getParent() + "/local.properties").withInputStream { properties.load(it) } @@ -133,5 +133,8 @@ if (flutterSdkVersionInt < 32400) { println 'rename onSurfaceAvailable to onSurfaceCreated' preprocessJava(['onSurfaceAvailable': 'onSurfaceCreated']) } + if (flutterSdkVersionInt < 32800) { + println 'rename onSurfaceCleanup to onSurfaceDestroyed' + preprocessJava(['onSurfaceCleanup': 'onSurfaceDestroyed']) + } } - diff --git a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java index 39f9244..a8471e5 100644 --- a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java +++ b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 WangBin + * Copyright (c) 2023-2025 WangBin */ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -102,8 +102,8 @@ public void onSurfaceAvailable() { } @Override - public void onSurfaceDestroyed() { - Log.d("FvpPlugin", "SurfaceProducer.onSurfaceDestroyed for textureId " + texId); + public void onSurfaceCleanup() { + Log.d("FvpPlugin", "SurfaceProducer.onSurfaceCleanup for textureId " + texId); textures.remove(texId); nativeSetSurface(handle, texId, null, 0, 0, tunnel); } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 8d838b4..95c94ef 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -4,4 +4,4 @@ distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists # 8.0 does not support compileSdk 34 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip \ No newline at end of file diff --git a/example/android/settings.gradle b/example/android/settings.gradle index f6feeb0..02fc9a8 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -18,8 +18,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.5.0' apply false // compileSdk 34 - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "com.android.application" version '8.7.3' apply false // compileSdk 34 + id "org.jetbrains.kotlin.android" version "2.0.20" apply false } include ":app" \ No newline at end of file From cbfb02ddab90a6e2182e71b93b7d6992d965ce3c Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 18 Feb 2025 16:42:43 +0800 Subject: [PATCH 019/109] subtitleFontFile option value can be an http url --- example/.gitignore | 3 +++ lib/fvp.dart | 9 +++++---- lib/src/video_player_mdk.dart | 29 ++++++++++++++++++++++++++--- pubspec.yaml | 14 ++++++++------ 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/example/.gitignore b/example/.gitignore index 24476c5..05a059d 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -5,10 +5,13 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ +.cxx/ # IntelliJ related *.iml diff --git a/lib/fvp.dart b/lib/fvp.dart index 2fac509..1d5afc9 100644 --- a/lib/fvp.dart +++ b/lib/fvp.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -23,13 +23,13 @@ export 'src/controller.dart'; /// /// 'lowLatency': int. default is 0. reduce network stream latency. 1: for vod. 2: for live stream, may drop frames to ensure the latest content is displayed /// -/// "player": backend player properties of type Map. See https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpropertyconst-stdstring-key-const-stdstring-value +/// "player": backend player properties of type [Map]. See https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpropertyconst-stdstring-key-const-stdstring-value /// -/// "global": backend global options of type Map. See https://github.com/wang-bin/mdk-sdk/wiki/Global-Options +/// "global": backend global options of type [Map]. See https://github.com/wang-bin/mdk-sdk/wiki/Global-Options /// /// "tunnel": android only, default is false. AMediacodec/MediaCodec decoder output to a SurfaceTexture surface directly without OpenGL. Maybe more efficient, but some features are not supported, e.g. HDR tone mapping, less codecs. /// -/// 'subtitleFontFile': default subtitle font file as the fallback. If not set, 'assets/subfont.ttf' will be used, you can add it in pubspec.yaml if you need it. +/// 'subtitleFontFile': default subtitle font file as the fallback, can be an http url. If not set, 'assets/subfont.ttf' will be used, you can add it in pubspec.yaml if you need it. /// subfont.ttf can be downloaded from https://github.com/mpv-android/mpv-android/raw/master/app/src/main/assets/subfont.ttf /// /// Example: @@ -53,6 +53,7 @@ class VideoPlayerRegistrant { MdkVideoPlayerPlatform.registerVideoPlayerPlatformsWith(); } } + /* bool isRegistered() { return VideoPlayerPlatform.instance.runtimeType == MdkVideoPlayerPlatform; diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 79f9bdb..a4c93c4 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,8 @@ import 'package:flutter/widgets.dart'; // import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'package:logging/logging.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; import 'fvp_platform_interface.dart'; import 'extensions.dart'; import 'media_info.dart'; @@ -147,6 +149,7 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _tunnel = options["tunnel"]; _playerOpts = options['player']; _globalOpts = options['global']; + // TODO: _env => putenv _decoders = options['video.decoders']; _subtitleFontFile = options['subtitleFontFile']; } @@ -196,8 +199,28 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { // mdk.setGlobalOptions('plugins', 'mdk-braw'); mdk.setGlobalOption("log", "all"); mdk.setGlobalOption('d3d11.sync.cpu', 1); - mdk.setGlobalOption('subtitle.fonts.file', - PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); + if (_subtitleFontFile?.startsWith('http') ?? false) { + final fileName = _subtitleFontFile!.split('/').last; + getApplicationCacheDirectory().then((dir) { + final fontPath = '${dir.path}/$fileName'; + _log.fine('check font path: $fontPath'); + if (File(fontPath).existsSync()) { + mdk.setGlobalOption('subtitle.fonts.file', fontPath); + return; + } + _log.fine('downloading font file: $_subtitleFontFile'); + http.get(Uri.parse(_subtitleFontFile!)).then((response) { + if (response.statusCode == 200) { + _log.fine('save font file: $fontPath'); + File(fontPath).writeAsBytes(response.bodyBytes).then((_) { + mdk.setGlobalOption('subtitle.fonts.file', fontPath); + }); + } + }); + }); + } else { + mdk.setGlobalOption('subtitle.fonts.file', PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); + } _globalOpts?.forEach((key, value) { mdk.setGlobalOption(key, value); }); diff --git a/pubspec.yaml b/pubspec.yaml index bccdb74..f8eea55 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,8 +10,8 @@ topics: - videoplayer environment: - sdk: '>=3.0.0 <4.0.0' - flutter: ">=3.0.0" + sdk: ^3.0.0 + flutter: ^3.0.0 dependencies: ffi: ^2.1.0 @@ -22,6 +22,8 @@ dependencies: plugin_platform_interface: ^2.0.0 video_player: ^2.6.0 video_player_platform_interface: ^6.2.0 + path_provider: ^2.1.2 + http: ^1.0.0 dev_dependencies: flutter_test: @@ -46,7 +48,7 @@ flutter: # All these are used by the tooling to maintain consistency when # adding or updating assets for this project. plugin: - #implements: video_player # flutter 3.27+ can only select 1 implementation + #implements: video_player # endorsed. flutter 3.27+ can only select 1 implementation platforms: android: package: com.mediadevkit.fvp @@ -70,10 +72,10 @@ flutter: # - images/a_dot_ham.jpeg # # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages + # https://flutter.dev/to/asset-from-package # # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware + # https://flutter.dev/to/resolution-aware-images # To add custom fonts to your plugin package, add a fonts section here, # in this "flutter" section. Each entry in this list should have a @@ -93,4 +95,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages + # https://flutter.dev/to/font-from-package From 0a1112500b48245276ddd0a22e5f218ff7bff4ea Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 24 Feb 2025 14:42:09 +0800 Subject: [PATCH 020/109] v0.30.0 --- CHANGELOG.md | 7 +++++++ README.md | 10 ++++++++-- darwin/fvp.podspec | 4 ++-- pubspec.yaml | 4 ++-- windows/CMakeLists.txt | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e018586..53093a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.30.0 + +* `subtitleFontFile` option can be an http url +* ios: fix no sound in silent mode +* android: fix snapshot result is empty +* android: use `onSurfaceCleanup` for flutter >= 3.28 + ## 0.29.0 * not declared as video_player implementation in pubdec.yaml, fix conflicting with other implementations diff --git a/README.md b/README.md index 47af6b8..f89eaa4 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ project is create with `flutter create -t plugin --platforms=linux,macos,windows - Hardware decoders are enabled by default - Dolby Vision support on all platforms - Minimal code change for existing [Video Player](https://pub.dev/packages/video_player) apps -- Support most formats via FFmpeg demuxer and software decoders if not supported by gpu. You can use your own ffmpeg 4.0~7.0(or master branch) by removing bundled ffmpeg dynamic library. +- Support most formats via FFmpeg demuxer and software decoders if not supported by gpu. You can use your own ffmpeg 4.0~7.1(or master branch) by removing bundled ffmpeg dynamic library. - High performance. Lower cpu, gpu and memory load than libmpv based players. - Support audio without video +- HEVC(ios, mac only), VP8 and VP9 transparent video - Small footprint. Only about 10MB size increase per cpu architecture(platform dependent). @@ -103,7 +104,12 @@ For other platforms, set environment var `FVP_DEPS_LATEST=1` and rebuilt, will u libass is required, and it's added to your app automatically for windows, macOS and android(remove ass.dll, libass.dylib and libass.so from mdk-sdk if you don't need it). For iOS, [download](https://sourceforge.net/projects/mdk-sdk/files/deps/dep.7z/download) and add `ass.framework` to your xcode project. For linux, system libass can be used, you may have to install manually via system package manager. -If required subtitle font is not found in the system(e.g. android), you can add [assets/subfont.ttf](https://github.com/mpv-android/mpv-android/raw/master/app/src/main/assets/subfont.ttf) in pubspec.yaml as the fallback. +If required subtitle font is not found in the system(e.g. android), you can add [assets/subfont.ttf](https://github.com/mpv-android/mpv-android/raw/master/app/src/main/assets/subfont.ttf) in pubspec.yaml assets as the fallback. Optionally you can also download the font file by fvp like this +```dart + fvp.registerWith(options: { + 'subtitleFontFile': 'https://github.com/mpv-android/mpv-android/raw/master/app/src/main/assets/subfont.ttf' + }); +``` # DO NOT use flutter-snap Flutter can be installed by snap, but it will add some [enviroment vars(`CPLUS_INCLUDE_PATH` and `LIBRARY_PATH`) which may break C++ compiler](https://github.com/canonical/flutter-snap/blob/main/env.sh#L15-L18). It's not recommended to use snap, althrough building for linux is [fixed](https://github.com/wang-bin/fvp/commit/567c68270ba16b95b1198ae58850707ae4ad7b22), but it's not possible for android. diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 5e81bb0..9bcffab 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.29.0' + s.version = '0.30.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.30.1' + s.dependency 'mdk', '~> 0.31.0' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index f8eea55..3da2f1e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.29.0 +version: 0.30.0 homepage: https://github.com/wang-bin/fvp topics: - video @@ -29,7 +29,7 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: any - ffigen: '>=9.0.0' + ffigen: ^9.0.0 lints: any # For information on the generic Dart part of this file, see the diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4ba658f..d94c523 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.29.0 +project(${PROJECT_NAME} VERSION 0.30.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From b160cba76c372a0710aad173f4d0396ee5e0849d Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 24 Feb 2025 15:48:46 +0800 Subject: [PATCH 021/109] format --- lib/src/controller.dart | 5 +++-- lib/src/video_player_mdk.dart | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 7496f2c..22d59c3 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -16,8 +16,9 @@ import 'video_player_mdk.dart' MdkVideoPlayerPlatform get _platform { if (VideoPlayerPlatform.instance is! MdkVideoPlayerPlatform) { throw StateError( - '`VideoPlayerPlatform.instance` have to be of `MdkVideoPlayerPlatform` to use advanced video player features.' - 'Make sure you\'ve called `fvp.registerWith()`'); + '`VideoPlayerPlatform.instance` have to be of `MdkVideoPlayerPlatform` to use advanced video player features.' + 'Make sure you\'ve called `fvp.registerWith()`', + ); } return VideoPlayerPlatform.instance as MdkVideoPlayerPlatform; } diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index a4c93c4..99dbcd1 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -219,7 +219,8 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { }); }); } else { - mdk.setGlobalOption('subtitle.fonts.file', PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); + mdk.setGlobalOption('subtitle.fonts.file', + PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); } _globalOpts?.forEach((key, value) { mdk.setGlobalOption(key, value); From 5ec73072363098d3dd0f3cc44500ab3db4863ef1 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sun, 2 Mar 2025 11:21:06 +0800 Subject: [PATCH 022/109] linux: fix player map destroyed too late when exiting process, invalid gl context map is accessed --- linux/fvp_plugin.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index 08f91fc..b4f1c94 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -83,7 +83,6 @@ class TexturePlayer final : public mdk::Player PlayerTexture* flTex; // hold ref }; -static unordered_map> players; // called in a current gl context static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, uint32_t *name, @@ -150,10 +149,12 @@ static void player_texture_init(PlayerTexture* self) { (G_TYPE_CHECK_INSTANCE_CAST((obj), fvp_plugin_get_type(), \ FvpPlugin)) +using PlayerMap = unordered_map>; struct _FvpPlugin { GObject parent_instance; FlTextureRegistrar* tex_registrar; + PlayerMap players; }; G_DEFINE_TYPE(FvpPlugin, fvp_plugin, g_object_get_type()) @@ -162,6 +163,7 @@ G_DEFINE_TYPE(FvpPlugin, fvp_plugin, g_object_get_type()) static void fvp_plugin_handle_method_call( FvpPlugin* self, FlMethodCall* method_call) { + // static PlayerMap players; // here is also fine, will be destroyed earlier than libmdk global objects(Context::current() map) g_autoptr(FlMethodResponse) response = nullptr; const gchar* method = fl_method_call_get_name(method_call); @@ -173,14 +175,14 @@ static void fvp_plugin_handle_method_call( const auto height = (int)fl_value_get_int(fl_value_lookup_string(args, "height")); auto tex = PLAYER_TEXTURE(g_object_new(player_texture_get_type(), nullptr)); auto player = make_shared(handle, tex, width, height, self->tex_registrar); - players[player->textureId] = player; + self->players[player->textureId] = player; g_autoptr(FlValue) result = fl_value_new_int(player->textureId); response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); } else if (strcmp(method, "ReleaseRT") == 0) { const auto args = fl_method_call_get_args(method_call); const auto texId = fl_value_get_int(fl_value_lookup_string(args, "texture")); - if (auto it = players.find(texId); it != players.cend()) { - players.erase(it); + if (auto it = self->players.find(texId); it != self->players.cend()) { + self->players.erase(it); } g_autoptr(FlValue) result = fl_value_new_null(); response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); @@ -194,7 +196,9 @@ static void fvp_plugin_handle_method_call( fl_method_call_respond(method_call, response, nullptr); } -static void fvp_plugin_dispose(GObject* object) { +static void fvp_plugin_dispose(GObject* object) { // seems never be invoked + auto self = FVP_PLUGIN(object); + self->players.~PlayerMap(); G_OBJECT_CLASS(fvp_plugin_parent_class)->dispose(object); } @@ -204,6 +208,7 @@ static void fvp_plugin_class_init(FvpPluginClass* klass) { static void fvp_plugin_init(FvpPlugin* self) { self->tex_registrar = nullptr; + new(&self->players) PlayerMap; } static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call, From 0711afde2105ce8092564bb804dee1a31c86abeb Mon Sep 17 00:00:00 2001 From: LinXunFeng Date: Wed, 5 Mar 2025 11:26:40 +0800 Subject: [PATCH 023/109] fix crash when killing app on iOS --- darwin/Classes/FvpPlugin.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/darwin/Classes/FvpPlugin.mm b/darwin/Classes/FvpPlugin.mm index 2fead94..9ac4519 100644 --- a/darwin/Classes/FvpPlugin.mm +++ b/darwin/Classes/FvpPlugin.mm @@ -135,6 +135,7 @@ + (void)registerWithRegistrar:(NSObject*)registrar { #endif FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"fvp" binaryMessenger:messenger]; FvpPlugin* instance = [[FvpPlugin alloc] initWithRegistrar:registrar]; + [registrar addApplicationDelegate:instance]; [registrar publish:instance]; [registrar addMethodCallDelegate:instance channel:channel]; SetGlobalOption("MDK_KEY", "C03BFF5306AB39058A767105F82697F42A00FE970FB0E641D306DEFF3F220547E5E5377A3C504DC30D547890E71059BC023A4DD91A95474D1F33CA4C26C81B0FC73B00ACF954C6FA75898EFA07D9680B6A00FDF179C0A15381101D01124498AF55B069BD4B0156D5CF5A56DEDE782E5F3930AD47C8F40BFBA379231142E31B0F"); @@ -183,4 +184,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - (void)detachFromEngineForRegistrar:(NSObject *)registrar { players.clear(); } + +- (void)applicationWillTerminate:(UIApplication *)application { + players.clear(); +} @end From 457d8f80b338b10206c54e057c8d55f4927a36ed Mon Sep 17 00:00:00 2001 From: LinXunFeng Date: Wed, 5 Mar 2025 18:37:59 +0800 Subject: [PATCH 024/109] fix macOS build --- darwin/Classes/FvpPlugin.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/darwin/Classes/FvpPlugin.mm b/darwin/Classes/FvpPlugin.mm index 9ac4519..abc8d3e 100644 --- a/darwin/Classes/FvpPlugin.mm +++ b/darwin/Classes/FvpPlugin.mm @@ -135,7 +135,10 @@ + (void)registerWithRegistrar:(NSObject*)registrar { #endif FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"fvp" binaryMessenger:messenger]; FvpPlugin* instance = [[FvpPlugin alloc] initWithRegistrar:registrar]; - [registrar addApplicationDelegate:instance]; +#if TARGET_OS_OSX +#else + [registrar addApplicationDelegate:instance]; +#endif [registrar publish:instance]; [registrar addMethodCallDelegate:instance channel:channel]; SetGlobalOption("MDK_KEY", "C03BFF5306AB39058A767105F82697F42A00FE970FB0E641D306DEFF3F220547E5E5377A3C504DC30D547890E71059BC023A4DD91A95474D1F33CA4C26C81B0FC73B00ACF954C6FA75898EFA07D9680B6A00FDF179C0A15381101D01124498AF55B069BD4B0156D5CF5A56DEDE782E5F3930AD47C8F40BFBA379231142E31B0F"); @@ -185,7 +188,10 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr players.clear(); } +#if TARGET_OS_OSX +#else - (void)applicationWillTerminate:(UIApplication *)application { players.clear(); } +#endif @end From 5d494df828da7b40dc8402634413142032eb1dd5 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 27 Mar 2025 09:03:01 +0800 Subject: [PATCH 025/109] v0.31.0 --- .github/workflows/pub.yml | 1 + CHANGELOG.md | 6 ++++++ README.md | 2 +- config.yaml | 2 +- darwin/fvp.podspec | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pub.yml b/.github/workflows/pub.yml index a23275a..a6eefc5 100644 --- a/.github/workflows/pub.yml +++ b/.github/workflows/pub.yml @@ -24,5 +24,6 @@ jobs: - uses: dart-lang/setup-dart@v1 # Here you can insert custom steps you need # - run: dart tool/generate-code.dart + - run: dart format lib - name: Publish run: dart pub publish --force \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 53093a8..129f649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.31.0 + +* fix crash when killing app on iOS +* fix crash when exiting app on linux + + ## 0.30.0 * `subtitleFontFile` option can be an http url diff --git a/README.md b/README.md index f89eaa4..df4be9c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ project is create with `flutter create -t plugin --platforms=linux,macos,windows - Support most formats via FFmpeg demuxer and software decoders if not supported by gpu. You can use your own ffmpeg 4.0~7.1(or master branch) by removing bundled ffmpeg dynamic library. - High performance. Lower cpu, gpu and memory load than libmpv based players. - Support audio without video -- HEVC(ios, mac only), VP8 and VP9 transparent video +- HEVC, VP8 and VP9 transparent video - Small footprint. Only about 10MB size increase per cpu architecture(platform dependent). diff --git a/config.yaml b/config.yaml index 16bb9c1..b5234da 100644 --- a/config.yaml +++ b/config.yaml @@ -34,5 +34,5 @@ globals: - mdk.* comments: true preamble: | - // Copyright (c) 2019-2024 WangBin + // Copyright (c) 2019-2025 WangBin // https://github.com/wang-bin/mdk-sdk \ No newline at end of file diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 9bcffab..d1ce1ae 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.30.0' + s.version = '0.31.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.31.0' + s.dependency 'mdk', '~> 0.32.0' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 3da2f1e..7c608b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.30.0 +version: 0.31.0 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index d94c523..b7cae82 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.30.0 +project(${PROJECT_NAME} VERSION 0.31.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 9904556738cdeb3dc37a1b5eed8dc0e021bc8f40 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 31 Mar 2025 12:43:47 +0800 Subject: [PATCH 026/109] linux: fix cmake 4.0 link error #231 --- linux/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 8fe9e91..9b09e0b 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -42,10 +42,9 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) # flutter in snap: linker will try to resolve symbols and dependencies in libmdk.so.0, dependencies found in host OS but not snap are compatible with snap glibc target_link_libraries(${PLUGIN_NAME} INTERFACE -Wl,--unresolved-symbols=ignore-in-shared-libs) - +target_link_options(${PLUGIN_NAME} PRIVATE -Wl,--enable-new-dtags -Wl,-z,origin) # add runpath, shared libs of a release bundle is in lib dir, plugin must add $ORIGIN to runpath to find libmdk -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--enable-new-dtags -Wl,-z,origin -Wl,-rpath,\\$ORIGIN") -set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--enable-new-dtags -Wl,-z,origin -Wl,-rpath,\\$ORIGIN") +set_target_properties(${PLUGIN_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN") target_compile_options(${PLUGIN_NAME} PRIVATE -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections) target_compile_options(${PLUGIN_NAME} PRIVATE -Wno-unused-function) # https://github.com/wang-bin/fvp/issues/49 target_link_libraries(${PLUGIN_NAME} PRIVATE -Wl,--gc-sections) From 77f15bd862669444951cb810b952ac4e35d7ac9e Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 7 Apr 2025 10:22:21 +0800 Subject: [PATCH 027/109] update bindings --- lib/src/generated_bindings.dart | 56 ++++++++++++++++++++++++++++++--- lib/src/global.dart | 21 ++++++++++++- lib/src/media_info.dart | 8 ++++- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/lib/src/generated_bindings.dart b/lib/src/generated_bindings.dart index a66a7c9..3ecf8c1 100644 --- a/lib/src/generated_bindings.dart +++ b/lib/src/generated_bindings.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2024 WangBin +// Copyright (c) 2019-2025 WangBin // https://github.com/wang-bin/mdk-sdk // AUTO GENERATED FILE, DO NOT EDIT. // @@ -469,6 +469,23 @@ class NativeLibrary { late final _mdkPlayerAPI_delete = _mdkPlayerAPI_deletePtr .asFunction>)>(); + void mdkPlayerAPI_reset( + ffi.Pointer> arg0, + bool release, + ) { + return _mdkPlayerAPI_reset( + arg0, + release, + ); + } + + late final _mdkPlayerAPI_resetPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer>, + ffi.Bool)>>('mdkPlayerAPI_reset'); + late final _mdkPlayerAPI_reset = _mdkPlayerAPI_resetPtr.asFunction< + void Function(ffi.Pointer>, bool)>(); + void MDK_foreignGLContextDestroyed() { return _MDK_foreignGLContextDestroyed(); } @@ -547,6 +564,7 @@ abstract class MDKSeekFlag { static const int MDK_SeekFlag_FromNow = 4; static const int MDK_SeekFlag_Frame = 64; static const int MDK_SeekFlag_KeyFrame = 256; + static const int MDK_SeekFlag_AnyFrame = 512; static const int MDK_SeekFlag_Fast = 256; static const int MDK_SeekFlag_InCache = 1024; static const int MDK_SeekFlag_Backward = 65536; @@ -556,11 +574,14 @@ abstract class MDKSeekFlag { /// ! /// \brief VideoEffect /// per video renderer effect. set via Player.setVideoEffect(MDK_VideoEffect effect, const float*); +/// Only one(the last call) of ScaleChannels or ShiftChannels will be applied abstract class MDK_VideoEffect { static const int MDK_VideoEffect_Brightness = 0; static const int MDK_VideoEffect_Contrast = 1; static const int MDK_VideoEffect_Hue = 2; static const int MDK_VideoEffect_Saturation = 3; + static const int MDK_VideoEffect_ScaleChannels = 4; + static const int MDK_VideoEffect_ShiftChannels = 5; } abstract class MDK_ColorSpace { @@ -571,6 +592,7 @@ abstract class MDK_ColorSpace { static const int MDK_ColorSpace_ExtendedLinearDisplayP3 = 4; static const int MDK_ColorSpace_ExtendedSRGB = 5; static const int MDK_ColorSpace_ExtendedLinearSRGB = 6; + static const int MDK_ColorSpace_BT2100_HLG = 7; } abstract class MDK_LogLevel { @@ -736,7 +758,13 @@ final class mdkVideoCodecParameters extends ffi.Struct { @ffi.Float() external double par; - @ffi.Array.multi([128]) + @ffi.Int32() + external int color_space; + + @ffi.Uint8() + external int dovi_profile; + + @ffi.Array.multi([123]) external ffi.Array reserved; } @@ -1167,6 +1195,15 @@ final class mdkSyncCallback extends ffi.Struct { external ffi.Pointer opaque; } +final class mdkSubtitleCallback extends ffi.Struct { + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer text, ffi.Pointer opaque)>> cb; + + external ffi.Pointer opaque; +} + final class mdkPlayerAPI extends ffi.Struct { external ffi.Pointer object; @@ -1721,6 +1758,16 @@ final class mdkPlayerAPI extends ffi.Struct { ffi.Int Function( ffi.Pointer, ffi.Pointer, ffi.Int)>> bufferedTimeRanges; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(ffi.Pointer, ffi.Pointer, + ffi.Size, ffi.Int)>> appendBuffer; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Int, + mdkSubtitleCallback)>> subtitleText; } final class SwitchBitrateCallback extends ffi.Struct { @@ -1738,7 +1785,6 @@ final class SwitchBitrateCallback extends ffi.Struct { /// Null callback(.opaque == null) + non-null token: can remove the callback of given token. /// Null callback(.opaque == null) + null token: clear all callbacks. typedef MDK_CallbackToken = ffi.Uint64; -typedef DartMDK_CallbackToken = int; /// ! /// \brief size @@ -1754,8 +1800,8 @@ final class UnnamedUnion2 extends ffi.Union { const int MDK_MAJOR = 0; -const int MDK_MINOR = 28; +const int MDK_MINOR = 31; const int MDK_MICRO = 0; -const int MDK_VERSION = 7168; +const int MDK_VERSION = 7936; diff --git a/lib/src/global.dart b/lib/src/global.dart index b3159e9..e7db5c4 100644 --- a/lib/src/global.dart +++ b/lib/src/global.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:ffi'; @@ -148,10 +148,29 @@ enum ColorSpace { unknown(MDK_ColorSpace.MDK_ColorSpace_Unknown), bt709(MDK_ColorSpace.MDK_ColorSpace_BT709), bt2100PQ(MDK_ColorSpace.MDK_ColorSpace_BT2100_PQ), + scrgb(MDK_ColorSpace.MDK_ColorSpace_scRGB), + bt2100hlg(MDK_ColorSpace.MDK_ColorSpace_BT2100_HLG), ; final int rawValue; const ColorSpace(this.rawValue); + + factory ColorSpace.from(int rawValue) { + switch (rawValue) { + case MDK_ColorSpace.MDK_ColorSpace_Unknown: + return unknown; + case MDK_ColorSpace.MDK_ColorSpace_BT709: + return bt709; + case MDK_ColorSpace.MDK_ColorSpace_BT2100_PQ: + return bt2100PQ; + case MDK_ColorSpace.MDK_ColorSpace_scRGB: + return scrgb; + case MDK_ColorSpace.MDK_ColorSpace_BT2100_HLG: + return bt2100hlg; + default: + return unknown; + } + } } enum LogLevel { diff --git a/lib/src/media_info.dart b/lib/src/media_info.dart index 2f799bb..8633f7e 100644 --- a/lib/src/media_info.dart +++ b/lib/src/media_info.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:ffi'; @@ -7,6 +7,7 @@ import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; import 'lib.dart'; +import 'global.dart'; class CodecParameters { /// codec name @@ -118,6 +119,9 @@ class VideoCodecParameters extends CodecParameters { /// pixel aspect ratio double par = 1.0; + var colorSpace = ColorSpace.unknown; + var doviProfile = 0; + VideoCodecParameters(); VideoCodecParameters._from(mdkVideoCodecParameters cp) { @@ -138,6 +142,8 @@ class VideoCodecParameters extends CodecParameters { if (cp.par > 0) { par = cp.par; } + colorSpace = ColorSpace.from(cp.color_space); + doviProfile = cp.dovi_profile; } @override From c4e1ce297a47925aa20304c9947c1d6a655cc766 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 10 Apr 2025 17:29:02 +0800 Subject: [PATCH 028/109] v0.31.1 --- CHANGELOG.md | 4 ++++ darwin/fvp.podspec | 2 +- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 129f649..12d88a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.31.1 + +* fix link error if use cmake 4.0 on linux + ## 0.31.0 * fix crash when killing app on iOS diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index d1ce1ae..137a24a 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.31.0' + s.version = '0.31.1' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. diff --git a/pubspec.yaml b/pubspec.yaml index 7c608b3..023212a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.31.0 +version: 0.31.1 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b7cae82..5c757a8 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.31.0 +project(${PROJECT_NAME} VERSION 0.31.1 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 529ca737a3b5a4e0385ae0f3a391d4499aee9686 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 10 Apr 2025 20:53:56 +0800 Subject: [PATCH 029/109] cmake: add env var FVP_DEPS_URL download from github release: https://github.com/wang-bin/mdk-sdk/releases/latest/download --- cmake/deps.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 7fb50a6..3499bdb 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -13,7 +13,12 @@ macro(fvp_setup_deps) endif() else() endif() - set(MDK_SDK_URL https://sourceforge.net/projects/mdk-sdk/files/nightly/${MDK_SDK_PKG}) + if("$ENV{FVP_DEPS_URL}" MATCHES "^http") # github release: https://github.com/wang-bin/mdk-sdk/releases/latest/download + set(FVP_DEPS_URL $ENV{FVP_DEPS_URL}) # TODO: md5 + else() + set(FVP_DEPS_URL https://sourceforge.net/projects/mdk-sdk/files/nightly) + endif() + set(MDK_SDK_URL ${FVP_DEPS_URL}/${MDK_SDK_PKG}) set(MDK_SDK_SAVE "${CMAKE_CURRENT_SOURCE_DIR}/${MDK_SDK_PKG}") set(DOWNLOAD_MDK_SDK OFF) From aa7295bf8cf6690060fba350b206025198b97ca2 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 10 Apr 2025 21:54:08 +0800 Subject: [PATCH 030/109] set global options w/o delay e.g. log level should be set before creating a player --- lib/src/video_player_mdk.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 99dbcd1..954a825 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -166,6 +166,13 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _decoders = vd[Platform.operatingSystem]; } + // mdk.setGlobalOptions('plugins', 'mdk-braw'); + mdk.setGlobalOption("log", "all"); + mdk.setGlobalOption('d3d11.sync.cpu', 1); + _globalOpts?.forEach((key, value) { + mdk.setGlobalOption(key, value); + }); + // delay: ensure log handler is set in main(), blank window if run with debugger. // registerWith() can be invoked by dart_plugin_registrant.dart before main. when debugging, won't enter main if posting message from native to dart(new native log message) before main? Future.delayed(const Duration(milliseconds: 0), () { @@ -196,9 +203,6 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { return; } }); - // mdk.setGlobalOptions('plugins', 'mdk-braw'); - mdk.setGlobalOption("log", "all"); - mdk.setGlobalOption('d3d11.sync.cpu', 1); if (_subtitleFontFile?.startsWith('http') ?? false) { final fileName = _subtitleFontFile!.split('/').last; getApplicationCacheDirectory().then((dir) { @@ -222,9 +226,6 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { mdk.setGlobalOption('subtitle.fonts.file', PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); } - _globalOpts?.forEach((key, value) { - mdk.setGlobalOption(key, value); - }); } @override From 5c89490f558fe6b56674bfa5792427190f123777 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 12 Apr 2025 15:23:55 +0800 Subject: [PATCH 031/109] doc for MediaInfo.bitRate --- lib/src/media_info.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/media_info.dart b/lib/src/media_info.dart index 8633f7e..c4c86e7 100644 --- a/lib/src/media_info.dart +++ b/lib/src/media_info.dart @@ -298,7 +298,9 @@ class MediaInfo { var startTime = 0; /// duration in milliseconds. may be 0, for example live stream. + /// duration may change when playing a stream being recorded var duration = 0; + /// when stream is loaded, the value from container. when playing, it's updated to the realtime value var bitRate = 0; /// format or container name, for example mp4, flv From 82b36fae9736a0013d56d352970a67402ae68a40 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 14 Apr 2025 12:03:43 +0800 Subject: [PATCH 032/109] linux: fix rpath --- linux/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 9b09e0b..c471c36 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -44,7 +44,11 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) target_link_libraries(${PLUGIN_NAME} INTERFACE -Wl,--unresolved-symbols=ignore-in-shared-libs) target_link_options(${PLUGIN_NAME} PRIVATE -Wl,--enable-new-dtags -Wl,-z,origin) # add runpath, shared libs of a release bundle is in lib dir, plugin must add $ORIGIN to runpath to find libmdk -set_target_properties(${PLUGIN_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN") +set_target_properties(${PLUGIN_NAME} PROPERTIES + BUILD_RPATH_USE_ORIGIN ON + BUILD_RPATH "$ORIGIN" + INSTALL_RPATH "$ORIGIN" +) target_compile_options(${PLUGIN_NAME} PRIVATE -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections) target_compile_options(${PLUGIN_NAME} PRIVATE -Wno-unused-function) # https://github.com/wang-bin/fvp/issues/49 target_link_libraries(${PLUGIN_NAME} PRIVATE -Wl,--gc-sections) From 3887037be8141bfa56defa4396f008a30b5825d1 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 14 Apr 2025 12:12:29 +0800 Subject: [PATCH 033/109] v0.31.2 --- CHANGELOG.md | 5 +++++ darwin/fvp.podspec | 2 +- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d88a3..a8c3c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.31.2 + +* fix rpath on linux +* cmake: add env var FVP_DEPS_URL to download dependencies from github + ## 0.31.1 * fix link error if use cmake 4.0 on linux diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 137a24a..aadb90b 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.31.1' + s.version = '0.31.2' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. diff --git a/pubspec.yaml b/pubspec.yaml index 023212a..70cf735 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.31.1 +version: 0.31.2 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 5c757a8..a85629e 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.31.1 +project(${PROJECT_NAME} VERSION 0.31.2 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 581d6aeac39916da72e4d0f797edf3ba02f7133e Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 10 May 2025 22:08:31 +0800 Subject: [PATCH 034/109] add rpi and rk decoders --- lib/src/video_player_mdk.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 954a825..62615b4 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -160,7 +160,7 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { 'windows': ['MFT:d3d=11', "D3D11", "DXVA", 'CUDA', 'FFmpeg'], 'macos': ['VT', 'FFmpeg'], 'ios': ['VT', 'FFmpeg'], - 'linux': ['VAAPI', 'CUDA', 'VDPAU', 'FFmpeg'], + 'linux': ['VAAPI', 'CUDA', 'VDPAU', 'rkmpp', 'V4L2M2M', 'FFmpeg:hwcontext=drm', 'FFmpeg'], 'android': ['AMediaCodec', 'FFmpeg'], }; _decoders = vd[Platform.operatingSystem]; From 3e11ec5298d2418e50b45e966786ff60166306f2 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 22 May 2025 23:20:17 +0800 Subject: [PATCH 035/109] linux: restore fbo --- linux/fvp_plugin.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index b4f1c94..d410017 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 WangBin + * Copyright (c) 2023-2025 WangBin */ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -92,14 +92,17 @@ static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, if (self->fbo == 0) { self->ctx = gdk_gl_context_get_current(); // fbo can not be shared glGenFramebuffers(1, &self->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); } if (self->texture_id == 0) { + GLint prevFbo = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo); + glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); glGenTextures(1, &self->texture_id); glBindTexture(GL_TEXTURE_2D, self->texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, self->player->width, self->player->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, GL_TEXTURE_2D, self->texture_id, 0); const GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); if (err != GL_FRAMEBUFFER_COMPLETE) { //glDeleteFramebuffers(1, &fbo); clog << "glFramebufferTexture2D error" << endl; From cffafa82999d4e638cc736ee2b1dd57e20f67981 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 22 May 2025 23:44:51 +0800 Subject: [PATCH 036/109] support flutter-elinux https://github.com/sony/flutter-elinux --- elinux/CMakeLists.txt | 50 +++++++ elinux/fvp_plugin.cc | 244 ++++++++++++++++++++++++++++++++ elinux/include/fvp/fvp_plugin.h | 23 +++ pubspec.yaml | 3 + 4 files changed, 320 insertions(+) create mode 100644 elinux/CMakeLists.txt create mode 100644 elinux/fvp_plugin.cc create mode 100644 elinux/include/fvp/fvp_plugin.h diff --git a/elinux/CMakeLists.txt b/elinux/CMakeLists.txt new file mode 100644 index 0000000..3a12b1f --- /dev/null +++ b/elinux/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "fvp") +project(${PROJECT_NAME} LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "fvp_plugin") + +add_library(${PLUGIN_NAME} SHARED + "fvp_plugin.cc" + ../lib/src/callbacks.cpp +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +target_link_libraries(${PLUGIN_NAME} PRIVATE EGL GLESv2) +#target_link_libraries(${PLUGIN_NAME} PRIVATE epoxy) +# flutter in snap: linker will try to resolve symbols and dependencies in libmdk.so.0, dependencies found in host OS but not snap are compatible with snap glibc +target_link_libraries(${PLUGIN_NAME} INTERFACE -Wl,--unresolved-symbols=ignore-in-shared-libs) +target_link_options(${PLUGIN_NAME} PRIVATE -Wl,--enable-new-dtags -Wl,-z,origin) +# add runpath, shared libs of a release bundle is in lib dir, plugin must add $ORIGIN to runpath to find libmdk +set_target_properties(${PLUGIN_NAME} PROPERTIES + BUILD_RPATH_USE_ORIGIN ON + BUILD_RPATH "$ORIGIN" + INSTALL_RPATH "$ORIGIN" +) +target_compile_options(${PLUGIN_NAME} PRIVATE -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections) +target_compile_options(${PLUGIN_NAME} PRIVATE -Wno-unused-function) # https://github.com/wang-bin/fvp/issues/49 +target_link_libraries(${PLUGIN_NAME} PRIVATE -Wl,--gc-sections) + +include(../cmake/deps.cmake) +fvp_setup_deps() +target_link_libraries(${PLUGIN_NAME} PRIVATE mdk) +# List of absolute paths to libraries that should be bundled with the plugin +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +get_filename_component(MDK_LIB_DIR ${MDK_LIBRARY} DIRECTORY) +set(fvp_bundled_libraries + ${MDK_RUNTIME} + ${MDK_FFMPEG} + ${MDK_LIB_DIR}/libc++.so.1 + PARENT_SCOPE +) diff --git a/elinux/fvp_plugin.cc b/elinux/fvp_plugin.cc new file mode 100644 index 0000000..6a1d556 --- /dev/null +++ b/elinux/fvp_plugin.cc @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2025 WangBin + */ +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "include/fvp/fvp_plugin.h" +#include +#include +#include +#if 1 +#include +#include +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include "mdk/RenderAPI.h" +#include "mdk/Player.h" +#undef Success // X.h + +using namespace std; + +#define EGL_ENSURE(x, ...) EGL_RUN_CHECK(x, return __VA_ARGS__) +#define EGL_WARN(x, ...) EGL_RUN_CHECK(x) +#define EGL_RUN_CHECK(x, ...) do { \ + while (eglGetError() != EGL_SUCCESS) {} \ + (x); \ + const EGLint err = eglGetError(); \ + if (err != EGL_SUCCESS) { \ + std::cout << #x " EGL ERROR (" << std::hex << err << std::dec << ") @" << __LINE__ << __FUNCTION__ << std::endl; \ + __VA_ARGS__; \ + } \ + } while(false) + +#define GL_ENSURE(x, ...) GL_RUN_CHECK(x, return __VA_ARGS__) +#define GL_WARN(x, ...) GL_RUN_CHECK(x) +// GL_CONTEXT_LOST repeats. stop render loop? see qtbase c33faac32b +// https://www.khronos.org/webgl/wiki/HandlingContextLost +#define GL_RUN_CHECK(expr, ...) do { \ + while (true) { \ + const GLenum err = glGetError(); \ + if (err == GL_NO_ERROR) \ + break; \ + if (err == GL_CONTEXT_LOST_KHR) { \ + std::cout << "GL_CONTEXT_LOST" << std::endl; \ + break; \ + } \ + } \ + expr; \ + const GLenum err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + std::cout << #expr " GL ERROR (" << std::hex << err << std::dec << ") @" << __FUNCTION__ << __LINE__ << std::endl; \ + __VA_ARGS__; \ + } \ + } while(false) + +namespace { +class TexturePlayer final : public mdk::Player +{ +public: + TexturePlayer(int64_t handle, int width, int height, flutter::TextureRegistrar* texRegistrar) + : mdk::Player(reinterpret_cast(handle)) + { + fltImg_->egl_image = EGL_NO_IMAGE_KHR; // TODO: + fltImg_->width = width; + fltImg_->height = height; + fltImg_->release_callback = [](void* release_context) { + }; + fltImg_->release_context = nullptr; // TODO: + fltTex_ = make_unique(flutter::EGLImageTexture( + [this](size_t width, size_t height, void* egl_display, void* egl_context) { + fltImg_->egl_image = ensureVideo(width, height, static_cast(egl_display), static_cast(egl_context)); + return fltImg_.get(); + } + )); + textureId = texRegistrar->RegisterTexture(fltTex_.get()); + + scale(1, -1); // y is flipped + setVideoSurfaceSize(width, height); + setRenderCallback([this, texRegistrar](void*) { + //renderVideo(); // need a gl context + texRegistrar->MarkTextureFrameAvailable(textureId); + }); + } + + ~TexturePlayer() override { + setRenderCallback(nullptr); + auto ctx = eglGetCurrentContext(); + auto r = eglGetCurrentSurface(EGL_READ); + auto d = eglGetCurrentSurface(EGL_DRAW); + // fbo is not shared, so we need to make fbo context current + if (ctx != ctx_) + EGL_WARN(eglMakeCurrent(eglGetCurrentDisplay(), draw_, read_, ctx_)); + if (img_ != EGL_NO_IMAGE_KHR) { + EGL_WARN(eglDestroyImageKHR(disp_, img_)); + img_ = EGL_NO_IMAGE_KHR; + } + if (tex_) + glDeleteTextures(1, &tex_); + if (fbo_) + glDeleteFramebuffers(1, &fbo_); + setVideoSurfaceSize(-1, -1); + if (ctx != ctx_ && ctx != EGL_NO_CONTEXT) + EGL_WARN(eglMakeCurrent(eglGetCurrentDisplay(), d, r, ctx)); + // TODO: unregister tex here? + } + + EGLImageKHR ensureVideo(size_t width, size_t height, EGLDisplay disp, EGLContext c) { + if (fbo_ == 0) { + ctx_ = c; // fbo can not be shared + disp_ = disp; + draw_ = eglGetCurrentSurface(EGL_DRAW); + read_ = eglGetCurrentSurface(EGL_READ); + GL_WARN(glGenFramebuffers(1, &fbo_)); + } + if (tex_ == 0) { + GLint prevFbo = 0; + GL_WARN(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo)); + GL_WARN(glBindFramebuffer(GL_FRAMEBUFFER, fbo_)); + GL_WARN(glGenTextures(1, &tex_)); + GL_WARN(glBindTexture(GL_TEXTURE_2D, tex_)); + GL_WARN(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + GL_WARN(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, GL_TEXTURE_2D, tex_, 0)); + const GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER); + GL_WARN(glBindFramebuffer(GL_FRAMEBUFFER, prevFbo)); + if (err != GL_FRAMEBUFFER_COMPLETE) { + //glDeleteFramebuffers(1, &fbo); + clog << fbo_ << " glFramebufferTexture2D " + std::to_string(tex_) + " error: " << err << endl; + return img_; + } + mdk::GLRenderAPI ra{}; + ra.fbo = fbo_; + setRenderAPI(&ra); + } + if (img_ == EGL_NO_IMAGE_KHR) { + if (!eglCreateImageKHR) { + eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); + eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); + } + EGL_WARN(img_ = eglCreateImageKHR(disp, c, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(intptr_t)tex_, nullptr)); + if (img_ == EGL_NO_IMAGE_KHR) { + clog << "eglCreateImageKHR error" << endl; + } + } + + renderVideo(); + return img_; + } + + int64_t textureId; +private: + unique_ptr fltImg_ = make_unique(); + unique_ptr fltTex_; + + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; + EGLDisplay disp_ = EGL_NO_DISPLAY; + EGLContext ctx_ = EGL_NO_CONTEXT; + EGLSurface read_ = EGL_NO_SURFACE; + EGLSurface draw_ = EGL_NO_SURFACE; + EGLImageKHR img_ = EGL_NO_IMAGE_KHR; + GLuint tex_ = 0; // TODO: array + GLuint fbo_ = 0; +}; + + +class FvpPlugin final : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar); + + FvpPlugin(flutter::TextureRegistrar* tr) + : texture_registrar_(tr) + {} + + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); + + + flutter::TextureRegistrar* texture_registrar_ = nullptr; + std::unordered_map> players_; +}; + +// static +void FvpPlugin::RegisterWithRegistrar( + flutter::PluginRegistrar *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "fvp", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(registrar->texture_registrar()); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); + mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); +} + +void FvpPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name() == "CreateRT") { + auto args = std::get(*method_call.arguments()); + const auto width = (int)args[flutter::EncodableValue("width")].LongValue(); + const auto height = (int)args[flutter::EncodableValue("height")].LongValue(); + const auto handle = args[flutter::EncodableValue("player")].LongValue(); + auto player = make_shared(handle, width, height, texture_registrar_); + result->Success(flutter::EncodableValue(player->textureId)); + players_[player->textureId] = player; + } else if (method_call.method_name() == "ReleaseRT") { + auto args = std::get(*method_call.arguments()); + const auto texId = args[flutter::EncodableValue("texture")].LongValue(); + texture_registrar_->UnregisterTexture(texId); + if (auto it = players_.find(texId); it != players_.cend()) { + players_.erase(it); + } + result->Success(); + } else if (method_call.method_name() == "MixWithOthers") { + result->Success(); + } else { + result->NotImplemented(); + } +} + +} // namespace + +void FvpPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + FvpPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/elinux/include/fvp/fvp_plugin.h b/elinux/include/fvp/fvp_plugin.h new file mode 100644 index 0000000..bd873c1 --- /dev/null +++ b/elinux/include/fvp/fvp_plugin.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_FVP_PLUGIN_H_ +#define FLUTTER_PLUGIN_FVP_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void FvpPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_FVP_PLUGIN_H_ diff --git a/pubspec.yaml b/pubspec.yaml index 70cf735..6e113c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,6 +65,9 @@ flutter: windows: dartPluginClass: VideoPlayerRegistrant pluginClass: FvpPluginCApi + elinux: + dartPluginClass: VideoPlayerRegistrant # auto registered in .dart_tool/flutter_build/dart_plugin_registrant.dart + pluginClass: FvpPlugin # To add assets to your plugin package, add an assets section, like this: # assets: From e765e7c0e6d02afdb5361656f5c9b36e20baadb8 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Fri, 23 May 2025 01:09:25 +0800 Subject: [PATCH 037/109] elinux: unable to make current context --- elinux/fvp_plugin.cc | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/elinux/fvp_plugin.cc b/elinux/fvp_plugin.cc index 6a1d556..72dafe0 100644 --- a/elinux/fvp_plugin.cc +++ b/elinux/fvp_plugin.cc @@ -91,24 +91,15 @@ class TexturePlayer final : public mdk::Player ~TexturePlayer() override { setRenderCallback(nullptr); - auto ctx = eglGetCurrentContext(); - auto r = eglGetCurrentSurface(EGL_READ); - auto d = eglGetCurrentSurface(EGL_DRAW); - // fbo is not shared, so we need to make fbo context current - if (ctx != ctx_) - EGL_WARN(eglMakeCurrent(eglGetCurrentDisplay(), draw_, read_, ctx_)); if (img_ != EGL_NO_IMAGE_KHR) { EGL_WARN(eglDestroyImageKHR(disp_, img_)); img_ = EGL_NO_IMAGE_KHR; } if (tex_) - glDeleteTextures(1, &tex_); + GL_WARN(glDeleteTextures(1, &tex_)); if (fbo_) - glDeleteFramebuffers(1, &fbo_); + GL_WARN(glDeleteFramebuffers(1, &fbo_)); setVideoSurfaceSize(-1, -1); - if (ctx != ctx_ && ctx != EGL_NO_CONTEXT) - EGL_WARN(eglMakeCurrent(eglGetCurrentDisplay(), d, r, ctx)); - // TODO: unregister tex here? } EGLImageKHR ensureVideo(size_t width, size_t height, EGLDisplay disp, EGLContext c) { From a7883c6a44f4339a054ed50c7cd4abb4e66dfc09 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sun, 25 May 2025 08:01:56 +0800 Subject: [PATCH 038/109] disable ffmpeg safe option https://github.com/wang-bin/fvp/issues/261#issuecomment-2903154199 --- README.md | 2 +- lib/src/video_player_mdk.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index df4be9c..ab06b6d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Prebuilt example can be download from artifacts of [github actions](https://gith project is create with `flutter create -t plugin --platforms=linux,macos,windows,android,ios -i objc -a java fvp` ## Features -- All platforms: Windows x64(including win7) and arm64, Linux x64 and arm64, macOS, iOS, Android(requires flutter > 3.19 because of minSdk 21). +- All platforms: Windows x64(including win7) and arm64, Linux x64 and arm64, [embedded linux](https://github.com/sony/flutter-elinux), macOS, iOS, Android(requires flutter > 3.19 because of minSdk 21). - You can choose official implementation or this plugin's - Optimal render api: d3d11 for windows, metal for macOS/iOS, OpenGL for Linux and Android(Impeller support) - Hardware decoders are enabled by default diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 62615b4..f9500f5 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -245,6 +245,7 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { //player.setProperty("keep_open", "1"); player.setProperty('video.decoder', 'shader_resource=0'); player.setProperty('avformat.strict', 'experimental'); + player.setProperty('avformat.safe', '0'); player.setProperty('avio.reconnect', '1'); player.setProperty('avio.reconnect_delay_max', '7'); player.setProperty('avio.protocol_whitelist', From 64c1ac4f50fa29e5a0effef88ceb7fe11edaeae6 Mon Sep 17 00:00:00 2001 From: WangBin Date: Tue, 27 May 2025 12:51:26 +0800 Subject: [PATCH 039/109] elinux: unregister and release gl resources iff header is correct https://github.com/sony/flutter-embedded-linux/issues/438 --- elinux/fvp_plugin.cc | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/elinux/fvp_plugin.cc b/elinux/fvp_plugin.cc index 72dafe0..0149521 100644 --- a/elinux/fvp_plugin.cc +++ b/elinux/fvp_plugin.cc @@ -66,6 +66,7 @@ class TexturePlayer final : public mdk::Player public: TexturePlayer(int64_t handle, int width, int height, flutter::TextureRegistrar* texRegistrar) : mdk::Player(reinterpret_cast(handle)) + , texture_registrar_(texRegistrar) { fltImg_->egl_image = EGL_NO_IMAGE_KHR; // TODO: fltImg_->width = width; @@ -89,17 +90,26 @@ class TexturePlayer final : public mdk::Player }); } + template // use template to not instantiate false branch + void unregisterIfGoodHeader() { + if constexpr (requires(T* t){ t->UnregisterTexture(0, nullptr); }) { + auto gfxRelease = [disp = disp_, img = img_, tex = tex_, fbo = fbo_, eglDestroyImageKHR = eglDestroyImageKHR]() { // called in raster thread and gl context is correct + if (img != EGL_NO_IMAGE_KHR) + EGL_WARN(eglDestroyImageKHR(disp, img)); + if (tex) + GL_WARN(glDeleteTextures(1, &tex)); + if (fbo) + GL_WARN(glDeleteFramebuffers(1, &fbo)); + }; + texture_registrar_->UnregisterTexture(textureId, gfxRelease); + } + } + ~TexturePlayer() override { setRenderCallback(nullptr); - if (img_ != EGL_NO_IMAGE_KHR) { - EGL_WARN(eglDestroyImageKHR(disp_, img_)); - img_ = EGL_NO_IMAGE_KHR; - } - if (tex_) - GL_WARN(glDeleteTextures(1, &tex_)); - if (fbo_) - GL_WARN(glDeleteFramebuffers(1, &fbo_)); - setVideoSurfaceSize(-1, -1); + // texture_registrar.h in flutter-elinux is outdated and results in crash. https://github.com/sony/flutter-embedded-linux/issues/438 + unregisterIfGoodHeader(); + setVideoSurfaceSize(-1, -1); // no gl context now, but gl resources will be released in raster thread later in ensureVideo() } EGLImageKHR ensureVideo(size_t width, size_t height, EGLDisplay disp, EGLContext c) { @@ -148,6 +158,7 @@ class TexturePlayer final : public mdk::Player private: unique_ptr fltImg_ = make_unique(); unique_ptr fltTex_; + flutter::TextureRegistrar* texture_registrar_ = nullptr; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; @@ -213,7 +224,6 @@ void FvpPlugin::HandleMethodCall( } else if (method_call.method_name() == "ReleaseRT") { auto args = std::get(*method_call.arguments()); const auto texId = args[flutter::EncodableValue("texture")].LongValue(); - texture_registrar_->UnregisterTexture(texId); if (auto it = players_.find(texId); it != players_.cend()) { players_.erase(it); } From e74f272647072c9b705455acc3d0df03fe965542 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 29 May 2025 23:08:50 +0800 Subject: [PATCH 040/109] format. global options override --- lib/src/media_info.dart | 1 + lib/src/video_player_mdk.dart | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/src/media_info.dart b/lib/src/media_info.dart index c4c86e7..888736f 100644 --- a/lib/src/media_info.dart +++ b/lib/src/media_info.dart @@ -300,6 +300,7 @@ class MediaInfo { /// duration in milliseconds. may be 0, for example live stream. /// duration may change when playing a stream being recorded var duration = 0; + /// when stream is loaded, the value from container. when playing, it's updated to the realtime value var bitRate = 0; diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index f9500f5..7a94b17 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -160,19 +160,20 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { 'windows': ['MFT:d3d=11', "D3D11", "DXVA", 'CUDA', 'FFmpeg'], 'macos': ['VT', 'FFmpeg'], 'ios': ['VT', 'FFmpeg'], - 'linux': ['VAAPI', 'CUDA', 'VDPAU', 'rkmpp', 'V4L2M2M', 'FFmpeg:hwcontext=drm', 'FFmpeg'], + 'linux': [ + 'VAAPI', + 'CUDA', + 'VDPAU', + 'rkmpp', + 'V4L2M2M', + 'FFmpeg:hwcontext=drm', + 'FFmpeg' + ], 'android': ['AMediaCodec', 'FFmpeg'], }; _decoders = vd[Platform.operatingSystem]; } - // mdk.setGlobalOptions('plugins', 'mdk-braw'); - mdk.setGlobalOption("log", "all"); - mdk.setGlobalOption('d3d11.sync.cpu', 1); - _globalOpts?.forEach((key, value) { - mdk.setGlobalOption(key, value); - }); - // delay: ensure log handler is set in main(), blank window if run with debugger. // registerWith() can be invoked by dart_plugin_registrant.dart before main. when debugging, won't enter main if posting message from native to dart(new native log message) before main? Future.delayed(const Duration(milliseconds: 0), () { @@ -203,6 +204,9 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { return; } }); + // mdk.setGlobalOptions('plugins', 'mdk-braw'); + mdk.setGlobalOption("log", "all"); + mdk.setGlobalOption('d3d11.sync.cpu', 1); if (_subtitleFontFile?.startsWith('http') ?? false) { final fileName = _subtitleFontFile!.split('/').last; getApplicationCacheDirectory().then((dir) { @@ -226,6 +230,9 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { mdk.setGlobalOption('subtitle.fonts.file', PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); } + _globalOpts?.forEach((key, value) { + mdk.setGlobalOption(key, value); + }); } @override From de70e0adc79888267bb7e6e4ccc489f1e25f9d99 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 29 May 2025 23:09:52 +0800 Subject: [PATCH 041/109] v0.32.0 --- CHANGELOG.md | 6 ++++++ darwin/fvp.podspec | 4 ++-- pubspec.yaml | 6 +++--- windows/CMakeLists.txt | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8c3c40..5c4ec3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.32.0 + +* support [embedded linux](https://github.com/sony/flutter-elinux), tested on x86 and arm, with x11, wayland and gbm. +* fix an fbo error on linux +* enable rkmpp decoder for rockchip + ## 0.31.2 * fix rpath on linux diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index aadb90b..4e04c4a 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.31.2' + s.version = '0.32.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.32.0' + s.dependency 'mdk', '~> 0.33.0' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 6e113c0..b1052cd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,17 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.31.2 +version: 0.32.0 homepage: https://github.com/wang-bin/fvp topics: - video - player - video-player - audio-player - - videoplayer + - elinux environment: sdk: ^3.0.0 - flutter: ^3.0.0 + flutter: ">=3.0.0" dependencies: ffi: ^2.1.0 diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a85629e..ae40e39 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.31.2 +project(${PROJECT_NAME} VERSION 0.32.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 72dd39b533d0c9c67f0951111f096a4a7b705106 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Wed, 4 Jun 2025 00:23:38 +0800 Subject: [PATCH 042/109] controller: compatible with video_player 2.10 textureId is replaced by playerId to support platform view --- lib/src/controller.dart | 59 +++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 22d59c3..62faace 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -28,26 +28,39 @@ MdkVideoPlayerPlatform get _platform { /// All methods in this extension must be called after initialized, otherwise no effect. extension FVPControllerExtensions on VideoPlayerController { /// Indicates whether current media is a live stream or not + + int _getId() { + final dynamic self = this; +// TODO: prefer playerId in a future version + try { + // or (this as dynamic).textureId; + return self.textureId; // try to get textureId + } on NoSuchMethodError { + return self + .playerId; // since video_player 2.10.0 to support platform view + } + } + bool isLive() { - return _platform.isLive(textureId); + return _platform.isLive(_getId()); } /// Get current media info. MediaInfo? getMediaInfo() { - return _platform.getMediaInfo(textureId); + return _platform.getMediaInfo(_getId()); } /// set additional properties /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpropertyconst-stdstring-key-const-stdstring-value void setProperty(String name, String value) { - _platform.setProperty(textureId, name, value); + _platform.setProperty(_getId(), name, value); } /// Change video decoder list on the fly. /// NOTE: the default decoder list used by [VideoPlayerController] constructor MUST set via [registerWith]. /// Detail: https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setdecodersmediatype-type-const-stdvectorstdstring-names void setVideoDecoders(List value) { - _platform.setVideoDecoders(textureId, value); + _platform.setVideoDecoders(_getId(), value); } /// Start to record if [to] is not null. Stop recording if [to] is null. @@ -55,7 +68,7 @@ extension FVPControllerExtensions on VideoPlayerController { /// If not stopped by user, recording will be stopped when playback is finished. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-recordconst-char-url--nullptr-const-char-format--nullptr void record({String? to, String? format}) { - _platform.record(textureId, to: to, format: format); + _platform.record(_getId(), to: to, format: format); } /// Take a snapshot for current rendered frame. @@ -64,13 +77,13 @@ extension FVPControllerExtensions on VideoPlayerController { /// [height] snapshot height. if not set, result is `mediaInfo.video[current_track].codec.height` /// Return rgba data of image size [width]x[height], stride is `width*4` Future snapshot({int? width, int? height}) async { - return _platform.snapshot(textureId, width: width, height: height); + return _platform.snapshot(_getId(), width: width, height: height); } /// Set position range in milliseconds. Can be used by A-B loop. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setrangeint64_t-a-int64_t-b--int64_max void setRange({required int from, int to = -1}) { - _platform.setRange(textureId, from: from, to: to); + _platform.setRange(_getId(), from: from, to: to); } /// Set duration range(milliseconds) of buffered data. @@ -86,91 +99,91 @@ extension FVPControllerExtensions on VideoPlayerController { /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setbufferrangeint64_t-minms-int64_t-maxms-bool-drop--false /// NOTE: default values are set in [VideoPlayerController] constructor if 'lowLatency' is enabled in [registerWith] void setBufferRange({int min = -1, int max = -1, bool drop = false}) { - _platform.setBufferRange(textureId, min: min, max: max, drop: drop); + _platform.setBufferRange(_getId(), min: min, max: max, drop: drop); } /// fast seek to a key frame Future fastSeekTo(Duration position) async { - return _platform.fastSeekTo(textureId, position); + return _platform.fastSeekTo(_getId(), position); } /// Step forward or backward. /// Step forward if [frames] > 0, backward otherwise. Future step({int frames = 1}) async { - return _platform.step(textureId, frames); + return _platform.step(_getId(), frames); } /// set brightness. -1 <= [value] <= 1 void setBrightness(double value) { - _platform.setBrightness(textureId, value); + _platform.setBrightness(_getId(), value); } /// set contrast. -1 <= [value] <= 1 void setContrast(double value) { - _platform.setContrast(textureId, value); + _platform.setContrast(_getId(), value); } /// set hue. -1 <= [value] <= 1 void setHue(double value) { - _platform.setHue(textureId, value); + _platform.setHue(_getId(), value); } /// set saturation. -1 <= [value] <= 1 void setSaturation(double value) { - _platform.setSaturation(textureId, value); + _platform.setSaturation(_getId(), value); } /// Set active audio tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external audio data source via [setExternalAudio] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks void setAudioTracks(List value) { - _platform.setAudioTracks(textureId, value); + _platform.setAudioTracks(_getId(), value); } /// Get active audio tracks. List? getActiveAudioTracks() { - return _platform.getActiveAudioTracks(textureId); + return _platform.getActiveAudioTracks(_getId()); } /// Set active video tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external video data source via [setExternalVideo] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks void setVideoTracks(List value) { - _platform.setVideoTracks(textureId, value); + _platform.setVideoTracks(_getId(), value); } /// Get active video tracks. List? getActiveVideoTracks() { - return _platform.getActiveVideoTracks(textureId); + return _platform.getActiveVideoTracks(_getId()); } /// Set active subtitle tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external subtitle data source via [setExternalSubtitle] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks void setSubtitleTracks(List value) { - _platform.setSubtitleTracks(textureId, value); + _platform.setSubtitleTracks(_getId(), value); } /// Get active subtitle tracks. List? getActiveSubtitleTracks() { - return _platform.getActiveSubtitleTracks(textureId); + return _platform.getActiveSubtitleTracks(_getId()); } /// set an external audio data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalAudio(String uri) { - _platform.setExternalAudio(textureId, uri); + _platform.setExternalAudio(_getId(), uri); } /// set an external video data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalVideo(String uri) { - _platform.setExternalVideo(textureId, uri); + _platform.setExternalVideo(_getId(), uri); } /// set an external subtitle data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalSubtitle(String uri) { - _platform.setExternalSubtitle(textureId, uri); + _platform.setExternalSubtitle(_getId(), uri); } } From eb77d85215f64c77078cba99ccd4f235beeb0614 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Wed, 4 Jun 2025 00:25:28 +0800 Subject: [PATCH 043/109] v0.32.1 --- CHANGELOG.md | 4 ++++ darwin/fvp.podspec | 2 +- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c4ec3f..23bb591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.32.0 +* compatible with video_player 2.10.0+ + +## 0.32.0 + * support [embedded linux](https://github.com/sony/flutter-elinux), tested on x86 and arm, with x11, wayland and gbm. * fix an fbo error on linux * enable rkmpp decoder for rockchip diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 4e04c4a..d22162c 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.32.0' + s.version = '0.32.1' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. diff --git a/pubspec.yaml b/pubspec.yaml index b1052cd..975c19b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.32.0 +version: 0.32.1 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index ae40e39..2710850 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.32.0 +project(${PROJECT_NAME} VERSION 0.32.1 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From f6b57be13d8d74c930d12f495a6a23fce88d92e5 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Wed, 4 Jun 2025 23:09:27 +0800 Subject: [PATCH 044/109] test textureId/playerId getter only once --- CHANGELOG.md | 2 +- lib/src/controller.dart | 82 +++++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23bb591..a8a0cad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.32.0 +## 0.32.1 * compatible with video_player 2.10.0+ diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 62faace..b357bd5 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -1,6 +1,7 @@ // ignore_for_file: invalid_use_of_visible_for_testing_member // see https://github.com/ardera/flutter_packages/blob/main/packages/flutterpi_gstreamer_video_player/lib/src/controller.dart +import 'dart:io'; import 'dart:typed_data'; import 'package:video_player/video_player.dart'; @@ -27,40 +28,57 @@ MdkVideoPlayerPlatform get _platform { /// /// All methods in this extension must be called after initialized, otherwise no effect. extension FVPControllerExtensions on VideoPlayerController { - /// Indicates whether current media is a live stream or not - - int _getId() { - final dynamic self = this; +/* + static int Function(VideoPlayerController)? _idGetter; + int _getId(VideoPlayerController controller) { + _idGetter ??= _getIdFunc(); + return _idGetter!(this); + } + int Function(VideoPlayerController) _getIdFunc() { + //final dynamic self = this; + try { // try to get textureId + //final _ = self.textureId; + final _ = (this as dynamic).textureId; + return (dynamic c) => c.textureId; + } on NoSuchMethodError { // since video_player 2.10.0 to support platform view + return (dynamic c) => c.playerId; + } + } + // extension can't override existing method, e.g. `dynamic noSuchMethod(Invocation invocation)` +*/ // TODO: prefer playerId in a future version + static final int Function(VideoPlayerController c) _getId = () { try { - // or (this as dynamic).textureId; - return self.textureId; // try to get textureId + // try to get textureId. static implies late, but can't access this + final _ = (VideoPlayerController.file(File('')) as dynamic).textureId; + return (dynamic c) => c.textureId as int; } on NoSuchMethodError { - return self - .playerId; // since video_player 2.10.0 to support platform view + // since video_player 2.10.0 to support platform view + return (dynamic c) => c.playerId as int; } - } + }(); + /// Indicates whether current media is a live stream or not bool isLive() { - return _platform.isLive(_getId()); + return _platform.isLive(_getId(this)); } /// Get current media info. MediaInfo? getMediaInfo() { - return _platform.getMediaInfo(_getId()); + return _platform.getMediaInfo(_getId(this)); } /// set additional properties /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpropertyconst-stdstring-key-const-stdstring-value void setProperty(String name, String value) { - _platform.setProperty(_getId(), name, value); + _platform.setProperty(_getId(this), name, value); } /// Change video decoder list on the fly. /// NOTE: the default decoder list used by [VideoPlayerController] constructor MUST set via [registerWith]. /// Detail: https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setdecodersmediatype-type-const-stdvectorstdstring-names void setVideoDecoders(List value) { - _platform.setVideoDecoders(_getId(), value); + _platform.setVideoDecoders(_getId(this), value); } /// Start to record if [to] is not null. Stop recording if [to] is null. @@ -68,7 +86,7 @@ extension FVPControllerExtensions on VideoPlayerController { /// If not stopped by user, recording will be stopped when playback is finished. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-recordconst-char-url--nullptr-const-char-format--nullptr void record({String? to, String? format}) { - _platform.record(_getId(), to: to, format: format); + _platform.record(_getId(this), to: to, format: format); } /// Take a snapshot for current rendered frame. @@ -77,13 +95,13 @@ extension FVPControllerExtensions on VideoPlayerController { /// [height] snapshot height. if not set, result is `mediaInfo.video[current_track].codec.height` /// Return rgba data of image size [width]x[height], stride is `width*4` Future snapshot({int? width, int? height}) async { - return _platform.snapshot(_getId(), width: width, height: height); + return _platform.snapshot(_getId(this), width: width, height: height); } /// Set position range in milliseconds. Can be used by A-B loop. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setrangeint64_t-a-int64_t-b--int64_max void setRange({required int from, int to = -1}) { - _platform.setRange(_getId(), from: from, to: to); + _platform.setRange(_getId(this), from: from, to: to); } /// Set duration range(milliseconds) of buffered data. @@ -99,91 +117,91 @@ extension FVPControllerExtensions on VideoPlayerController { /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setbufferrangeint64_t-minms-int64_t-maxms-bool-drop--false /// NOTE: default values are set in [VideoPlayerController] constructor if 'lowLatency' is enabled in [registerWith] void setBufferRange({int min = -1, int max = -1, bool drop = false}) { - _platform.setBufferRange(_getId(), min: min, max: max, drop: drop); + _platform.setBufferRange(_getId(this), min: min, max: max, drop: drop); } /// fast seek to a key frame Future fastSeekTo(Duration position) async { - return _platform.fastSeekTo(_getId(), position); + return _platform.fastSeekTo(_getId(this), position); } /// Step forward or backward. /// Step forward if [frames] > 0, backward otherwise. Future step({int frames = 1}) async { - return _platform.step(_getId(), frames); + return _platform.step(_getId(this), frames); } /// set brightness. -1 <= [value] <= 1 void setBrightness(double value) { - _platform.setBrightness(_getId(), value); + _platform.setBrightness(_getId(this), value); } /// set contrast. -1 <= [value] <= 1 void setContrast(double value) { - _platform.setContrast(_getId(), value); + _platform.setContrast(_getId(this), value); } /// set hue. -1 <= [value] <= 1 void setHue(double value) { - _platform.setHue(_getId(), value); + _platform.setHue(_getId(this), value); } /// set saturation. -1 <= [value] <= 1 void setSaturation(double value) { - _platform.setSaturation(_getId(), value); + _platform.setSaturation(_getId(this), value); } /// Set active audio tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external audio data source via [setExternalAudio] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks void setAudioTracks(List value) { - _platform.setAudioTracks(_getId(), value); + _platform.setAudioTracks(_getId(this), value); } /// Get active audio tracks. List? getActiveAudioTracks() { - return _platform.getActiveAudioTracks(_getId()); + return _platform.getActiveAudioTracks(_getId(this)); } /// Set active video tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external video data source via [setExternalVideo] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks void setVideoTracks(List value) { - _platform.setVideoTracks(_getId(), value); + _platform.setVideoTracks(_getId(this), value); } /// Get active video tracks. List? getActiveVideoTracks() { - return _platform.getActiveVideoTracks(_getId()); + return _platform.getActiveVideoTracks(_getId(this)); } /// Set active subtitle tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external subtitle data source via [setExternalSubtitle] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks void setSubtitleTracks(List value) { - _platform.setSubtitleTracks(_getId(), value); + _platform.setSubtitleTracks(_getId(this), value); } /// Get active subtitle tracks. List? getActiveSubtitleTracks() { - return _platform.getActiveSubtitleTracks(_getId()); + return _platform.getActiveSubtitleTracks(_getId(this)); } /// set an external audio data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalAudio(String uri) { - _platform.setExternalAudio(_getId(), uri); + _platform.setExternalAudio(_getId(this), uri); } /// set an external video data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalVideo(String uri) { - _platform.setExternalVideo(_getId(), uri); + _platform.setExternalVideo(_getId(this), uri); } /// set an external subtitle data source /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setmediaconst-char-url-mediatype-type void setExternalSubtitle(String uri) { - _platform.setExternalSubtitle(_getId(), uri); + _platform.setExternalSubtitle(_getId(this), uri); } } From 5b0edc1af49b96d3a5f30aad0a07a4fc4e3e06ea Mon Sep 17 00:00:00 2001 From: wang-bin Date: Wed, 11 Jun 2025 23:35:37 +0800 Subject: [PATCH 045/109] set linux decoders depending on device model fix https://github.com/wang-bin/fvp/issues/266 why v4l2m2m can freeze? --- lib/src/extensions.dart | 15 +++++++++++++++ lib/src/video_player_mdk.dart | 19 +++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index b4d77d2..01407e9 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -17,6 +17,21 @@ extension PlatformEx on Platform { return Libfvp.isEmulator(); } + // TODO: check content /proc/device-tree/model? + static bool isRockchip() { + if (!Platform.isLinux) { + return false; + } + return File('/dev/mpp_service').existsSync(); + } + + static bool isRaspberryPi() { + if (!Platform.isLinux) { + return false; + } + return File('/dev/vchiq').existsSync(); + } + static String assetUri(String asset, {String? package}) { final key = asset; switch (Platform.operatingSystem) { diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 7a94b17..aa29165 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -156,19 +156,18 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { if (_decoders == null && !PlatformEx.isAndroidEmulator()) { // prefer hardware decoders - const vd = { + const vdRk = ['rkmpp', 'FFmpeg']; + const vdPi = ['V4L2M2M', 'FFmpeg:hwcontext=drm', 'FFmpeg']; + final vdLinux = PlatformEx.isRockchip() + ? vdRk + : (PlatformEx.isRaspberryPi() + ? vdPi + : ['VAAPI', 'CUDA', 'VDPAU', 'FFmpeg']); + final vd = { 'windows': ['MFT:d3d=11', "D3D11", "DXVA", 'CUDA', 'FFmpeg'], 'macos': ['VT', 'FFmpeg'], 'ios': ['VT', 'FFmpeg'], - 'linux': [ - 'VAAPI', - 'CUDA', - 'VDPAU', - 'rkmpp', - 'V4L2M2M', - 'FFmpeg:hwcontext=drm', - 'FFmpeg' - ], + 'linux': vdLinux, 'android': ['AMediaCodec', 'FFmpeg'], }; _decoders = vd[Platform.operatingSystem]; From ca049f8d829ac81ccb2c3f2cfb694a2bdf660975 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Wed, 11 Jun 2025 23:44:34 +0800 Subject: [PATCH 046/109] add VideoPlayerController.setProgram() --- lib/src/controller.dart | 7 +++++++ lib/src/video_player_mdk.dart | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index b357bd5..0da6b35 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -151,6 +151,13 @@ extension FVPControllerExtensions on VideoPlayerController { _platform.setSaturation(_getId(this), value); } + /// Set a program to play. used by mpegts programs or hls. + /// [programId] is the index in [MediaInfo.programs] + /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks + void setProgram(int programId) { + _platform.setProgram(_getId(this), programId); + } + /// Set active audio tracks. Other tracks will be disabled. /// The tracks can be from data source from [VideoPlayerController] constructor, or an external audio data source via [setExternalAudio] /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setactivetracksmediatype-type-const-stdsetint-tracks diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index aa29165..376e4f6 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -461,6 +461,10 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _players[textureId]?.setVideoEffect(mdk.VideoEffect.saturation, [value]); } + void setProgram(int textureId, int programId) { + _players[textureId]?.setActiveTracks(mdk.MediaType.unknown, [programId]); + } + // embedded tracks, can be main data source from create(), or external media source via setExternalAudio void setAudioTracks(int textureId, List value) { _players[textureId]?.activeAudioTracks = value; From 130ccf54e8c9e11fb6e3af76e7aeec98486a379b Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 12 Jun 2025 23:04:14 +0800 Subject: [PATCH 047/109] fix web/wasm build --- lib/src/video_player_dummy.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/video_player_dummy.dart b/lib/src/video_player_dummy.dart index f3e8291..da03a8a 100644 --- a/lib/src/video_player_dummy.dart +++ b/lib/src/video_player_dummy.dart @@ -44,6 +44,8 @@ class MdkVideoPlayerPlatform { void setSaturation(int textureId, double value) {} + void setProgram(int textureId, int programId) {} + void setAudioTracks(int textureId, List value) {} List? getActiveAudioTracks(int textureId) { From 4246baf20cfffc20f252cfda4ff386ed9c89b488 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 12 Jun 2025 23:12:27 +0800 Subject: [PATCH 048/109] use highest resolution of all tracks otherwise switching program or video tracks may always render in low resolution --- lib/src/player.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/src/player.dart b/lib/src/player.dart index ceb0fbe..9e8bb08 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; @@ -713,10 +713,19 @@ class Player { // loading=>loaded, then frame decoded return; } - final vc = mediaInfo.video?[0].codec; + var v = mediaInfo.video?[0]; + // we don't support dynamic texture size change, so use the max video codec width + if (v != null) { + for (final i in mediaInfo.video!) { + if (i.codec.width > v!.codec.width) { + v = i; + } + } + } // if no video stream, create a dummy texture of size 16x16 double w = 16; double h = 16; + final vc = v?.codec; if (vc != null) { if (vc.width <= 0 || vc.height <= 0) { // failed to parse video size, e.g. small probesize @@ -724,7 +733,7 @@ class Player { } w = vc.width.toDouble(); h = (vc.height.toDouble() / vc.par).roundToDouble(); - if (mediaInfo.video![0].rotation % 180 == 90) { + if (v!.rotation % 180 == 90) { (w, h) = (h, w); } } From 39945563644528ef31e20bbb06f796e2e7bf17ca Mon Sep 17 00:00:00 2001 From: LinXunFeng Date: Fri, 13 Jun 2025 10:52:44 +0800 Subject: [PATCH 049/109] add missing dispose for textureId --- lib/src/player.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/player.dart b/lib/src/player.dart index 9e8bb08..837b62c 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -155,6 +155,7 @@ class Player { /// Release resources void dispose() async { if (_pp == nullptr) { + textureId.dispose(); return; } // await: ensure no player ref in fvp plugin before mdkPlayerAPI_delete() in dart @@ -170,6 +171,7 @@ class Player { Libmdk.instance.mdkPlayerAPI_delete(_pp); calloc.free(_pp); _pp = nullptr; + textureId.dispose(); } /// Release current texture then create a new one for current [media], and update [textureId]. From 241d60b4f1ee4d57f11edec8d22c5a87e6c1b954 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sun, 15 Jun 2025 17:02:53 +0800 Subject: [PATCH 050/109] linux: set wayland display --- linux/fvp_plugin.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index d410017..013754a 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -239,6 +240,8 @@ void fvp_plugin_register_with_registrar(FlPluginRegistrar* registrar) { auto gdisp = gdk_display_get_default(); if (GDK_IS_X11_DISPLAY(gdisp)) { mdk::SetGlobalOption("X11Display", GDK_DISPLAY_XDISPLAY(gdisp)); + } else if (GDK_IS_WAYLAND_DISPLAY(gdisp)) { + mdk::SetGlobalOption("wl_display*", gdk_wayland_display_get_wl_display(gdisp)); } mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); } From 194571c74fee239e7fd5ca9fc3594ca1fb7f3901 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 17 Jun 2025 01:15:02 +0800 Subject: [PATCH 051/109] linux: fix gl resouces cleanup in a wrong context. #270 assmue raster thread and context won't change. what about hot reload? --- linux/fvp_plugin.cc | 48 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index 013754a..72a45f6 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include @@ -39,6 +41,9 @@ struct _PlayerTexture { G_DEFINE_TYPE(PlayerTexture, player_texture, fl_texture_gl_get_type()) +// will raster thread change? +static thread_local unordered_map>> gDelayCleanup; + class TexturePlayer final : public mdk::Player { @@ -84,6 +89,25 @@ class TexturePlayer final : public mdk::Player PlayerTexture* flTex; // hold ref }; +static void try_to_cleanup_gl_res(PlayerTexture* self) +{ + if (gDelayCleanup.empty()) + return; + clog << gDelayCleanup.size() << " delayed cleanup contexts in this thread " << this_thread::get_id() << ", current gl context: " << self->ctx << endl; + for (auto i : gDelayCleanup) { + clog << "delayed cleanup context: " << i.first << endl; + } + for (auto ctx : {self->ctx, (GdkGLContext*)nullptr}) { // nullptr: null context. assume rendering context never changes, so cleanup here + if (auto it = gDelayCleanup.find(ctx); it != gDelayCleanup.cend()) { + if (!it->second.empty()) + clog << it->second.size() << " executing delayed tasks for context: " << ctx << endl; + for (auto& task : it->second) { + task(); + } + gDelayCleanup.erase(it); + } + } +} // called in a current gl context static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, uint32_t *name, @@ -93,6 +117,8 @@ static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, if (self->fbo == 0) { self->ctx = gdk_gl_context_get_current(); // fbo can not be shared glGenFramebuffers(1, &self->fbo); + + try_to_cleanup_gl_res(self); } if (self->texture_id == 0) { GLint prevFbo = 0; @@ -128,9 +154,25 @@ static void player_texture_dispose(GObject* obj) { G_OBJECT_CLASS(player_texture_parent_class)->dispose(obj); auto self = PLAYER_TEXTURE(obj); auto ctx = gdk_gl_context_get_current(); - gdk_gl_context_make_current(self->ctx); - glDeleteTextures(1, &self->texture_id); - glDeleteFramebuffers(1, &self->fbo); + if (self->ctx != ctx) { + clog << "gdk gl context change: " << self->ctx << " => " << ctx << endl; + if (self->ctx) // null: dispose w/o populate? why? + gdk_gl_context_make_current(self->ctx); + } + const auto newCtx = gdk_gl_context_get_current(); + auto cleanup = [tex = self->texture_id, fbo = self->fbo]() { + glDeleteTextures(1, &tex); + glDeleteFramebuffers(1, &fbo); + }; + if (self->ctx == newCtx && newCtx) { + cleanup(); + } else { + clog << self->ctx << " self->ctx is gl context: " << GDK_IS_GL_CONTEXT(self->ctx) << endl; + // delay cleanup until the context is back + gDelayCleanup[self->ctx].push_back(std::move(cleanup)); + clog << "delay cleanup. dispose w/o a correct gl context: " << ctx << " => " << newCtx << " / " << self->ctx << endl; + clog << gDelayCleanup[self->ctx].size() << " context delayed cleanup for this thread " << this_thread::get_id() << endl; + } if (ctx) gdk_gl_context_make_current(ctx); } From c0467ef005f86583400121512282273da1383624 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Wed, 18 Jun 2025 00:41:01 +0800 Subject: [PATCH 052/109] linux: return immediately if gl resource not created #270 dispose w/o populate, self->ctx is not initialized too --- linux/fvp_plugin.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index 72a45f6..1cf63e6 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -117,7 +117,7 @@ static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, if (self->fbo == 0) { self->ctx = gdk_gl_context_get_current(); // fbo can not be shared glGenFramebuffers(1, &self->fbo); - + clog << "created fbo " + std::to_string(self->fbo) << endl; try_to_cleanup_gl_res(self); } if (self->texture_id == 0) { @@ -153,6 +153,10 @@ static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, static void player_texture_dispose(GObject* obj) { G_OBJECT_CLASS(player_texture_parent_class)->dispose(obj); auto self = PLAYER_TEXTURE(obj); + if (!self->texture_id && !self->fbo) { + clog << "texture and fbo are not created yet" << endl; + return; + } auto ctx = gdk_gl_context_get_current(); if (self->ctx != ctx) { clog << "gdk gl context change: " << self->ctx << " => " << ctx << endl; From f7dd4c5577063bfc3441ceebcad47dfb1ea521f4 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Thu, 19 Jun 2025 00:41:36 +0800 Subject: [PATCH 053/109] linux: add env FVP_GL_CLEANUP_DELAY --- linux/fvp_plugin.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index 1cf63e6..9e173da 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -157,10 +158,12 @@ static void player_texture_dispose(GObject* obj) { clog << "texture and fbo are not created yet" << endl; return; } + static const auto env = std::getenv("FVP_GL_CLEANUP_DELAY"); + static const bool kDelayCleanup = env && std::atoi(env); auto ctx = gdk_gl_context_get_current(); if (self->ctx != ctx) { clog << "gdk gl context change: " << self->ctx << " => " << ctx << endl; - if (self->ctx) // null: dispose w/o populate? why? + if (self->ctx && !kDelayCleanup) // null: dispose w/o populate. why? gdk_gl_context_make_current(self->ctx); } const auto newCtx = gdk_gl_context_get_current(); @@ -174,10 +177,10 @@ static void player_texture_dispose(GObject* obj) { clog << self->ctx << " self->ctx is gl context: " << GDK_IS_GL_CONTEXT(self->ctx) << endl; // delay cleanup until the context is back gDelayCleanup[self->ctx].push_back(std::move(cleanup)); - clog << "delay cleanup. dispose w/o a correct gl context: " << ctx << " => " << newCtx << " / " << self->ctx << endl; + clog << (kDelayCleanup ? "force " : "") << "delay cleanup. dispose w/o a correct gl context: " << ctx << " => " << newCtx << " / " << self->ctx << endl; clog << gDelayCleanup[self->ctx].size() << " context delayed cleanup for this thread " << this_thread::get_id() << endl; } - if (ctx) + if (ctx && ctx != newCtx) // make current was called gdk_gl_context_make_current(ctx); } From b7a688e408a3f267e27cbeb81588dc26fd146dd1 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Fri, 20 Jun 2025 13:26:20 +0800 Subject: [PATCH 054/109] linux: gl resource cleanup ASAP if dispose thread == raster thread, cleanup in dispose, and may have context change. otherwise, cleanup in raster thread in the next populate --- linux/fvp_plugin.cc | 99 ++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index 9e173da..c9c6396 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -5,6 +5,7 @@ // found in the LICENSE file. #include "include/fvp/fvp_plugin.h" +#include #include #include #include @@ -14,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,6 +33,29 @@ G_DECLARE_FINAL_TYPE(PlayerTexture, player_texture, FL, PLAYER_TEXTURE, FlTextur #define PLAYER_TEXTURE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), player_texture_get_type(), PlayerTexture)) + +class CleanupTask { +public: + CleanupTask(GdkGLContext* ctx, function&& callback) : ctx_(ctx), cb_(std::move(callback)) {} + ~CleanupTask() { + auto ctx = gdk_gl_context_get_current(); + if (ctx_ != ctx) { + clog << "gdk gl context change: " << ctx_ << " => " << ctx << endl; + gdk_gl_context_make_current(ctx_); + } + cb_(); + if (ctx_ != ctx) { + gdk_gl_context_make_current(ctx); + } + } + + bool disposed = false; +private: + GdkGLContext* ctx_; + function cb_; +}; +static thread_local list> gCleanupTasks; + struct _PlayerTexture { FlTextureGL parent_instance; @@ -38,14 +64,11 @@ struct _PlayerTexture { GLuint fbo; TexturePlayer* player; + CleanupTask* cleanup; }; G_DEFINE_TYPE(PlayerTexture, player_texture, fl_texture_gl_get_type()) -// will raster thread change? -static thread_local unordered_map>> gDelayCleanup; - - class TexturePlayer final : public mdk::Player { public: @@ -90,42 +113,32 @@ class TexturePlayer final : public mdk::Player PlayerTexture* flTex; // hold ref }; -static void try_to_cleanup_gl_res(PlayerTexture* self) -{ - if (gDelayCleanup.empty()) - return; - clog << gDelayCleanup.size() << " delayed cleanup contexts in this thread " << this_thread::get_id() << ", current gl context: " << self->ctx << endl; - for (auto i : gDelayCleanup) { - clog << "delayed cleanup context: " << i.first << endl; - } - for (auto ctx : {self->ctx, (GdkGLContext*)nullptr}) { // nullptr: null context. assume rendering context never changes, so cleanup here - if (auto it = gDelayCleanup.find(ctx); it != gDelayCleanup.cend()) { - if (!it->second.empty()) - clog << it->second.size() << " executing delayed tasks for context: " << ctx << endl; - for (auto& task : it->second) { - task(); - } - gDelayCleanup.erase(it); - } - } -} - // called in a current gl context static gboolean player_texture_populate(FlTextureGL *texture, uint32_t *target, uint32_t *name, uint32_t *width, uint32_t *height, GError **error) { + // cleanup ASAP before drawing the next frame + if (auto count = std::erase_if(gCleanupTasks, [](auto task) { return task->disposed; })) { + clog << std::to_string(count) + " cleanup tasks executed in raster thread " << this_thread::get_id() << endl; + } PlayerTexture *self = PLAYER_TEXTURE(texture); if (self->fbo == 0) { self->ctx = gdk_gl_context_get_current(); // fbo can not be shared glGenFramebuffers(1, &self->fbo); - clog << "created fbo " + std::to_string(self->fbo) << endl; - try_to_cleanup_gl_res(self); - } - if (self->texture_id == 0) { + assert(self->texture_id == 0); GLint prevFbo = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo); glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); glGenTextures(1, &self->texture_id); + clog << "created fbo: " + std::to_string(self->fbo) + " tex: " + std::to_string(self->texture_id) + " in raster thread " << this_thread::get_id() << endl; + auto task = make_shared(self->ctx, [tex = self->texture_id, fbo = self->fbo]() { + clog << "delete fbo: " + std::to_string(fbo) + " tex: " + std::to_string(tex) << endl; + glDeleteTextures(1, &tex); + glDeleteFramebuffers(1, &fbo); + }); + self->cleanup = task.get(); + gCleanupTasks.push_back(std::move(task)); + glBindTexture(GL_TEXTURE_2D, self->texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, self->player->width, self->player->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, GL_TEXTURE_2D, self->texture_id, 0); @@ -158,30 +171,13 @@ static void player_texture_dispose(GObject* obj) { clog << "texture and fbo are not created yet" << endl; return; } - static const auto env = std::getenv("FVP_GL_CLEANUP_DELAY"); - static const bool kDelayCleanup = env && std::atoi(env); - auto ctx = gdk_gl_context_get_current(); - if (self->ctx != ctx) { - clog << "gdk gl context change: " << self->ctx << " => " << ctx << endl; - if (self->ctx && !kDelayCleanup) // null: dispose w/o populate. why? - gdk_gl_context_make_current(self->ctx); - } - const auto newCtx = gdk_gl_context_get_current(); - auto cleanup = [tex = self->texture_id, fbo = self->fbo]() { - glDeleteTextures(1, &tex); - glDeleteFramebuffers(1, &fbo); - }; - if (self->ctx == newCtx && newCtx) { - cleanup(); - } else { - clog << self->ctx << " self->ctx is gl context: " << GDK_IS_GL_CONTEXT(self->ctx) << endl; - // delay cleanup until the context is back - gDelayCleanup[self->ctx].push_back(std::move(cleanup)); - clog << (kDelayCleanup ? "force " : "") << "delay cleanup. dispose w/o a correct gl context: " << ctx << " => " << newCtx << " / " << self->ctx << endl; - clog << gDelayCleanup[self->ctx].size() << " context delayed cleanup for this thread " << this_thread::get_id() << endl; + if (self->cleanup) { + self->cleanup->disposed = true; + clog << "try to cleanup gl resources in dispose thread " << this_thread::get_id() << endl; + if (auto count = std::erase_if(gCleanupTasks, [](auto task) { return task->disposed; })) { + clog << std::to_string(count) + " cleanup tasks executed in dispose thread " << this_thread::get_id() << endl; + } } - if (ctx && ctx != newCtx) // make current was called - gdk_gl_context_make_current(ctx); } static void player_texture_class_init(PlayerTextureClass* klass) { @@ -195,6 +191,7 @@ static void player_texture_init(PlayerTexture* self) { self->fbo = 0; self->player = nullptr; self->ctx = nullptr; + self->cleanup = nullptr; } From 4dc12dbc433060d54043103f16f479a23fe37e97 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 21 Jun 2025 20:13:42 +0800 Subject: [PATCH 055/109] elinux: release gl resources with wrong texture_registrar.h --- elinux/fvp_plugin.cc | 68 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/elinux/fvp_plugin.cc b/elinux/fvp_plugin.cc index 0149521..38e325f 100644 --- a/elinux/fvp_plugin.cc +++ b/elinux/fvp_plugin.cc @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include "mdk/RenderAPI.h" #include "mdk/Player.h" @@ -61,6 +63,19 @@ using namespace std; } while(false) namespace { +class CleanupTask { +public: + CleanupTask(function callback) : cb_(callback) {} + ~CleanupTask() { + cb_(); + } + + bool disposed = false; +private: + function cb_; +}; +static thread_local list> gCleanupTasks; + class TexturePlayer final : public mdk::Player { public: @@ -90,37 +105,41 @@ class TexturePlayer final : public mdk::Player }); } - template // use template to not instantiate false branch - void unregisterIfGoodHeader() { + template // use template to not instantiate false branch + bool unregisterIfGoodHeader(F&& f) { if constexpr (requires(T* t){ t->UnregisterTexture(0, nullptr); }) { - auto gfxRelease = [disp = disp_, img = img_, tex = tex_, fbo = fbo_, eglDestroyImageKHR = eglDestroyImageKHR]() { // called in raster thread and gl context is correct - if (img != EGL_NO_IMAGE_KHR) - EGL_WARN(eglDestroyImageKHR(disp, img)); - if (tex) - GL_WARN(glDeleteTextures(1, &tex)); - if (fbo) - GL_WARN(glDeleteFramebuffers(1, &fbo)); - }; - texture_registrar_->UnregisterTexture(textureId, gfxRelease); + texture_registrar_->UnregisterTexture(textureId, std::forward(f)); + return true; } + return false; + } + + template // use template to not instantiate false branch + bool unregisterCanPostTask() { + return requires(T* t){ t->UnregisterTexture(0, nullptr); }; } ~TexturePlayer() override { + if (task_) + task_->disposed = true; setRenderCallback(nullptr); // texture_registrar.h in flutter-elinux is outdated and results in crash. https://github.com/sony/flutter-embedded-linux/issues/438 - unregisterIfGoodHeader(); - setVideoSurfaceSize(-1, -1); // no gl context now, but gl resources will be released in raster thread later in ensureVideo() + if (!unregisterIfGoodHeader(cleanup_)) { + texture_registrar_->UnregisterTexture(textureId); + } + setVideoSurfaceSize(-1, -1); // no gl context now, but gl resources will be released in raster thread later in ensureVideo() } EGLImageKHR ensureVideo(size_t width, size_t height, EGLDisplay disp, EGLContext c) { + if (auto count = std::erase_if(gCleanupTasks, [](auto task) { return task->disposed; })) { + clog << std::to_string(count) + " cleanup tasks executed in raster thread " << this_thread::get_id() << endl; + } if (fbo_ == 0) { ctx_ = c; // fbo can not be shared disp_ = disp; draw_ = eglGetCurrentSurface(EGL_DRAW); read_ = eglGetCurrentSurface(EGL_READ); GL_WARN(glGenFramebuffers(1, &fbo_)); - } - if (tex_ == 0) { GLint prevFbo = 0; GL_WARN(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo)); GL_WARN(glBindFramebuffer(GL_FRAMEBUFFER, fbo_)); @@ -148,6 +167,23 @@ class TexturePlayer final : public mdk::Player if (img_ == EGL_NO_IMAGE_KHR) { clog << "eglCreateImageKHR error" << endl; } + clog << gCleanupTasks.size() << " tasks. created fbo: " + std::to_string(fbo_) + " tex: " + std::to_string(tex_) + " in raster thread " << this_thread::get_id() << endl; + + cleanup_ = [disp = disp_, img = img_, tex = tex_, fbo = fbo_, eglDestroyImageKHR = eglDestroyImageKHR]() { // called in raster thread and gl context is correct + clog << "delete fbo: " + std::to_string(fbo) + " tex: " + std::to_string(tex) << endl; + if (img != EGL_NO_IMAGE_KHR) + EGL_WARN(eglDestroyImageKHR(disp, img)); + if (tex) + GL_WARN(glDeleteTextures(1, &tex)); + if (fbo) + GL_WARN(glDeleteFramebuffers(1, &fbo)); + }; + if (!unregisterCanPostTask()) { + clog << "incompatible texture_registrar.h, see https://github.com/sony/flutter-embedded-linux/issues/438" << endl; + auto task = make_shared(cleanup_); + task_ = task.get(); + gCleanupTasks.push_back(std::move(task)); + } } renderVideo(); @@ -159,6 +195,8 @@ class TexturePlayer final : public mdk::Player unique_ptr fltImg_ = make_unique(); unique_ptr fltTex_; flutter::TextureRegistrar* texture_registrar_ = nullptr; + CleanupTask* task_ = nullptr; + function cleanup_ = {}; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; From 3045dc4685aaf100b6043224796ebbdd87576cce Mon Sep 17 00:00:00 2001 From: wang-bin Date: Tue, 1 Jul 2025 20:01:37 +0800 Subject: [PATCH 056/109] v0.33.0 --- CHANGELOG.md | 8 ++++++++ darwin/fvp.podspec | 2 +- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8a0cad..9b63839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.33.0 + +* improve test textureId/playerId only once +* add `VideoPlayerController.setProgram()` +* use highest resolution for rendering if stream have multiple programs/tracks +* enable wayland display +* linux/elinux: improve gl resource cleanup + ## 0.32.1 * compatible with video_player 2.10.0+ diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index d22162c..eeefe01 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.32.1' + s.version = '0.33.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. diff --git a/pubspec.yaml b/pubspec.yaml index 975c19b..19977a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.32.1 +version: 0.33.0 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 2710850..0a6315b 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.32.1 +project(${PROJECT_NAME} VERSION 0.33.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 78bddb6142ff82742538220f96d0643795cb854a Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 5 Jul 2025 22:31:13 +0800 Subject: [PATCH 057/109] don't set protocol_whitelist for network source. fix #274 protocol_whitelist was added for #15 to test m3u8 local files. if set it unconditionally, other protocols are unusable if not in the list --- lib/src/video_player_mdk.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 376e4f6..3037214 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -254,9 +254,12 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { player.setProperty('avformat.safe', '0'); player.setProperty('avio.reconnect', '1'); player.setProperty('avio.reconnect_delay_max', '7'); - player.setProperty('avio.protocol_whitelist', - 'file,rtmp,http,https,tls,rtp,tcp,udp,crypto,httpproxy,data,concatf,concat,subfile'); player.setProperty('avformat.rtsp_transport', 'tcp'); + if (dataSource.sourceType != DataSourceType.network) { + // for m3u8 local file etc. + player.setProperty('avio.protocol_whitelist', + 'file,ftp,rtmp,http,https,tls,rtp,tcp,udp,crypto,httpproxy,data,concatf,concat,subfile'); + } _playerOpts?.forEach((key, value) { player.setProperty(key, value); }); From 0daf97c75319dab9f9653406a322c0cddf4615c1 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 10 Jul 2025 16:24:53 +0800 Subject: [PATCH 058/109] readme: hwdec for rpi & rk --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ab06b6d..0bdc253 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,10 @@ For other platforms, set environment var `FVP_DEPS_LATEST=1` and rebuilt, will u - Callbacks and events in C++ are notified by ReceivePort - Function with a one time callback is async and returns a future +# Enable Hardware Decoders for Embedded Linux +delete libffmpeg.so.* in your app bundle, which is copied from libmdk sdk. +- Raspberry Pi: install ffmpeg and system ffmpeg libraries with v4l2 acceleration will be used. +- Rockchip: build and install https://github.com/nyanmisaka/ffmpeg-rockchip . You may also need environment var `export GL_UBO=1` for arm mali driver on linux 6.x kernel to avoid gl driver bug. # Enable Subtitles From 44913ff6d458503b2bb1038ecc515a11e9ff1c2b Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 12 Jul 2025 08:32:23 +0800 Subject: [PATCH 059/109] enable dav1d hap decoder. #276 --- lib/src/video_player_mdk.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 3037214..3c25885 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -156,19 +156,19 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { if (_decoders == null && !PlatformEx.isAndroidEmulator()) { // prefer hardware decoders - const vdRk = ['rkmpp', 'FFmpeg']; - const vdPi = ['V4L2M2M', 'FFmpeg:hwcontext=drm', 'FFmpeg']; + const vdRk = ['rkmpp', 'FFmpeg', 'dav1d']; + const vdPi = ['V4L2M2M', 'FFmpeg:hwcontext=drm', 'FFmpeg', 'dav1d']; final vdLinux = PlatformEx.isRockchip() ? vdRk : (PlatformEx.isRaspberryPi() ? vdPi - : ['VAAPI', 'CUDA', 'VDPAU', 'FFmpeg']); + : ['VAAPI', 'CUDA', 'VDPAU', 'hap', 'FFmpeg', 'dav1d']); final vd = { - 'windows': ['MFT:d3d=11', "D3D11", "DXVA", 'CUDA', 'FFmpeg'], - 'macos': ['VT', 'FFmpeg'], - 'ios': ['VT', 'FFmpeg'], + 'windows': ['MFT:d3d=11', "D3D11", "DXVA", 'CUDA', 'hap', 'FFmpeg', 'dav1d'], + 'macos': ['VT', 'hap', 'FFmpeg', 'dav1d'], + 'ios': ['VT', 'FFmpeg', 'dav1d'], 'linux': vdLinux, - 'android': ['AMediaCodec', 'FFmpeg'], + 'android': ['AMediaCodec', 'FFmpeg', 'dav1d'], }; _decoders = vd[Platform.operatingSystem]; } From 43fd8736aeb2a13d67771d1126922834e561f072 Mon Sep 17 00:00:00 2001 From: wangbin Date: Fri, 18 Jul 2025 10:57:41 +0800 Subject: [PATCH 060/109] v0.33.1 --- CHANGELOG.md | 5 +++++ darwin/fvp.podspec | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b63839..12654e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.33.1 + +* fix protocol whitelist +* enable dav1d, hap + ## 0.33.0 * improve test textureId/playerId only once diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index eeefe01..e1f36e2 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.33.0' + s.version = '0.33.1' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.33.0' + s.dependency 'mdk', '~> 0.33.1' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 19977a7..70f0123 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.33.0 +version: 0.33.1 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 0a6315b..d136383 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.33.0 +project(${PROJECT_NAME} VERSION 0.33.1 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 9361defbf86591e816470c8b8108b915c4b10fef Mon Sep 17 00:00:00 2001 From: wangbin Date: Fri, 15 Aug 2025 17:50:40 +0800 Subject: [PATCH 061/109] add 'rockchip' decoder the latest libmdk is required --- lib/src/video_player_mdk.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 3c25885..0146c6d 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -156,7 +156,7 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { if (_decoders == null && !PlatformEx.isAndroidEmulator()) { // prefer hardware decoders - const vdRk = ['rkmpp', 'FFmpeg', 'dav1d']; + const vdRk = ['rockchip', 'rkmpp', 'FFmpeg', 'dav1d']; const vdPi = ['V4L2M2M', 'FFmpeg:hwcontext=drm', 'FFmpeg', 'dav1d']; final vdLinux = PlatformEx.isRockchip() ? vdRk From c60262ceee4133a2ae58c88a79ec08d1cda6fc7d Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 19 Aug 2025 19:19:04 +0800 Subject: [PATCH 062/109] install plugins --- elinux/CMakeLists.txt | 1 + linux/CMakeLists.txt | 1 + windows/CMakeLists.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/elinux/CMakeLists.txt b/elinux/CMakeLists.txt index 3a12b1f..1d45215 100644 --- a/elinux/CMakeLists.txt +++ b/elinux/CMakeLists.txt @@ -44,6 +44,7 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE mdk) get_filename_component(MDK_LIB_DIR ${MDK_LIBRARY} DIRECTORY) set(fvp_bundled_libraries ${MDK_RUNTIME} + ${MDK_PLUGINS} ${MDK_FFMPEG} ${MDK_LIB_DIR}/libc++.so.1 PARENT_SCOPE diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index c471c36..a767a09 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -62,6 +62,7 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE mdk) get_filename_component(MDK_LIB_DIR ${MDK_LIBRARY} DIRECTORY) set(fvp_bundled_libraries ${MDK_RUNTIME} + ${MDK_PLUGINS} ${MDK_FFMPEG} ${MDK_LIB_DIR}/libc++.so.1 PARENT_SCOPE diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index d136383..0f7878f 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -70,6 +70,7 @@ get_filename_component(MDK_LIB_DIR ${MDK_LIBRARY} DIRECTORY) string(REPLACE "/lib/" "/bin/" MDK_BIN_DIR ${MDK_LIB_DIR}) set(fvp_bundled_libraries ${MDK_RUNTIME} + ${MDK_PLUGINS} ${MDK_FFMPEG} ${MDK_LIBASS} PARENT_SCOPE From 311110d02818717941a33f7458e8a40b5cc66d53 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 19 Aug 2025 19:39:14 +0800 Subject: [PATCH 063/109] ci --- .github/workflows/build.yml | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ee4172..e45ad52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -88,7 +88,7 @@ jobs: - version: '3.19.x' channel: 'beta' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -123,7 +123,7 @@ jobs: - version: '3.10.x' channel: 'beta' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -148,7 +148,7 @@ jobs: Linux-arm64: - if: false + if: true runs-on: ubuntu-24.04-arm defaults: run: @@ -157,19 +157,19 @@ jobs: fail-fast: false matrix: version: ['any'] - channel: ['stable'] + channel: ['master'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' + - run: | + sudo apt-get update -y + sudo apt-get install -y cmake clang ninja-build libgtk-3-dev libpulse-dev - uses: subosito/flutter-action@v2 with: flutter-version: ${{ matrix.version }} channel: ${{ matrix.channel }} cache: true - - run: | - sudo apt-get update -y - sudo apt-get install -y cmake clang ninja-build libgtk-3-dev libpulse-dev # - run: flutter config --enable-linux-desktop - run: flutter doctor --verbose - run: flutter pub get @@ -184,12 +184,13 @@ jobs: Snap-arm64: + if: false runs-on: ubuntu-24.04-arm defaults: run: working-directory: example steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - name: Install dependencies @@ -218,7 +219,7 @@ jobs: matrix: target: [linux] #, apk] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: actions/setup-java@v4 @@ -246,7 +247,7 @@ jobs: host: [ubuntu] version: ['3.22.x', 'any'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -279,7 +280,7 @@ jobs: matrix: wasm: ['--wasm', ''] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 From f5c524fc9909ec8661fea4c57a4a73cf5a9a0b4f Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 2 Sep 2025 09:46:31 +0800 Subject: [PATCH 064/109] v0.34.0 --- .github/workflows/build.yml | 4 ++-- CHANGELOG.md | 5 +++++ darwin/fvp.podspec | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e45ad52..a60c5e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -222,7 +222,7 @@ jobs: - uses: actions/checkout@v5 with: submodules: 'recursive' - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 if: ${{ matrix.target == 'apk' }} with: distribution: 'zulu' @@ -255,7 +255,7 @@ jobs: flutter-version: ${{ matrix.version }} channel: 'stable' cache: true - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: '17' diff --git a/CHANGELOG.md b/CHANGELOG.md index 12654e2..8b5c3c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.34.0 + +* add rockchip decoder +* deploy libmdk plugins + ## 0.33.1 * fix protocol whitelist diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index e1f36e2..d26dcbe 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.33.1' + s.version = '0.34.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.33.1' + s.dependency 'mdk', '~> 0.34.0' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 70f0123..1506ffa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.33.1 +version: 0.34.0 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 0f7878f..b4f2b0b 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.33.1 +project(${PROJECT_NAME} VERSION 0.34.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 01aa97275a8ddabf2242f847ae6867e50e4c186d Mon Sep 17 00:00:00 2001 From: wangbin Date: Fri, 5 Sep 2025 23:26:03 +0800 Subject: [PATCH 065/109] allow hls & segments w/o extension. fix #218 --- lib/src/video_player_mdk.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 0146c6d..a6c602e 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -255,6 +255,8 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { player.setProperty('avio.reconnect', '1'); player.setProperty('avio.reconnect_delay_max', '7'); player.setProperty('avformat.rtsp_transport', 'tcp'); + player.setProperty('avformat.extension_picky', '0'); + player.setProperty('avformat.allowed_segment_extensions', 'ALL'); if (dataSource.sourceType != DataSourceType.network) { // for m3u8 local file etc. player.setProperty('avio.protocol_whitelist', From 1f8209051900c4b270ab7a7a543a7aedf06de9e8 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 11 Sep 2025 10:38:27 +0800 Subject: [PATCH 066/109] add Player.onSubtitleText #291 --- lib/src/callbacks.cpp | 68 ++++++++++++++++++++++++++++++++++- lib/src/callbacks.h | 3 +- lib/src/player.dart | 18 ++++++++++ lib/src/video_player_mdk.dart | 10 +++++- 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/lib/src/callbacks.cpp b/lib/src/callbacks.cpp index dcaf0ee..61242bd 100644 --- a/lib/src/callbacks.cpp +++ b/lib/src/callbacks.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -248,6 +248,72 @@ FVP_EXPORT void MdkCallbacksRegisterPort(int64_t handle, void* post_c_object, in return p->data[type].mediaStatus.ret; }); + player->onSubtitleText([=](double start, double end, const std::vector& texts){ + auto sp = wp.lock(); + if (!sp) + return; + auto p = sp.get(); + const auto type = int(CallbackType::SubtitleText); + if (!(p->callbackTypes & (1 << type))) + return; + + Dart_CObject t{ + .type = Dart_CObject_kInt64, + .value = { + .as_int64 = type, + } + }; + Dart_CObject v0{ + .type = Dart_CObject_kDouble, + .value = { + .as_double = start, + } + }; + Dart_CObject v1{ + .type = Dart_CObject_kDouble, + .value = { + .as_double = end, + } + }; + std::vector textObjs; + std::vector textObjPtrs; + textObjs.reserve(texts.size()); + textObjPtrs.reserve(texts.size()); + for (const auto& s : texts) { + Dart_CObject txt{ + .type = Dart_CObject_kString, + .value = { + .as_string = s.data(), + } + }; + textObjs.push_back(txt); + textObjPtrs.push_back(&textObjs.back()); + } + Dart_CObject textArray{ + .type = Dart_CObject_kArray, + .value = { + .as_array = { + .length = (int)textObjPtrs.size(), + .values = textObjPtrs.data(), + }, + } + }; + Dart_CObject* arr[] = { &t, &v0, &v1, &textArray }; + Dart_CObject msg { + .type = Dart_CObject_kArray, + .value = { + .as_array = { + .length = std::size(arr), + .values = arr, + }, + } + }; + if (!postCObject(send_port, &msg)) { + clog << __func__ << __LINE__ << "postCObject error" << endl; + return; + } + }); + } FVP_EXPORT void MdkCallbacksUnregisterPort(int64_t handle) diff --git a/lib/src/callbacks.h b/lib/src/callbacks.h index bc7fc7a..166e364 100644 --- a/lib/src/callbacks.h +++ b/lib/src/callbacks.h @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2025 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -36,6 +36,7 @@ enum CallbackType { Log, Seek, // no register, one time callback Snapshot, // no register, one time callback + SubtitleText, Count, }; diff --git a/lib/src/player.dart b/lib/src/player.dart index 837b62c..dc25371 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -108,6 +108,13 @@ class Player { } _snapshot = null; } + case 8: + { + final start = message[1] as double; + final end = message[2] as double; + final texts = (message[3] as List).cast(); + _subtitleCb?.call(start, end, texts); + } } calloc.free(rep); }); @@ -710,6 +717,16 @@ class Player { } } + void onSubtitleText( + void Function(double start, double end, List text)? callback) { + _subtitleCb = callback; + if (callback == null) { + Libfvp.unregisterType(nativeHandle, 8); + } else { + Libfvp.registerType(nativeHandle, 8, false); + } + } + void _setVideoSize() { if (_videoSize.isCompleted) { // loading=>loaded, then frame decoded @@ -765,6 +782,7 @@ class Player { final _stateCb = []; final _statusCb = []; + Function(double start, double end, List text)? _subtitleCb; Future Function()? _prepareCb; bool _mute = false; diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index a6c602e..3a0a7fc 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -164,7 +164,15 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { ? vdPi : ['VAAPI', 'CUDA', 'VDPAU', 'hap', 'FFmpeg', 'dav1d']); final vd = { - 'windows': ['MFT:d3d=11', "D3D11", "DXVA", 'CUDA', 'hap', 'FFmpeg', 'dav1d'], + 'windows': [ + 'MFT:d3d=11', + "D3D11", + "DXVA", + 'CUDA', + 'hap', + 'FFmpeg', + 'dav1d' + ], 'macos': ['VT', 'hap', 'FFmpeg', 'dav1d'], 'ios': ['VT', 'FFmpeg', 'dav1d'], 'linux': vdLinux, From d82923c19663950abf873defeb4c6496edccabf8 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 30 Sep 2025 16:41:47 +0800 Subject: [PATCH 067/109] v0.35.0 --- CHANGELOG.md | 5 +++++ darwin/fvp.podspec | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b5c3c1..9d0c688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.35.0 + +* add Player.onSubtitleText +* allow hls & segments w/o or w/ any extension + ## 0.34.0 * add rockchip decoder diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index d26dcbe..0972042 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.34.0' + s.version = '0.35.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.34.0' + s.dependency 'mdk', '~> 0.35.0' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 1506ffa..19894cd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.34.0 +version: 0.35.0 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b4f2b0b..53d1105 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.34.0 +project(${PROJECT_NAME} VERSION 0.35.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 4a8fe2e52938cdf03b6730742cdb0ee13797e5a6 Mon Sep 17 00:00:00 2001 From: wangbin Date: Sat, 11 Oct 2025 16:55:26 +0800 Subject: [PATCH 068/109] upgrade gradle --- android/build.gradle | 10 +++++----- darwin/fvp.podspec | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 1f06351..19479c6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:8.7.0' + classpath("com.android.tools.build:gradle:8.9.1") } } @@ -27,7 +27,7 @@ android { } // Bumping the plugin compileSdkVersion requires all clients of this plugin // to bump the version in their app. - compileSdk 35 + compileSdk = 36 // Use the NDK version // declared in /android/app/build.gradle file of the Flutter project. @@ -50,12 +50,12 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } defaultConfig { - minSdkVersion 21 // lower version error in ndk 26. requires flutter > 3.19 + minSdk = 21 // lower version error in ndk 26. requires flutter > 3.19 externalNativeBuild { cmake { diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 0972042..ec62274 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -30,5 +30,5 @@ Flutter video player plugin. # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.resource_bundles = {'fvp_privacy' => ['PrivacyInfo.xcprivacy']} - s.swift_version = '5.0' +# s.swift_version = '5.0' end From 6d8d7ef2643af6c4694a8d3c1c9f973a7fc36d62 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 28 Oct 2025 13:19:59 +0800 Subject: [PATCH 069/109] cmake: fix linux arch for cmake4.0+ --- cmake/deps.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 3499bdb..e5ddd91 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -8,7 +8,9 @@ macro(fvp_setup_deps) set(MDK_SDK_PKG mdk-sdk-android.7z) elseif(LINUX OR CMAKE_SYSTEM_NAME MATCHES "Linux") set(MDK_SDK_PKG mdk-sdk-linux.tar.xz) - if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX].*64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][mM][dD]64") + if(CMAKE_C_COMPILER_ARCHITECTURE_ID MATCHES "[xX].*64") + set(MDK_SDK_PKG mdk-sdk-linux-x64.tar.xz) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX].*64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][mM][dD]64") set(MDK_SDK_PKG mdk-sdk-linux-x64.tar.xz) endif() else() From ad708448a85207689294a652dadb6d518e3652ff Mon Sep 17 00:00:00 2001 From: Apo Date: Thu, 13 Nov 2025 17:54:18 +0800 Subject: [PATCH 070/109] fix: Flutter 3.38.0 flutter version file not found https://github.com/wang-bin/fvp/issues/304 https://github.com/flutter/flutter/pull/172793 --- android/build.gradle | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 19479c6..064f620 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -101,9 +101,14 @@ def flutterSdkVersion = { } } assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - def version = file(flutterSdkPath + "/version") - assert version.exists(), "flutter version file not found" - return version.text.trim() + def versionFile = file(flutterSdkPath + '/bin/cache/flutter.version.json') + if (!versionFile.exists()) { + def version = file(flutterSdkPath + '/version') + assert version.exists(), 'flutter version file not found' + return version.text.trim() + } + def versionJson = new groovy.json.JsonSlurper().parseText(versionFile.text) + return versionJson.flutterVersion }() def preprocessJava(Map textMap) { From 0c1ecb2744ef114ef7fb60a196b2d29aa35daf76 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 19 Nov 2025 10:05:05 +0800 Subject: [PATCH 071/109] v0.35.1 --- CHANGELOG.md | 5 +++++ darwin/fvp.podspec | 2 +- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d0c688..2d58e30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.35.1 + +* fix linux arch for cmake 4.0+ +* fix flutter 3.38 android build + ## 0.35.0 * add Player.onSubtitleText diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index ec62274..a706c0a 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.35.0' + s.version = '0.35.1' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. diff --git a/pubspec.yaml b/pubspec.yaml index 19894cd..db22501 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.35.0 +version: 0.35.1 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 53d1105..b2293f7 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.35.0 +project(${PROJECT_NAME} VERSION 0.35.1 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From f19448ed4a0c20048519ef80ee44c3f9459ff3e5 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 19 Nov 2025 11:43:41 +0800 Subject: [PATCH 072/109] ffigen version range new version is not supported yet --- lib/src/generated_bindings.dart | 343 +++++++++++++++++++++++++++++++- pubspec.yaml | 2 +- 2 files changed, 334 insertions(+), 11 deletions(-) diff --git a/lib/src/generated_bindings.dart b/lib/src/generated_bindings.dart index 3ecf8c1..0697960 100644 --- a/lib/src/generated_bindings.dart +++ b/lib/src/generated_bindings.dart @@ -395,6 +395,72 @@ class NativeLibrary { bool Function( ffi.Pointer, ffi.Pointer)>(); + ffi.Pointer mdkAudioFrameAPI_new( + int format, + int channels, + int sampleRate, + int samples, + ) { + return _mdkAudioFrameAPI_new( + format, + channels, + sampleRate, + samples, + ); + } + + late final _mdkAudioFrameAPI_newPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int32, ffi.Int, ffi.Int, ffi.Int)>>('mdkAudioFrameAPI_new'); + late final _mdkAudioFrameAPI_new = _mdkAudioFrameAPI_newPtr + .asFunction Function(int, int, int, int)>(); + + void mdkAudioFrameAPI_delete( + ffi.Pointer> arg0, + ) { + return _mdkAudioFrameAPI_delete( + arg0, + ); + } + + late final _mdkAudioFrameAPI_deletePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer>)>>( + 'mdkAudioFrameAPI_delete'); + late final _mdkAudioFrameAPI_delete = _mdkAudioFrameAPI_deletePtr + .asFunction>)>(); + + ffi.Pointer mdkAudioFrameAPI_ref( + ffi.Pointer p, + ) { + return _mdkAudioFrameAPI_ref( + p, + ); + } + + late final _mdkAudioFrameAPI_refPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('mdkAudioFrameAPI_ref'); + late final _mdkAudioFrameAPI_ref = _mdkAudioFrameAPI_refPtr.asFunction< + ffi.Pointer Function(ffi.Pointer)>(); + + void mdkAudioFrameAPI_unref( + ffi.Pointer> pp, + ) { + return _mdkAudioFrameAPI_unref( + pp, + ); + } + + late final _mdkAudioFrameAPI_unrefPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer>)>>( + 'mdkAudioFrameAPI_unref'); + late final _mdkAudioFrameAPI_unref = _mdkAudioFrameAPI_unrefPtr + .asFunction>)>(); + ffi.Pointer mdkVideoFrameAPI_new( int width, int height, @@ -429,6 +495,36 @@ class NativeLibrary { late final _mdkVideoFrameAPI_delete = _mdkVideoFrameAPI_deletePtr .asFunction>)>(); + ffi.Pointer mdkVideoFrameAPI_ref( + ffi.Pointer p, + ) { + return _mdkVideoFrameAPI_ref( + p, + ); + } + + late final _mdkVideoFrameAPI_refPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('mdkVideoFrameAPI_ref'); + late final _mdkVideoFrameAPI_ref = _mdkVideoFrameAPI_refPtr.asFunction< + ffi.Pointer Function(ffi.Pointer)>(); + + void mdkVideoFrameAPI_unref( + ffi.Pointer> pp, + ) { + return _mdkVideoFrameAPI_unref( + pp, + ); + } + + late final _mdkVideoFrameAPI_unrefPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer>)>>( + 'mdkVideoFrameAPI_unref'); + late final _mdkVideoFrameAPI_unref = _mdkVideoFrameAPI_unrefPtr + .asFunction>)>(); + void mdkVideoBufferPoolFree( ffi.Pointer> pool, ) { @@ -888,6 +984,103 @@ final class mdkMediaInfo extends ffi.Struct { external int nb_programs; } +final class mdkAudioFrame extends ffi.Opaque {} + +abstract class MDK_SampleFormat { + static const int MDK_SampleFormat_Unknown = 0; + static const int MDK_SampleFormat_U8 = 1; + static const int MDK_SampleFormat_U8P = 2; + static const int MDK_SampleFormat_S16 = 3; + static const int MDK_SampleFormat_S16P = 4; + static const int MDK_SampleFormat_S32 = 5; + static const int MDK_SampleFormat_S32P = 6; + static const int MDK_SampleFormat_F32 = 7; + static const int MDK_SampleFormat_F32P = 8; + static const int MDK_SampleFormat_F64 = 9; + static const int MDK_SampleFormat_F64P = 10; +} + +final class mdkAudioFrameAPI extends ffi.Struct { + external ffi.Pointer object; + + external ffi + .Pointer)>> + planeCount; + + external ffi.Pointer< + ffi.NativeFunction)>> + sampleFormat; + + external ffi.Pointer< + ffi.NativeFunction)>> + channelMask; + + external ffi + .Pointer)>> + channels; + + external ffi + .Pointer)>> + sampleRate; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Size, + ffi.Int, + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer>)>>)>> addBuffer; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, + ffi.Pointer>, ffi.Int)>> setBuffers; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Int)>> bufferData; + + external ffi + .Pointer)>> + bytesPerPlane; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Int)>> + setSamplesPerChannel; + + external ffi + .Pointer)>> + samplesPerChannel; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Double)>> + setTimestamp; + + external ffi.Pointer< + ffi.NativeFunction)>> + timestamp; + + external ffi.Pointer< + ffi.NativeFunction)>> + duration; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Int32, ffi.Int, ffi.Int)>> to; + + @ffi.Array.multi([8]) + external ffi.Array> reserved; +} + final class mdkDX11Resource extends ffi.Struct { @ffi.Int() external int size; @@ -896,10 +1089,18 @@ final class mdkDX11Resource extends ffi.Struct { @ffi.Int() external int subResource; + + @ffi.Array.multi([4]) + external ffi.Array> plane; + + @ffi.Int() + external int planeCount; } final class ID3D11DeviceChild extends ffi.Opaque {} +final class ID3D11Texture2D extends ffi.Opaque {} + final class mdkDX9Resource extends ffi.Struct { @ffi.Int() external int size; @@ -909,6 +1110,28 @@ final class mdkDX9Resource extends ffi.Struct { final class IDirect3DSurface9 extends ffi.Opaque {} +final class mdkVAAPIResource extends ffi.Struct { + @ffi.Int() + external int size; + + @VASurfaceID() + external int surface; + + external VADisplay display; + + external ffi.Pointer x11Display; + + external ffi.Pointer opaque; + + external ffi.Pointer< + ffi.NativeFunction opaque)>> + unref; +} + +typedef VASurfaceID = ffi.UnsignedInt; +typedef DartVASurfaceID = int; +typedef VADisplay = ffi.Pointer; + final class mdkVideoBufferPool extends ffi.Opaque {} final class mdkVideoFrame extends ffi.Opaque {} @@ -944,6 +1167,36 @@ abstract class MDK_PixelFormat { static const int MDK_PixelFormat_BGRAF32 = 25; } +final class mdkCUDAResource extends ffi.Struct { + @ffi.Int() + external int size; + + @ffi.Array.multi([4]) + external ffi.Array> ptr; + + @ffi.Int() + external int width; + + @ffi.Int() + external int height; + + @ffi.Array.multi([4]) + external ffi.Array stride; + + @ffi.Int32() + external int format; + + external ffi.Pointer context; + + external ffi.Pointer stream; + + external ffi.Pointer opaque; + + external ffi.Pointer< + ffi.NativeFunction opaque)>> + unref; +} + final class mdkVideoFrameAPI extends ffi.Struct { external ffi.Pointer object; @@ -1039,21 +1292,55 @@ final class mdkVideoFrameAPI extends ffi.Struct { ffi.Int, ffi.Int)>> fromDX9; - external ffi.Pointer> fromDX12; + external ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer>, + ffi.Pointer, + ffi.Int, + ffi.Int)>> fromVAAPI; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer>, + ffi.Pointer, + ffi.Int, + ffi.Int)>> fromCUDA; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>> getDX11; + + external ffi + .Pointer)>> + rotation; - external ffi.Pointer> fromMetal; + external ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>> metadata; external ffi.Pointer> fromVk; external ffi.Pointer> fromGL; + external ffi.Pointer> fromDX12; + external ffi.Pointer< ffi.NativeFunction)>> toHost; - @ffi.Array.multi([12]) + @ffi.Array.multi([8]) external ffi.Array> reserved; } +final class ID3D11Device extends ffi.Opaque {} + abstract class MDK_RenderAPI { static const int MDK_RenderAPI_Invalid = 0; static const int MDK_RenderAPI_OpenGL = 1; @@ -1065,8 +1352,6 @@ abstract class MDK_RenderAPI { final class mdkRenderAPI extends ffi.Opaque {} -final class mdkAudioFrame extends ffi.Opaque {} - final class mdkPlayer extends ffi.Opaque {} abstract class MDK_SurfaceType { @@ -1108,6 +1393,9 @@ final class mdkRenderCallback extends ffi.Struct { } final class mdkVideoCallback extends ffi.Struct { + /// ! + /// \brief cb + /// \return pending number of frames. For most filters, 1 input frame generates 1 output frame, then return 0. external ffi.Pointer< ffi.NativeFunction< ffi.Int Function(ffi.Pointer> pFrame, @@ -1116,6 +1404,15 @@ final class mdkVideoCallback extends ffi.Struct { external ffi.Pointer opaque; } +final class mdkAudioCallback extends ffi.Struct { + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer> pFrame, + ffi.Int track, ffi.Pointer opaque)>> cb; + + external ffi.Pointer opaque; +} + final class mdkSeekCallback extends ffi.Struct { external ffi.Pointer< ffi.NativeFunction< @@ -1202,6 +1499,15 @@ final class mdkSubtitleCallback extends ffi.Struct { ffi.Pointer text, ffi.Pointer opaque)>> cb; external ffi.Pointer opaque; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Double start, + ffi.Double end, + ffi.Pointer> texts, + ffi.Int textCount, + ffi.Pointer opaque)>> cb2; } final class mdkPlayerAPI extends ffi.Struct { @@ -1451,9 +1757,9 @@ final class mdkPlayerAPI extends ffi.Struct { ffi.NativeFunction< ffi.Void Function(ffi.Pointer, mdkVideoCallback)>> onVideo; - external ffi - .Pointer)>> - onAudio; + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, mdkAudioCallback)>> onAudio; external ffi.Pointer< ffi.NativeFunction< @@ -1768,6 +2074,22 @@ final class mdkPlayerAPI extends ffi.Struct { ffi.NativeFunction< ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Int, mdkSubtitleCallback)>> subtitleText; + + /// ! + /// \brief setAudioMix + /// Audio channel map or remix. + /// \param mat map or mix coefficients matrix. matrix[i + cols * o] is the weight of input channel i in output channel o. + /// \param rows mat rows, output channel count + /// \param cols mat colums, input channel count to use + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Int, ffi.Int)>> setAudioMix; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, mdkSubtitleCallback, + ffi.Bool, ffi.Pointer)>> onSubtitleText; } final class SwitchBitrateCallback extends ffi.Struct { @@ -1785,6 +2107,7 @@ final class SwitchBitrateCallback extends ffi.Struct { /// Null callback(.opaque == null) + non-null token: can remove the callback of given token. /// Null callback(.opaque == null) + null token: clear all callbacks. typedef MDK_CallbackToken = ffi.Uint64; +typedef DartMDK_CallbackToken = int; /// ! /// \brief size @@ -1800,8 +2123,8 @@ final class UnnamedUnion2 extends ffi.Union { const int MDK_MAJOR = 0; -const int MDK_MINOR = 31; +const int MDK_MINOR = 35; const int MDK_MICRO = 0; -const int MDK_VERSION = 7936; +const int MDK_VERSION = 8960; diff --git a/pubspec.yaml b/pubspec.yaml index db22501..7fb96e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,7 +29,7 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: any - ffigen: ^9.0.0 + ffigen: '>=9.0.0 <13.0.0' lints: any # For information on the generic Dart part of this file, see the From e7638c1f2ce5d50c27a70136cf9982a600bb0752 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 19 Nov 2025 11:45:05 +0800 Subject: [PATCH 073/109] textureId => playerId warnings in new versions --- lib/src/video_player_dummy.dart | 50 +++++------ lib/src/video_player_mdk.dart | 154 ++++++++++++++++---------------- 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/lib/src/video_player_dummy.dart b/lib/src/video_player_dummy.dart index da03a8a..68dad0d 100644 --- a/lib/src/video_player_dummy.dart +++ b/lib/src/video_player_dummy.dart @@ -7,66 +7,66 @@ class MdkVideoPlayerPlatform { static void registerVideoPlayerPlatformsWith({dynamic options}) {} // for FVPController - bool isLive(int textureId) { + bool isLive(int playerId) { return false; } - MediaInfo? getMediaInfo(int textureId) { + MediaInfo? getMediaInfo(int playerId) { return null; } - void setProperty(int textureId, String name, String value) {} + void setProperty(int playerId, String name, String value) {} - void setAudioDecoders(int textureId, List value) {} + void setAudioDecoders(int playerId, List value) {} - void setVideoDecoders(int textureId, List value) {} + void setVideoDecoders(int playerId, List value) {} - void record(int textureId, {String? to, String? format}) {} + void record(int playerId, {String? to, String? format}) {} - Future snapshot(int textureId, {int? width, int? height}) async { + Future snapshot(int playerId, {int? width, int? height}) async { return null; } - void setRange(int textureId, {required int from, int to = -1}) {} + void setRange(int playerId, {required int from, int to = -1}) {} - void setBufferRange(int textureId, + void setBufferRange(int playerId, {int min = -1, int max = -1, bool drop = false}) {} - Future fastSeekTo(int textureId, Duration position) async {} + Future fastSeekTo(int playerId, Duration position) async {} - Future step(int textureId, int frames) async {} + Future step(int playerId, int frames) async {} - void setBrightness(int textureId, double value) {} + void setBrightness(int playerId, double value) {} - void setContrast(int textureId, double value) {} + void setContrast(int playerId, double value) {} - void setHue(int textureId, double value) {} + void setHue(int playerId, double value) {} - void setSaturation(int textureId, double value) {} + void setSaturation(int playerId, double value) {} - void setProgram(int textureId, int programId) {} + void setProgram(int playerId, int programId) {} - void setAudioTracks(int textureId, List value) {} + void setAudioTracks(int playerId, List value) {} - List? getActiveAudioTracks(int textureId) { + List? getActiveAudioTracks(int playerId) { return null; } - void setVideoTracks(int textureId, List value) {} + void setVideoTracks(int playerId, List value) {} - List? getActiveVideoTracks(int textureId) { + List? getActiveVideoTracks(int playerId) { return null; } - void setSubtitleTracks(int textureId, List value) {} + void setSubtitleTracks(int playerId, List value) {} - List? getActiveSubtitleTracks(int textureId) { + List? getActiveSubtitleTracks(int playerId) { return null; } - void setExternalAudio(int textureId, String uri) {} + void setExternalAudio(int playerId, String uri) {} - void setExternalVideo(int textureId, String uri) {} + void setExternalVideo(int playerId, String uri) {} - void setExternalSubtitle(int textureId, String uri) {} + void setExternalSubtitle(int playerId, String uri) {} } diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 3a0a7fc..e6bf871 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -39,8 +39,8 @@ class MdkVideoPlayer extends mdk.Player { if (!oldValue.test(mdk.MediaStatus.loaded) && newValue.test(mdk.MediaStatus.loaded)) { // initialized event must be sent only once. keep_open=1 is another solution - //if ((textureId.value ?? -1) >= 0) { - // return true; // prepared callback is invoked before MediaStatus.loaded, so textureId can be a valid value here + //if ((playerId.value ?? -1) >= 0) { + // return true; // prepared callback is invoked before MediaStatus.loaded, so playerId can be a valid value here //} if (_initialized) { _log.fine('$hashCode player$nativeHandle already initialized'); @@ -246,8 +246,8 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { Future init() async {} @override - Future dispose(int textureId) async { - _players.remove(textureId)?.dispose(); + Future dispose(int playerId) async { + _players.remove(playerId)?.dispose(); } @override @@ -324,47 +324,47 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { //player.dispose(); return -hashCode; } - _log.fine('$hashCode player${player.nativeHandle} textureId=$tex'); + _log.fine('$hashCode player${player.nativeHandle} textureId/playerId=$tex'); _players[tex] = player; return tex; } @override - Future setLooping(int textureId, bool looping) async { - final player = _players[textureId]; + Future setLooping(int playerId, bool looping) async { + final player = _players[playerId]; if (player != null) { player.loop = looping ? -1 : 0; } } @override - Future play(int textureId) async { - _players[textureId]?.state = mdk.PlaybackState.playing; + Future play(int playerId) async { + _players[playerId]?.state = mdk.PlaybackState.playing; } @override - Future pause(int textureId) async { - _players[textureId]?.state = mdk.PlaybackState.paused; + Future pause(int playerId) async { + _players[playerId]?.state = mdk.PlaybackState.paused; } @override - Future setVolume(int textureId, double volume) async { - _players[textureId]?.volume = volume; + Future setVolume(int playerId, double volume) async { + _players[playerId]?.volume = volume; } @override - Future setPlaybackSpeed(int textureId, double speed) async { - _players[textureId]?.playbackRate = speed; + Future setPlaybackSpeed(int playerId, double speed) async { + _players[playerId]?.playbackRate = speed; } @override - Future seekTo(int textureId, Duration position) async { - return _seekToWithFlags(textureId, position, mdk.SeekFlag(_seekFlags)); + Future seekTo(int playerId, Duration position) async { + return _seekToWithFlags(playerId, position, mdk.SeekFlag(_seekFlags)); } @override - Future getPosition(int textureId) async { - final player = _players[textureId]; + Future getPosition(int playerId) async { + final player = _players[playerId]; if (player == null) { return Duration.zero; } @@ -382,17 +382,17 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { } @override - Stream videoEventsFor(int textureId) { - final player = _players[textureId]; + Stream videoEventsFor(int playerId) { + final player = _players[playerId]; if (player != null) { return player.streamCtl.stream; } - throw Exception('No Stream for textureId: $textureId.'); + throw Exception('No Stream for textureId/playerId: $playerId.'); } @override - Widget buildView(int textureId) { - return Texture(textureId: textureId); + Widget buildView(int playerId) { + return Texture(textureId: playerId); } @override @@ -401,55 +401,55 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { } // more apis for fvp controller - bool isLive(int textureId) { - return _players[textureId]?.isLive ?? false; + bool isLive(int playerId) { + return _players[playerId]?.isLive ?? false; } - MediaInfo? getMediaInfo(int textureId) { - return _players[textureId]?.mediaInfo; + MediaInfo? getMediaInfo(int playerId) { + return _players[playerId]?.mediaInfo; } - void setProperty(int textureId, String name, String value) { - _players[textureId]?.setProperty(name, value); + void setProperty(int playerId, String name, String value) { + _players[playerId]?.setProperty(name, value); } - void setAudioDecoders(int textureId, List value) { - _players[textureId]?.audioDecoders = value; + void setAudioDecoders(int playerId, List value) { + _players[playerId]?.audioDecoders = value; } - void setVideoDecoders(int textureId, List value) { - _players[textureId]?.videoDecoders = value; + void setVideoDecoders(int playerId, List value) { + _players[playerId]?.videoDecoders = value; } - void record(int textureId, {String? to, String? format}) { - _players[textureId]?.record(to: to, format: format); + void record(int playerId, {String? to, String? format}) { + _players[playerId]?.record(to: to, format: format); } - Future snapshot(int textureId, {int? width, int? height}) async { + Future snapshot(int playerId, {int? width, int? height}) async { Uint8List? data; - final player = _players[textureId]; + final player = _players[playerId]; if (player == null) { return data; } - return _players[textureId]?.snapshot(width: width, height: height); + return _players[playerId]?.snapshot(width: width, height: height); } - void setRange(int textureId, {required int from, int to = -1}) { - _players[textureId]?.setRange(from: from, to: to); + void setRange(int playerId, {required int from, int to = -1}) { + _players[playerId]?.setRange(from: from, to: to); } - void setBufferRange(int textureId, + void setBufferRange(int playerId, {int min = -1, int max = -1, bool drop = false}) { - _players[textureId]?.setBufferRange(min: min, max: max, drop: drop); + _players[playerId]?.setBufferRange(min: min, max: max, drop: drop); } - Future fastSeekTo(int textureId, Duration position) async { + Future fastSeekTo(int playerId, Duration position) async { return _seekToWithFlags( - textureId, position, mdk.SeekFlag(_seekFlags | mdk.SeekFlag.keyFrame)); + playerId, position, mdk.SeekFlag(_seekFlags | mdk.SeekFlag.keyFrame)); } - Future step(int textureId, int frames) async { - final player = _players[textureId]; + Future step(int playerId, int frames) async { + final player = _players[playerId]; if (player == null) { return; } @@ -458,67 +458,67 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { flags: const mdk.SeekFlag(mdk.SeekFlag.frame | mdk.SeekFlag.fromNow)); } - void setBrightness(int textureId, double value) { - _players[textureId]?.setVideoEffect(mdk.VideoEffect.brightness, [value]); + void setBrightness(int playerId, double value) { + _players[playerId]?.setVideoEffect(mdk.VideoEffect.brightness, [value]); } - void setContrast(int textureId, double value) { - _players[textureId]?.setVideoEffect(mdk.VideoEffect.contrast, [value]); + void setContrast(int playerId, double value) { + _players[playerId]?.setVideoEffect(mdk.VideoEffect.contrast, [value]); } - void setHue(int textureId, double value) { - _players[textureId]?.setVideoEffect(mdk.VideoEffect.hue, [value]); + void setHue(int playerId, double value) { + _players[playerId]?.setVideoEffect(mdk.VideoEffect.hue, [value]); } - void setSaturation(int textureId, double value) { - _players[textureId]?.setVideoEffect(mdk.VideoEffect.saturation, [value]); + void setSaturation(int playerId, double value) { + _players[playerId]?.setVideoEffect(mdk.VideoEffect.saturation, [value]); } - void setProgram(int textureId, int programId) { - _players[textureId]?.setActiveTracks(mdk.MediaType.unknown, [programId]); + void setProgram(int playerId, int programId) { + _players[playerId]?.setActiveTracks(mdk.MediaType.unknown, [programId]); } // embedded tracks, can be main data source from create(), or external media source via setExternalAudio - void setAudioTracks(int textureId, List value) { - _players[textureId]?.activeAudioTracks = value; + void setAudioTracks(int playerId, List value) { + _players[playerId]?.activeAudioTracks = value; } - List? getActiveAudioTracks(int textureId) { - return _players[textureId]?.activeAudioTracks; + List? getActiveAudioTracks(int playerId) { + return _players[playerId]?.activeAudioTracks; } - void setVideoTracks(int textureId, List value) { - _players[textureId]?.activeVideoTracks = value; + void setVideoTracks(int playerId, List value) { + _players[playerId]?.activeVideoTracks = value; } - List? getActiveVideoTracks(int textureId) { - return _players[textureId]?.activeVideoTracks; + List? getActiveVideoTracks(int playerId) { + return _players[playerId]?.activeVideoTracks; } - void setSubtitleTracks(int textureId, List value) { - _players[textureId]?.activeSubtitleTracks = value; + void setSubtitleTracks(int playerId, List value) { + _players[playerId]?.activeSubtitleTracks = value; } - List? getActiveSubtitleTracks(int textureId) { - return _players[textureId]?.activeSubtitleTracks; + List? getActiveSubtitleTracks(int playerId) { + return _players[playerId]?.activeSubtitleTracks; } // external track. can select external tracks via setAudioTracks() - void setExternalAudio(int textureId, String uri) { - _players[textureId]?.setMedia(uri, mdk.MediaType.audio); + void setExternalAudio(int playerId, String uri) { + _players[playerId]?.setMedia(uri, mdk.MediaType.audio); } - void setExternalVideo(int textureId, String uri) { - _players[textureId]?.setMedia(uri, mdk.MediaType.video); + void setExternalVideo(int playerId, String uri) { + _players[playerId]?.setMedia(uri, mdk.MediaType.video); } - void setExternalSubtitle(int textureId, String uri) { - _players[textureId]?.setMedia(uri, mdk.MediaType.subtitle); + void setExternalSubtitle(int playerId, String uri) { + _players[playerId]?.setMedia(uri, mdk.MediaType.subtitle); } Future _seekToWithFlags( - int textureId, Duration position, mdk.SeekFlag flags) async { - final player = _players[textureId]; + int playerId, Duration position, mdk.SeekFlag flags) async { + final player = _players[playerId]; if (player == null) { return; } From 8c79c5010c966c77ebac66b0d5c5fbd7fde58661 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 4 Dec 2025 11:21:54 +0800 Subject: [PATCH 074/109] log plugin version --- cmake/deps.cmake | 21 +++++++++++++++++++++ darwin/fvp.podspec | 4 ++++ lib/src/callbacks.cpp | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index e5ddd91..c44a017 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -1,4 +1,25 @@ + +function(fvp_version) + set(PUBSPEC_FILE "${CMAKE_CURRENT_LIST_DIR}/../pubspec.yaml") + if(NOT EXISTS ${PUBSPEC_FILE}) + message(FATAL_ERROR "pubspec.yaml not found: ${PUBSPEC_FILE}") + endif() + file(READ ${PUBSPEC_FILE} PUBSPEC_CONTENTS) + string(REGEX MATCH "version:[ \t]*([0-9]+\\.[0-9]+\\.[0-9]+|[0-9]+\\.[0-9]+|[^ \t\n\r]+)" MATCHED_LINE "${PUBSPEC_CONTENTS}") + + if(MATCHED_LINE) + string(REGEX REPLACE "version:[ \t]*" "" FVP_VERSION "${MATCHED_LINE}") + message(STATUS "Found fvp version: ${FVP_VERSION}") + set(VERSION_HEADER_FILE "${CMAKE_CURRENT_LIST_DIR}/../lib/src/version.h") + file(WRITE ${VERSION_HEADER_FILE} "#pragma once\n#define FVP_VERSION \"${FVP_VERSION}\"\n") + else() + message(WARNING "No version line found in file") + endif() +endfunction(fvp_version) + + macro(fvp_setup_deps) + fvp_version() if(WIN32) set(MDK_SDK_PKG mdk-sdk-windows-desktop-vs2022.7z) if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "[xX]64") # msvc diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index a706c0a..4c54cf3 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -31,4 +31,8 @@ Flutter video player plugin. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.resource_bundles = {'fvp_privacy' => ['PrivacyInfo.xcprivacy']} # s.swift_version = '5.0' + s.prepare_command = <<-CMD + FVP_VERSION=`grep 'version: ' ../pubspec.yaml | head -1 | awk '{print $2}'` + echo '#pragma once\n#define FVP_VERSION "'$FVP_VERSION'"' > ../lib/src/version.h + CMD end diff --git a/lib/src/callbacks.cpp b/lib/src/callbacks.cpp index 61242bd..5b38e48 100644 --- a/lib/src/callbacks.cpp +++ b/lib/src/callbacks.cpp @@ -11,6 +11,9 @@ #include #include "dart_api_types.h" #include "callbacks.h" +#if __has_include("version.h") +#include "version.h" +#endif using namespace std; @@ -79,6 +82,9 @@ FVP_EXPORT void MdkCallbacksRegisterPort(int64_t handle, void* post_c_object, in return; } }); +#ifdef FVP_VERSION + clog << "fvp plugin version: " FVP_VERSION << endl; +#endif return; } auto player = make_shared(handle); From f310ab0ae276649b9be45badd844fcae9fe490f1 Mon Sep 17 00:00:00 2001 From: WangBin Date: Wed, 24 Dec 2025 10:07:26 +0800 Subject: [PATCH 075/109] Create dependabot.yml --- .github/dependabot.yml | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..197a34a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,72 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch"] + + # Android example apps. + - package-ecosystem: "gradle" + directories: + - /example/android/app + commit-message: + prefix: "[dependabot]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + labels: + - "autosubmit" + open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + + # Android packages (excluding example apps). + - package-ecosystem: "gradle" + directories: + - /android + commit-message: + prefix: "[dependabot]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + labels: + - "autosubmit" + groups: + test-dependencies: + patterns: + - "androidx.test:*" + - "io.mockk:*" + - "junit:junit" + - "org.mockito:*" + - "org.robolectric:*" + kotlin-gradle-plugin: + patterns: + - "org.jetbrains.kotlin:kotlin-gradle-plugin" + gradle-plugin: + patterns: + - "com.android.tools.build:gradle" + androidx: + patterns: + - "androidx.annotation:annotation" + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-patch"] + - dependency-name: "io.mockk:mockk:*" + update-types: ["version-update:semver-patch"] From 3d61d80dbe35858e39c009140308f1990e66f821 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Dec 2025 02:08:06 +0000 Subject: [PATCH 076/109] build(deps): bump actions/upload-artifact from 4 to 6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a60c5e0..b23d3d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: mv build/macos/Build/Products/Release/fvp_example.app . cmake -E tar cvf fvp_example_macos.7z --format=7zip fvp_example.app - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-macOS path: example/fvp_example_macos.7z @@ -68,7 +68,7 @@ jobs: mv build/ios/iphoneos/Runner.app . cmake -E tar cvf fvp_example_ios.7z --format=7zip Runner.app - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-iOS path: example/fvp_example_ios.7z @@ -102,7 +102,7 @@ jobs: - run: mv build/windows/x64/runner/Release . - run: cmake -E tar cvf fvp_example_windows-flutter-${{ matrix.version }}-${{ matrix.channel }}.7z --format=7zip Release - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-windows-flutter-${{ matrix.version }}-${{ matrix.channel }} path: | @@ -141,7 +141,7 @@ jobs: - run: mv build/linux/x64/release/bundle . - run: cmake -E tar Jcvf fvp_example_linux-${{ matrix.channel }}-${{ matrix.version }}.tar.xz bundle - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-linux-${{ matrix.channel }}-${{ matrix.version }} path: example/fvp_example_linux*.tar.xz @@ -177,7 +177,7 @@ jobs: - run: mv build/linux/arm64/release/bundle . - run: cmake -E tar Jcvf fvp_example_linux-arm64-${{ matrix.channel }}-${{ matrix.version }}.tar.xz bundle - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-linux-arm64-${{ matrix.channel }}-${{ matrix.version }} path: example/fvp_example_linux*.tar.xz @@ -204,7 +204,7 @@ jobs: - run: mv build/linux/arm64/release/bundle . - run: cmake -E tar Jcvf fvp_example_linux-arm64-snap.tar.xz bundle - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-linux-arm64-snap path: example/fvp_example_linux*.tar.xz @@ -264,7 +264,7 @@ jobs: - run: flutter build apk --verbose - run: mv ./build/app/outputs/apk/release/app-release.apk fvp_example_android.apk - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fvp-example-android-${{ matrix.host }}-${{ matrix.version }} path: example/fvp_example_android.apk From 263aa75cad5070e819838759bdb4102145729536 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Dec 2025 02:08:12 +0000 Subject: [PATCH 077/109] build(deps): bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 18 +++++++++--------- .github/workflows/pub.yml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a60c5e0..69b9342 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -88,7 +88,7 @@ jobs: - version: '3.19.x' channel: 'beta' steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -123,7 +123,7 @@ jobs: - version: '3.10.x' channel: 'beta' steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -159,7 +159,7 @@ jobs: version: ['any'] channel: ['master'] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - run: | @@ -190,7 +190,7 @@ jobs: run: working-directory: example steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - name: Install dependencies @@ -219,7 +219,7 @@ jobs: matrix: target: [linux] #, apk] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: actions/setup-java@v5 @@ -247,7 +247,7 @@ jobs: host: [ubuntu] version: ['3.22.x', 'any'] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 @@ -280,7 +280,7 @@ jobs: matrix: wasm: ['--wasm', ''] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 diff --git a/.github/workflows/pub.yml b/.github/workflows/pub.yml index a6eefc5..d732cf1 100644 --- a/.github/workflows/pub.yml +++ b/.github/workflows/pub.yml @@ -14,7 +14,7 @@ jobs: id-token: write # Required for authentication using OIDC runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: 'recursive' - uses: subosito/flutter-action@v2 From e26a253322f73617600cf0c122e1815e00a7076b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Dec 2025 02:12:07 +0000 Subject: [PATCH 078/109] [dependabot]: Bump com.android.tools.build:gradle Bumps the gradle-plugin group with 1 update in the /android directory: com.android.tools.build:gradle. Updates `com.android.tools.build:gradle` from 8.9.1 to 8.13.2 --- updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-version: 8.13.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-plugin ... Signed-off-by: dependabot[bot] --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 064f620..b63aae5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath("com.android.tools.build:gradle:8.9.1") + classpath("com.android.tools.build:gradle:8.13.2") } } From 96eaf85537a85b1013556a2cebed3877eeb1a788 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 25 Dec 2025 14:22:10 +0800 Subject: [PATCH 079/109] update dep download url --- cmake/deps.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index c44a017..f392d62 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -21,9 +21,9 @@ endfunction(fvp_version) macro(fvp_setup_deps) fvp_version() if(WIN32) - set(MDK_SDK_PKG mdk-sdk-windows-desktop-vs2022.7z) + set(MDK_SDK_PKG mdk-sdk-windows.7z) if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "[xX]64") # msvc - set(MDK_SDK_PKG mdk-sdk-windows-desktop-vs2022-x64.7z) + set(MDK_SDK_PKG mdk-sdk-windows-x64.7z) endif() elseif(ANDROID) set(MDK_SDK_PKG mdk-sdk-android.7z) From 08af71d9fbeada091047a1650b1a0b9a9d6265e9 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 31 Dec 2025 11:48:36 +0800 Subject: [PATCH 080/109] v0.35.2 --- CHANGELOG.md | 4 ++++ darwin/fvp.podspec | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d58e30..813b726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.35.2 + +* log plugin version + ## 0.35.1 * fix linux arch for cmake 4.0+ diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 4c54cf3..7bec450 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.35.1' + s.version = '0.35.2' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.35.0' + s.dependency 'mdk', '~> 0.35.1' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 7fb96e8..ad0cbf3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.35.1 +version: 0.35.2 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b2293f7..f304849 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.35.1 +project(${PROJECT_NAME} VERSION 0.35.2 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 6a2d58861b3d6112b993c57aceb7e38b5e75a4db Mon Sep 17 00:00:00 2001 From: wangbin Date: Mon, 26 Jan 2026 14:34:23 +0800 Subject: [PATCH 081/109] use StreamController for onXXX callbacks, more dart idiomatic --- lib/src/player.dart | 86 +++++++++++++---------------------- lib/src/video_player_mdk.dart | 41 ++++++----------- 2 files changed, 46 insertions(+), 81 deletions(-) diff --git a/lib/src/player.dart b/lib/src/player.dart index dc25371..3b89ccb 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2025 Wang Bin. All rights reserved. +// Copyright 2022-2026 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; @@ -38,8 +38,8 @@ class Player { final category = message[2] as String; final detail = message[3] as String; final ev = MediaEvent(error, category, detail); - for (final cb in _eventCb) { - cb(ev); + if (_eventCb.hasListener) { + _eventCb.add(ev); } } case 1: @@ -47,8 +47,8 @@ class Player { // state final oldValue = message[1] as int; final newValue = message[2] as int; - for (final cb in _stateCb) { - cb(PlaybackState.from(oldValue), PlaybackState.from(newValue)); + if (_stateCb.hasListener) { + _stateCb.add((oldValue: PlaybackState.from(oldValue), newValue: PlaybackState.from(newValue))); } Libfvp.replyType(nativeHandle, type, nullptr); } @@ -58,8 +58,8 @@ class Player { final oldValue = message[1] as int; final newValue = message[2] as int; bool ret = true; - for (var cb in _statusCb) { - ret = cb(MediaStatus(oldValue), MediaStatus(newValue)) && ret; + if (_statusCb.hasListener) { + _statusCb.add((oldValue: MediaStatus(oldValue), newValue: MediaStatus(newValue))); } rep.ref.mediaStatus.ret = ret; Libfvp.replyType(nativeHandle, type, rep.cast()); @@ -121,10 +121,13 @@ class Player { Libfvp.registerPort(nativeHandle, NativeApi.postCObject.cast(), _receivePort.sendPort.nativePort); - onStateChanged((oldValue, newValue) { - _state = newValue; + _stateCb.stream.listen((event) { + _state = event.newValue; }); - onMediaStatus((oldValue, newValue) { + Libfvp.registerType(nativeHandle, 1, false); + _statusCb.stream.listen((event) { + final oldValue = event.oldValue; + final newValue = event.newValue; if (!oldValue.test(MediaStatus.loaded) && newValue.test(MediaStatus.loaded)) { _setVideoSize(); @@ -147,9 +150,9 @@ class Player { _videoSize.complete(null); } } - return true; }); - onEvent((e) { + Libfvp.registerType(nativeHandle, 2, false); + _eventCb.stream.listen((e) { if (_videoSize.isCompleted) { return; } @@ -157,6 +160,7 @@ class Player { _setVideoSize(); } }); + Libfvp.registerType(nativeHandle, 0, false); } /// Release resources @@ -169,9 +173,12 @@ class Player { await updateTexture(width: -1); state = PlaybackState.stopped; Libfvp.unregisterPort(nativeHandle); - onEvent(null); - onStateChanged(null); - onMediaStatus(null); + _eventCb.close(); + Libfvp.unregisterType(nativeHandle, 0); + _stateCb.close(); + Libfvp.unregisterType(nativeHandle, 1); + _statusCb.close(); + Libfvp.unregisterType(nativeHandle, 2); _receivePort.close(); @@ -674,48 +681,18 @@ class Player { } // callbacks - /// Set [MediaEvent] callback. + /// Get a [MediaEvent] stream. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-oneventstdfunctionboolconst-mediaevent-cb-callbacktoken-token--nullptr - void onEvent(void Function(MediaEvent)? callback) { - if (callback == null) { - _eventCb.clear(); - Libfvp.unregisterType(nativeHandle, 0); - } else { - _eventCb.add(callback); - Libfvp.registerType(nativeHandle, 0, false); - } - } + Stream get onEvent => _eventCb.stream; - /// Set a [PlaybackState] change callback. + /// Get a [PlaybackState] change stream. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-onstatechangedstdfunctionvoidstate-cb -// reply: true to let native code wait for dart callback result - void onStateChanged( - void Function(PlaybackState oldValue, PlaybackState newValue)? callback, - {bool reply = false}) { - if (callback == null) { - _stateCb.clear(); - Libfvp.unregisterType(nativeHandle, 1); - } else { - _stateCb.add(callback); - Libfvp.registerType(nativeHandle, 1, reply); - } - } + Stream<({PlaybackState oldValue, PlaybackState newValue})> get onStateChanged => _stateCb.stream; - /// Add a [MediaStatus] callback or remove all callbacks. + /// Get a [MediaStatus] change stream. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-onmediastatusstdfunctionboolmediastatus-oldvalue-mediastatus-newvalue-cb-callbacktoken-token--nullptr // reply: true to let native code wait for dart callback result, may result in dead lock because when native waiting main isolate reply, main isolate may execute another task(e.g. frequent seekTo) which also acquire the same lock in native -// only the last callback reply parameter works - void onMediaStatus( - bool Function(MediaStatus oldValue, MediaStatus newValue)? callback, - {bool reply = false}) { - if (callback == null) { - _statusCb.clear(); - Libfvp.unregisterType(nativeHandle, 2); - } else { - _statusCb.add(callback); - Libfvp.registerType(nativeHandle, 2, reply); - } - } + Stream<({MediaStatus oldValue, MediaStatus newValue})> get onMediaStatus => _statusCb.stream; void onSubtitleText( void Function(double start, double end, List text)? callback) { @@ -778,10 +755,9 @@ class Player { Completer? _seeked; final _receivePort = ReceivePort(); - final _eventCb = []; - final _stateCb = []; - final _statusCb = - []; + final _eventCb = StreamController.broadcast(); + final _stateCb = StreamController<({PlaybackState oldValue, PlaybackState newValue})>.broadcast(); + final _statusCb = StreamController<({MediaStatus oldValue, MediaStatus newValue})>.broadcast(); Function(double start, double end, List text)? _subtitleCb; Future Function()? _prepareCb; diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index e6bf871..8e97a77 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2025 Wang Bin. All rights reserved. +// Copyright 2022-2026 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -24,16 +24,15 @@ class MdkVideoPlayer extends mdk.Player { @override void dispose() { - onMediaStatus(null); - onEvent(null); - onStateChanged(null); streamCtl.close(); _initialized = false; super.dispose(); } MdkVideoPlayer() : super() { - onMediaStatus((oldValue, newValue) { + onMediaStatus.listen((event) { + final oldValue = event.oldValue; + final newValue = event.newValue; _log.fine( '$hashCode player$nativeHandle onMediaStatus: $oldValue => $newValue'); if (!oldValue.test(mdk.MediaStatus.loaded) && @@ -44,7 +43,7 @@ class MdkVideoPlayer extends mdk.Player { //} if (_initialized) { _log.fine('$hashCode player$nativeHandle already initialized'); - return true; + return; } _initialized = true; textureSize.then((size) { @@ -67,10 +66,9 @@ class MdkVideoPlayer extends mdk.Player { newValue.test(mdk.MediaStatus.buffered)) { streamCtl.add(VideoEvent(eventType: VideoEventType.bufferingEnd)); } - return true; }); - onEvent((ev) { + onEvent.listen((ev) { _log.fine( '$hashCode player$nativeHandle onEvent: ${ev.category} - ${ev.detail} - ${ev.error}'); if (ev.category == "reader.buffering") { @@ -84,17 +82,17 @@ class MdkVideoPlayer extends mdk.Player { } }); - onStateChanged((oldValue, newValue) { + onStateChanged.listen((event) { _log.fine( - '$hashCode player$nativeHandle onPlaybackStateChanged: $oldValue => $newValue'); - if (newValue == mdk.PlaybackState.stopped) { + '$hashCode player$nativeHandle onPlaybackStateChanged: ${event.oldValue} => ${event.newValue}'); + if (event.newValue == mdk.PlaybackState.stopped) { // FIXME: keep_open no stopped streamCtl.add(VideoEvent(eventType: VideoEventType.completed)); return; } streamCtl.add(VideoEvent( eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: newValue == mdk.PlaybackState.playing)); + isPlaying: event.newValue == mdk.PlaybackState.playing)); }); } } @@ -332,9 +330,8 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { @override Future setLooping(int playerId, bool looping) async { final player = _players[playerId]; - if (player != null) { - player.loop = looping ? -1 : 0; - } + if (player == null) return; + player.loop = looping ? -1 : 0; } @override @@ -426,11 +423,6 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { } Future snapshot(int playerId, {int? width, int? height}) async { - Uint8List? data; - final player = _players[playerId]; - if (player == null) { - return data; - } return _players[playerId]?.snapshot(width: width, height: height); } @@ -450,9 +442,7 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { Future step(int playerId, int frames) async { final player = _players[playerId]; - if (player == null) { - return; - } + if (player == null) return; player.seek( position: frames, flags: const mdk.SeekFlag(mdk.SeekFlag.frame | mdk.SeekFlag.fromNow)); @@ -519,9 +509,8 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { Future _seekToWithFlags( int playerId, Duration position, mdk.SeekFlag flags) async { final player = _players[playerId]; - if (player == null) { - return; - } + if (player == null) return; + if (player.isLive) { final bufMax = player.buffered(); final pos = player.position; From 444815a1325e2e4e542f36d3b7664a4a1778eb41 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 14 Apr 2026 17:33:52 +0800 Subject: [PATCH 082/109] cmake: fix dir --- android/CMakeLists.txt | 4 ++-- cmake/deps.cmake | 4 ++-- elinux/CMakeLists.txt | 2 +- linux/CMakeLists.txt | 2 +- windows/CMakeLists.txt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index dbbf890..e190be8 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -1,7 +1,7 @@ # The Flutter tooling requires that developers have CMake 3.10 or later # installed. You should not increase this version, as doing so will cause # the plugin to fail to compile for some customers of the plugin. -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") @@ -39,7 +39,7 @@ target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") -#set_target_properties(fvp_plugin PROPERTIES +#set_target_properties(${PLUGIN_NAME} PROPERTIES # PUBLIC_HEADER fvp_plugin.h # OUTPUT_NAME "fvp_plugin" #) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index f392d62..1e87a39 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -1,6 +1,6 @@ function(fvp_version) - set(PUBSPEC_FILE "${CMAKE_CURRENT_LIST_DIR}/../pubspec.yaml") + set(PUBSPEC_FILE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../pubspec.yaml") if(NOT EXISTS ${PUBSPEC_FILE}) message(FATAL_ERROR "pubspec.yaml not found: ${PUBSPEC_FILE}") endif() @@ -10,7 +10,7 @@ function(fvp_version) if(MATCHED_LINE) string(REGEX REPLACE "version:[ \t]*" "" FVP_VERSION "${MATCHED_LINE}") message(STATUS "Found fvp version: ${FVP_VERSION}") - set(VERSION_HEADER_FILE "${CMAKE_CURRENT_LIST_DIR}/../lib/src/version.h") + set(VERSION_HEADER_FILE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/src/version.h") file(WRITE ${VERSION_HEADER_FILE} "#pragma once\n#define FVP_VERSION \"${FVP_VERSION}\"\n") else() message(WARNING "No version line found in file") diff --git a/elinux/CMakeLists.txt b/elinux/CMakeLists.txt index 1d45215..6e0764a 100644 --- a/elinux/CMakeLists.txt +++ b/elinux/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.17) set(PROJECT_NAME "fvp") project(${PROJECT_NAME} LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index a767a09..aa88d6a 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -1,7 +1,7 @@ # The Flutter tooling requires that developers have CMake 3.10 or later # installed. You should not increase this version, as doing so will cause # the plugin to fail to compile for some customers of the plugin. -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index f304849..54f4e42 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -2,7 +2,7 @@ # installed that includes CMake 3.14 or later. You should not increase this # version, as doing so will cause the plugin to fail to compile for some # customers of the plugin. -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") From 37323ee6b7800d0e5a1be8c366257132e79b2f2c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 00:01:06 +0800 Subject: [PATCH 083/109] add ohos platform (#344) * add ohos support: native plugin, cmake deps, dart library loading, asset uri, decoders Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/c80e2b02-02c3-461e-b42e-5d57a5c3fdaa Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * consolidate ohos/android cases in lib.dart Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/c80e2b02-02c3-461e-b42e-5d57a5c3fdaa Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * restructure ohos directory with proper OHOS Flutter plugin layout and native C++ NAPI support Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/ef1c213b-5b60-48b4-99c5-c3c804e2db89 Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * fix ohos native: use napi_module_register+constructor, HiLog, correct library links, abiFilters Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/9d6b16e9-81aa-45a1-b44e-c689b61dad7f Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * fix ohos: use explicit %{public}lld format for HiLog int64 logging Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/9d6b16e9-81aa-45a1-b44e-c689b61dad7f Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * ohos: arm64-v8a only, keep C++20/cmake 3.10, revert log changes, copyright 2026 Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/bf7fd620-dfce-4e1e-b381-7e616ab5d6d4 Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * ohos: use NAPI_MODULE macro, set OH decoder, export symbols in fvp_plugin.h Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/96c70a2a-728f-4489-aa87-8ba64606a28c Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * ohos: fix CMake 3.17, libnative_window, hvigorfile.ts, copyright years, mdk-sdk-ohos.7z Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/d72442c9-83e4-43cc-bed3-8c4a9a5d85d7 Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> --- .gitignore | 3 + cmake/deps.cmake | 2 + lib/src/extensions.dart | 2 + lib/src/lib.dart | 5 +- lib/src/video_player_mdk.dart | 1 + ohos/build-profile.json5 | 31 +++++ ohos/hvigorfile.ts | 6 + ohos/index.ets | 8 ++ ohos/oh-package.json5 | 11 ++ ohos/src/main/cpp/CMakeLists.txt | 32 ++++++ ohos/src/main/cpp/fvp_plugin.cpp | 108 ++++++++++++++++++ ohos/src/main/cpp/include/fvp/fvp_plugin.h | 24 ++++ .../main/ets/components/plugin/FvpPlugin.ets | 82 +++++++++++++ ohos/src/main/module.json5 | 10 ++ pubspec.yaml | 3 + 15 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 ohos/build-profile.json5 create mode 100644 ohos/hvigorfile.ts create mode 100644 ohos/index.ets create mode 100644 ohos/oh-package.json5 create mode 100644 ohos/src/main/cpp/CMakeLists.txt create mode 100644 ohos/src/main/cpp/fvp_plugin.cpp create mode 100644 ohos/src/main/cpp/include/fvp/fvp_plugin.h create mode 100644 ohos/src/main/ets/components/plugin/FvpPlugin.ets create mode 100644 ohos/src/main/module.json5 diff --git a/.gitignore b/.gitignore index d12f841..0b55bda 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,9 @@ migrate_working_dir/ # is commented out by default. #.vscode/ +# OHOS local configuration (machine-specific) +local.properties + # Flutter/Dart/Pub related # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. /pubspec.lock diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 1e87a39..dcc3af9 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -27,6 +27,8 @@ macro(fvp_setup_deps) endif() elseif(ANDROID) set(MDK_SDK_PKG mdk-sdk-android.7z) + elseif(CMAKE_SYSTEM_NAME MATCHES "OHOS") + set(MDK_SDK_PKG mdk-sdk-ohos.7z) elseif(LINUX OR CMAKE_SYSTEM_NAME MATCHES "Linux") set(MDK_SDK_PKG mdk-sdk-linux.tar.xz) if(CMAKE_C_COMPILER_ARCHITECTURE_ID MATCHES "[xX].*64") diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 01407e9..60dbcb7 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -49,6 +49,8 @@ extension PlatformEx on Platform { 'Frameworks', 'App.framework', 'flutter_assets', key); case 'android': return 'assets://flutter_assets/$key'; + case 'ohos': + return 'assets://flutter_assets/$key'; } return asset; } diff --git a/lib/src/lib.dart b/lib/src/lib.dart index 932e9bf..6e4b3ed 100644 --- a/lib/src/lib.dart +++ b/lib/src/lib.dart @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Wang Bin. All rights reserved. +// Copyright 2022-2026 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:ffi'; @@ -18,6 +18,7 @@ abstract class Libmdk { case 'linux': name = 'libmdk.so.0'; case 'android': + case 'ohos': name = 'libmdk.so'; default: throw Exception( @@ -41,7 +42,7 @@ abstract class Libfvp { name = 'fvp_plugin.dll'; } else if (Platform.isIOS || Platform.isMacOS) { name = 'fvp.framework/fvp'; - } else if (Platform.isAndroid || Platform.isLinux) { + } else if (Platform.isAndroid || Platform.isLinux || Platform.operatingSystem == 'ohos') { name = 'libfvp_plugin.so'; } else { throw Exception( diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 8e97a77..a19cb7f 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -175,6 +175,7 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { 'ios': ['VT', 'FFmpeg', 'dav1d'], 'linux': vdLinux, 'android': ['AMediaCodec', 'FFmpeg', 'dav1d'], + 'ohos': ['OH', 'FFmpeg', 'dav1d'], }; _decoders = vd[Platform.operatingSystem]; } diff --git a/ohos/build-profile.json5 b/ohos/build-profile.json5 new file mode 100644 index 0000000..82df827 --- /dev/null +++ b/ohos/build-profile.json5 @@ -0,0 +1,31 @@ +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "abiFilters": ["arm64-v8a"], + "targets": ["fvp_plugin"] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + } + ], + "targets": [ + { + "name": "default" + } + ] +} diff --git a/ohos/hvigorfile.ts b/ohos/hvigorfile.ts new file mode 100644 index 0000000..8132322 --- /dev/null +++ b/ohos/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, + plugins: [] +} diff --git a/ohos/index.ets b/ohos/index.ets new file mode 100644 index 0000000..0168ee3 --- /dev/null +++ b/ohos/index.ets @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2025 WangBin + */ +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FvpPlugin from './src/main/ets/components/plugin/FvpPlugin'; +export default FvpPlugin; diff --git a/ohos/oh-package.json5 b/ohos/oh-package.json5 new file mode 100644 index 0000000..24a2946 --- /dev/null +++ b/ohos/oh-package.json5 @@ -0,0 +1,11 @@ +{ + "name": "fvp", + "version": "1.0.0", + "description": "fvp flutter plugin for OpenHarmony", + "main": "index.ets", + "author": "", + "license": "BSD-3-Clause", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + } +} diff --git a/ohos/src/main/cpp/CMakeLists.txt b/ohos/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..55c7def --- /dev/null +++ b/ohos/src/main/cpp/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.17) +set(PROJECT_NAME "fvp") +project(${PROJECT_NAME} LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# This value is used when generating builds using this plugin, so it must +# not be changed. +set(PLUGIN_NAME "fvp_plugin") + +add_library(${PLUGIN_NAME} SHARED + "fvp_plugin.cpp" + ../../../../lib/src/callbacks.cpp +) + +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_compile_options(${PLUGIN_NAME} PRIVATE -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections) +target_link_options(${PLUGIN_NAME} PRIVATE -Wl,--gc-sections) + +target_link_libraries(${PLUGIN_NAME} PRIVATE + libace_napi.z.so + libace_ndk.z.so + libnative_window.so +) + +include(../../../../cmake/deps.cmake) +fvp_setup_deps() +target_link_libraries(${PLUGIN_NAME} PRIVATE mdk) diff --git a/ohos/src/main/cpp/fvp_plugin.cpp b/ohos/src/main/cpp/fvp_plugin.cpp new file mode 100644 index 0000000..05743ac --- /dev/null +++ b/ohos/src/main/cpp/fvp_plugin.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2026 WangBin + */ +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "include/fvp/fvp_plugin.h" +#include +#include +#include +#include +#include + +using namespace std; + +class TexturePlayer final : public mdk::Player { +public: + explicit TexturePlayer(int64_t handle) + : mdk::Player(reinterpret_cast(handle)) + {} + + int width = 0; + int height = 0; + OHNativeWindow* window = nullptr; +}; + +static unordered_map> players; + +// nativeSetSurface(playerHandle: number, texId: number, surfaceId: number, w: number, h: number): void +static napi_value NativeSetSurface(napi_env env, napi_callback_info info) +{ + size_t argc = 5; + napi_value args[5]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + int64_t playerHandle = 0; + napi_get_value_int64(env, args[0], &playerHandle); + + int64_t texId = 0; + napi_get_value_int64(env, args[1], &texId); + + int64_t surfaceId = 0; + napi_get_value_int64(env, args[2], &surfaceId); + + int32_t w = 0, h = 0; + napi_get_value_int32(env, args[3], &w); + napi_get_value_int32(env, args[4], &h); + + if (!playerHandle || !surfaceId) { + if (auto it = players.find(texId); it != players.end()) { + auto& player = it->second; + player->updateNativeSurface(nullptr); + if (player->window) { + OH_NativeWindow_DestroyNativeWindow(player->window); + player->window = nullptr; + } + players.erase(it); + } else { + clog << "FvpPlugin: player not found (already removed?) for texId " << texId << endl; + } + return nullptr; + } + + auto player = make_shared(playerHandle); + player->width = w; + player->height = h; + + OHNativeWindow* window = nullptr; + int32_t ret = OH_NativeWindow_CreateNativeWindowFromSurfaceId(static_cast(surfaceId), &window); + if (ret != 0 || !window) { + clog << "FvpPlugin: OH_NativeWindow_CreateNativeWindowFromSurfaceId failed: " << ret << endl; + return nullptr; + } + + player->window = window; + player->updateNativeSurface(window, w, h); + players[texId] = player; + + return nullptr; +} + +static napi_value Init(napi_env env, napi_value exports) +{ + mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); + + napi_property_descriptor desc[] = { + { "nativeSetSurface", nullptr, NativeSetSurface, nullptr, nullptr, nullptr, napi_default, nullptr } + }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + + return exports; +} + +NAPI_MODULE(fvp_plugin, Init) + +extern "C" bool MdkIsEmulator() +{ + return false; +} + +extern "C" void* MdkGetPlayerVid(int64_t tex_id) +{ + if (tex_id < 0) + return nullptr; + if (const auto it = players.find(tex_id); it != players.end()) { + return it->second->window; + } + return nullptr; +} diff --git a/ohos/src/main/cpp/include/fvp/fvp_plugin.h b/ohos/src/main/cpp/include/fvp/fvp_plugin.h new file mode 100644 index 0000000..1e3796b --- /dev/null +++ b/ohos/src/main/cpp/include/fvp/fvp_plugin.h @@ -0,0 +1,24 @@ +#ifndef FLUTTER_PLUGIN_FVP_PLUGIN_H_ +#define FLUTTER_PLUGIN_FVP_PLUGIN_H_ + +#include +#include + +#if defined(_WIN32) +#define FVP_EXPORT __declspec(dllexport) +#else +#define FVP_EXPORT __attribute__((visibility("default"))) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FVP_EXPORT bool MdkIsEmulator(); +FVP_EXPORT void* MdkGetPlayerVid(int64_t tex_id); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_FVP_PLUGIN_H_ diff --git a/ohos/src/main/ets/components/plugin/FvpPlugin.ets b/ohos/src/main/ets/components/plugin/FvpPlugin.ets new file mode 100644 index 0000000..751d790 --- /dev/null +++ b/ohos/src/main/ets/components/plugin/FvpPlugin.ets @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2026 WangBin + */ +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import { + FlutterPlugin, + FlutterPluginBinding, + MethodCall, + MethodCallHandler, + MethodChannel, + MethodResult, + SurfaceTextureEntry, +} from '@ohos/flutter_ohos'; +import fvpNative from 'libfvp_plugin.so'; + +const TAG = 'FvpPlugin'; + +/** FvpPlugin */ +export default class FvpPlugin implements FlutterPlugin, MethodCallHandler { + private channel: MethodChannel | null = null; + private binding: FlutterPluginBinding | null = null; + private textures: Map = new Map(); + + getUniqueClassName(): string { + return 'FvpPlugin'; + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.binding = binding; + this.channel = new MethodChannel(binding.getBinaryMessenger(), 'fvp'); + this.channel.setMethodCallHandler(this); + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.textures.forEach((_entry, texId) => { + fvpNative.nativeSetSurface(0, texId, 0, 0, 0); + }); + this.textures.clear(); + this.channel?.setMethodCallHandler(null); + this.channel = null; + this.binding = null; + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + if (call.method === 'CreateRT') { + const args = call.args as Map; + const handle: number = args.get('player') as number; + const width: number = args.get('width') as number; + const height: number = args.get('height') as number; + + const textureRegistry = this.binding?.getTextureRegistry(); + if (!textureRegistry) { + result.error('NO_TEXTURE_REGISTRY', 'TextureRegistry not available', null); + return; + } + + const texId = textureRegistry.getTextureId(); + const entry = textureRegistry.registerTexture(texId); + const surfaceId = entry.getSurfaceId(); + textureRegistry.setTextureBufferSize(texId, width, height); + + fvpNative.nativeSetSurface(handle, texId, surfaceId, width, height); + + this.textures.set(texId, entry); + result.success(texId); + } else if (call.method === 'ReleaseRT') { + const args = call.args as Map; + const texId: number = args.get('texture') as number; + + fvpNative.nativeSetSurface(0, texId, 0, 0, 0); + this.binding?.getTextureRegistry()?.unregisterTexture(texId); + this.textures.delete(texId); + result.success(null); + } else if (call.method === 'MixWithOthers') { + result.success(null); + } else { + result.notImplemented(); + } + } +} diff --git a/ohos/src/main/module.json5 b/ohos/src/main/module.json5 new file mode 100644 index 0000000..b4f00c7 --- /dev/null +++ b/ohos/src/main/module.json5 @@ -0,0 +1,10 @@ +{ + "module": { + "name": "fvp", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} diff --git a/pubspec.yaml b/pubspec.yaml index ad0cbf3..8348dd1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,9 @@ flutter: elinux: dartPluginClass: VideoPlayerRegistrant # auto registered in .dart_tool/flutter_build/dart_plugin_registrant.dart pluginClass: FvpPlugin + ohos: + dartPluginClass: VideoPlayerRegistrant + pluginClass: FvpPlugin # To add assets to your plugin package, add an assets section, like this: # assets: From 38e349cd8d00e4c4f478dd2e9d184556eef915dd Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:04:25 +0800 Subject: [PATCH 084/109] Rename plugin shared library to `fvp` set key from dart (#345) * Initial plan * Change plugin library output name to fvp and export MdkSetKey Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/b1b7d524-fb7d-46a5-b750-f65b516b56aa Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * Remove MDK_KEY from native plugins; add setLicenseKey() in Dart Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/31fcc672-34d1-45ca-a71b-a19d59c1815c Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * Move MDK_KEY injection into MdkVideoPlayerPlatform._setupMdk(); remove setLicenseKey from global.dart Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/24bfef81-0618-4a86-a979-9431b192dc7e Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * Update default MDK_KEY to new value Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/27f20756-cb4f-4dcd-8040-ae5fa3eeedd9 Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> * Simplify mdkKey initialization using null-coalescing Agent-Logs-Url: https://github.com/wang-bin/fvp/sessions/ed7d1073-7872-4ec3-b7ad-09ad6aaea592 Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: wang-bin <785206+wang-bin@users.noreply.github.com> --- android/CMakeLists.txt | 3 ++- android/fvp_plugin.cpp | 1 - darwin/Classes/FvpPlugin.mm | 1 - elinux/CMakeLists.txt | 3 ++- elinux/fvp_plugin.cc | 1 - lib/src/callbacks.cpp | 7 +++++++ lib/src/callbacks.h | 1 + lib/src/lib.dart | 7 +++++-- lib/src/video_player_mdk.dart | 16 ++++++++++++++++ linux/CMakeLists.txt | 3 ++- linux/fvp_plugin.cc | 1 - ohos/src/main/cpp/CMakeLists.txt | 3 ++- ohos/src/main/cpp/fvp_plugin.cpp | 2 -- windows/CMakeLists.txt | 3 ++- windows/fvp_plugin.cpp | 1 - 15 files changed, 39 insertions(+), 14 deletions(-) diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index e190be8..b638b8e 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -31,7 +31,8 @@ add_library(${PLUGIN_NAME} SHARED # between plugins. This should not be removed; any symbols that should be # exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. set_target_properties(${PLUGIN_NAME} PROPERTIES - CXX_VISIBILITY_PRESET hidden) + CXX_VISIBILITY_PRESET hidden + OUTPUT_NAME "fvp") target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) # Source include directories and library dependencies. Add any plugin-specific diff --git a/android/fvp_plugin.cpp b/android/fvp_plugin.cpp index 44e59c2..2347f62 100644 --- a/android/fvp_plugin.cpp +++ b/android/fvp_plugin.cpp @@ -48,7 +48,6 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { } mdk::SetGlobalOption("JavaVM", vm); - mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); return JNI_VERSION_1_4; } diff --git a/darwin/Classes/FvpPlugin.mm b/darwin/Classes/FvpPlugin.mm index abc8d3e..d530d1c 100644 --- a/darwin/Classes/FvpPlugin.mm +++ b/darwin/Classes/FvpPlugin.mm @@ -141,7 +141,6 @@ + (void)registerWithRegistrar:(NSObject*)registrar { #endif [registrar publish:instance]; [registrar addMethodCallDelegate:instance channel:channel]; - SetGlobalOption("MDK_KEY", "C03BFF5306AB39058A767105F82697F42A00FE970FB0E641D306DEFF3F220547E5E5377A3C504DC30D547890E71059BC023A4DD91A95474D1F33CA4C26C81B0FC73B00ACF954C6FA75898EFA07D9680B6A00FDF179C0A15381101D01124498AF55B069BD4B0156D5CF5A56DEDE782E5F3930AD47C8F40BFBA379231142E31B0F"); } - (instancetype)initWithRegistrar:(NSObject*)registrar { diff --git a/elinux/CMakeLists.txt b/elinux/CMakeLists.txt index 6e0764a..91998e0 100644 --- a/elinux/CMakeLists.txt +++ b/elinux/CMakeLists.txt @@ -14,7 +14,8 @@ add_library(${PLUGIN_NAME} SHARED ) apply_standard_settings(${PLUGIN_NAME}) set_target_properties(${PLUGIN_NAME} PROPERTIES - CXX_VISIBILITY_PRESET hidden) + CXX_VISIBILITY_PRESET hidden + OUTPUT_NAME "fvp") target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") diff --git a/elinux/fvp_plugin.cc b/elinux/fvp_plugin.cc index 38e325f..40e36ba 100644 --- a/elinux/fvp_plugin.cc +++ b/elinux/fvp_plugin.cc @@ -245,7 +245,6 @@ void FvpPlugin::RegisterWithRegistrar( }); registrar->AddPlugin(std::move(plugin)); - mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); } void FvpPlugin::HandleMethodCall( diff --git a/lib/src/callbacks.cpp b/lib/src/callbacks.cpp index 5b38e48..26d4acb 100644 --- a/lib/src/callbacks.cpp +++ b/lib/src/callbacks.cpp @@ -41,6 +41,13 @@ static unordered_map> players; // global callbacks static int gCallbackTypes = 0; +FVP_EXPORT void MdkSetKey(const char* key) +{ + if (!key) + return; + mdk::SetGlobalOption("MDK_KEY", key); +} + FVP_EXPORT void MdkCallbacksRegisterPort(int64_t handle, void* post_c_object, int64_t send_port) { const auto postCObject = reinterpret_cast(post_c_object); diff --git a/lib/src/callbacks.h b/lib/src/callbacks.h index 166e364..d19a70c 100644 --- a/lib/src/callbacks.h +++ b/lib/src/callbacks.h @@ -18,6 +18,7 @@ #define FVP_EXPORT FVP_EXTERN_C __attribute__((visibility("default"))) #endif +FVP_EXPORT void MdkSetKey(const char* key); FVP_EXPORT void MdkCallbacksRegisterPort(int64_t handle, void* post_c_object, int64_t send_port); FVP_EXPORT void MdkCallbacksUnregisterPort(int64_t handle); FVP_EXPORT void MdkCallbacksRegisterType(int64_t handle, int type, bool reply); diff --git a/lib/src/lib.dart b/lib/src/lib.dart index 6e4b3ed..2018733 100644 --- a/lib/src/lib.dart +++ b/lib/src/lib.dart @@ -39,11 +39,11 @@ abstract class Libfvp { static DynamicLibrary _load() { String name; if (Platform.isWindows) { - name = 'fvp_plugin.dll'; + name = 'fvp.dll'; } else if (Platform.isIOS || Platform.isMacOS) { name = 'fvp.framework/fvp'; } else if (Platform.isAndroid || Platform.isLinux || Platform.operatingSystem == 'ohos') { - name = 'libfvp_plugin.so'; + name = 'libfvp.so'; } else { throw Exception( 'Unsupported operating system: ${Platform.operatingSystem}.', @@ -57,6 +57,9 @@ abstract class Libfvp { } static final instance = _load(); + static final setKey = instance.lookupFunction< + Void Function(Pointer), + void Function(Pointer)>('MdkSetKey'); static final registerPort = instance.lookupFunction< Void Function(Int64, Pointer, Int64), void Function(int, Pointer, int)>('MdkCallbacksRegisterPort'); diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index a19cb7f..16cecc4 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:ffi'; import 'dart:io'; import 'package:flutter/widgets.dart'; // import 'package:flutter/services.dart'; @@ -10,14 +11,24 @@ import 'package:video_player_platform_interface/video_player_platform_interface. import 'package:logging/logging.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; +import 'package:ffi/ffi.dart'; import 'fvp_platform_interface.dart'; import 'extensions.dart'; +import 'lib.dart'; import 'media_info.dart'; import '../mdk.dart' as mdk; final _log = Logger('fvp'); +// Default MDK_KEY bundled with this plugin. Users can override it by passing +// {'global': {'MDK_KEY': ''}} to registerWith(). +const _kDefaultMdkKey = + '4DF316BC71206BCEECD01559CC0FEDAF32DF8ECAE656BD9999CB821B67DB5B33' + '2B323F24539BA7172746B8F5A64CA67AF342B16BF4B418F76718B821F77BEA07' + '22F71DBC8EDF9431132FEAA633F0125072DF8DAC90268C62D0E4BA7D6DF9A6C4' + '976AB1146EC55FFD1945CAB4125B20D5C77976CF1BCB77B14C563868EA00EA07'; + class MdkVideoPlayer extends mdk.Player { final streamCtl = StreamController(); bool _initialized = false; @@ -237,8 +248,13 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { PlatformEx.assetUri(_subtitleFontFile ?? 'assets/subfont.ttf')); } _globalOpts?.forEach((key, value) { + if (key == 'MDK_KEY') return; // handled separately below mdk.setGlobalOption(key, value); }); + final mdkKey = _globalOpts?['MDK_KEY'] as String? ?? _kDefaultMdkKey; + final k = mdkKey.toNativeUtf8(); + Libfvp.setKey(k.cast()); + malloc.free(k); } @override diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index aa88d6a..8eecb41 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -31,7 +31,8 @@ apply_standard_settings(${PLUGIN_NAME}) # between plugins. This should not be removed; any symbols that should be # exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. set_target_properties(${PLUGIN_NAME} PROPERTIES - CXX_VISIBILITY_PRESET hidden) + CXX_VISIBILITY_PRESET hidden + OUTPUT_NAME "fvp") target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) # Source include directories and library dependencies. Add any plugin-specific diff --git a/linux/fvp_plugin.cc b/linux/fvp_plugin.cc index c9c6396..632b47b 100644 --- a/linux/fvp_plugin.cc +++ b/linux/fvp_plugin.cc @@ -289,7 +289,6 @@ void fvp_plugin_register_with_registrar(FlPluginRegistrar* registrar) { } else if (GDK_IS_WAYLAND_DISPLAY(gdisp)) { mdk::SetGlobalOption("wl_display*", gdk_wayland_display_get_wl_display(gdisp)); } - mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); } __attribute__((constructor, used)) diff --git a/ohos/src/main/cpp/CMakeLists.txt b/ohos/src/main/cpp/CMakeLists.txt index 55c7def..1642128 100644 --- a/ohos/src/main/cpp/CMakeLists.txt +++ b/ohos/src/main/cpp/CMakeLists.txt @@ -14,7 +14,8 @@ add_library(${PLUGIN_NAME} SHARED ) set_target_properties(${PLUGIN_NAME} PROPERTIES - CXX_VISIBILITY_PRESET hidden) + CXX_VISIBILITY_PRESET hidden + OUTPUT_NAME "fvp") target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") diff --git a/ohos/src/main/cpp/fvp_plugin.cpp b/ohos/src/main/cpp/fvp_plugin.cpp index 05743ac..6a62ede 100644 --- a/ohos/src/main/cpp/fvp_plugin.cpp +++ b/ohos/src/main/cpp/fvp_plugin.cpp @@ -80,8 +80,6 @@ static napi_value NativeSetSurface(napi_env env, napi_callback_info info) static napi_value Init(napi_env env, napi_value exports) { - mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); - napi_property_descriptor desc[] = { { "nativeSetSurface", nullptr, NativeSetSurface, nullptr, nullptr, nullptr, napi_default, nullptr } }; diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 54f4e42..d4cee76 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -46,7 +46,8 @@ apply_standard_settings(${PLUGIN_NAME}) # between plugins. This should not be removed; any symbols that should be # exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. set_target_properties(${PLUGIN_NAME} PROPERTIES - CXX_VISIBILITY_PRESET hidden) + CXX_VISIBILITY_PRESET hidden + OUTPUT_NAME "fvp") target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) #target_compile_definitions(${PLUGIN_NAME} PUBLIC DART_SHARED_LIB) # all platforms? diff --git a/windows/fvp_plugin.cpp b/windows/fvp_plugin.cpp index 7add44a..1a9af74 100644 --- a/windows/fvp_plugin.cpp +++ b/windows/fvp_plugin.cpp @@ -124,7 +124,6 @@ void FvpPlugin::RegisterWithRegistrar( }); registrar->AddPlugin(std::move(plugin)); - SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109"); } FvpPlugin::FvpPlugin(flutter::TextureRegistrar* tr, IDXGIAdapter* adapter) From 8ae19d628c4e33e63114c17f9ca05d283a9bbc26 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 21 Apr 2026 13:13:11 +0800 Subject: [PATCH 085/109] android: plugin name --- android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java index a8471e5..2e37bd7 100644 --- a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java +++ b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025 WangBin + * Copyright (c) 2023-2026 WangBin */ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -154,9 +154,9 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { static { try { - System.loadLibrary("fvp_plugin"); + System.loadLibrary("fvp"); } catch (UnsatisfiedLinkError e) { - Log.w("FvpPlugin", "static initializer: loadLibrary fvp_plugin error: " + e); + Log.w("FvpPlugin", "static initializer: loadLibrary fvp error: " + e); } } } From a75b76502ff6a37ff78fa42c122f27adb9141916 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 21 Apr 2026 17:47:13 +0800 Subject: [PATCH 086/109] ohos: asset path --- lib/src/extensions.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 60dbcb7..130cc98 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -50,7 +50,8 @@ extension PlatformEx on Platform { case 'android': return 'assets://flutter_assets/$key'; case 'ohos': - return 'assets://flutter_assets/$key'; + return path.join(path.dirname(Platform.resolvedExecutable), '..', '..', + 'resources', 'flutter_assets', key); } return asset; } From 02046e386fcac013567b1a51a9e7e15865eb6052 Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 21 Apr 2026 17:49:45 +0800 Subject: [PATCH 087/109] format --- lib/src/lib.dart | 7 ++++--- lib/src/player.dart | 22 ++++++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/src/lib.dart b/lib/src/lib.dart index 2018733..c8cebf3 100644 --- a/lib/src/lib.dart +++ b/lib/src/lib.dart @@ -42,7 +42,9 @@ abstract class Libfvp { name = 'fvp.dll'; } else if (Platform.isIOS || Platform.isMacOS) { name = 'fvp.framework/fvp'; - } else if (Platform.isAndroid || Platform.isLinux || Platform.operatingSystem == 'ohos') { + } else if (Platform.isAndroid || + Platform.isLinux || + Platform.operatingSystem == 'ohos') { name = 'libfvp.so'; } else { throw Exception( @@ -57,8 +59,7 @@ abstract class Libfvp { } static final instance = _load(); - static final setKey = instance.lookupFunction< - Void Function(Pointer), + static final setKey = instance.lookupFunction), void Function(Pointer)>('MdkSetKey'); static final registerPort = instance.lookupFunction< Void Function(Int64, Pointer, Int64), diff --git a/lib/src/player.dart b/lib/src/player.dart index 3b89ccb..c1dbd98 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -48,7 +48,10 @@ class Player { final oldValue = message[1] as int; final newValue = message[2] as int; if (_stateCb.hasListener) { - _stateCb.add((oldValue: PlaybackState.from(oldValue), newValue: PlaybackState.from(newValue))); + _stateCb.add(( + oldValue: PlaybackState.from(oldValue), + newValue: PlaybackState.from(newValue) + )); } Libfvp.replyType(nativeHandle, type, nullptr); } @@ -59,7 +62,10 @@ class Player { final newValue = message[2] as int; bool ret = true; if (_statusCb.hasListener) { - _statusCb.add((oldValue: MediaStatus(oldValue), newValue: MediaStatus(newValue))); + _statusCb.add(( + oldValue: MediaStatus(oldValue), + newValue: MediaStatus(newValue) + )); } rep.ref.mediaStatus.ret = ret; Libfvp.replyType(nativeHandle, type, rep.cast()); @@ -687,12 +693,14 @@ class Player { /// Get a [PlaybackState] change stream. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-onstatechangedstdfunctionvoidstate-cb - Stream<({PlaybackState oldValue, PlaybackState newValue})> get onStateChanged => _stateCb.stream; + Stream<({PlaybackState oldValue, PlaybackState newValue})> + get onStateChanged => _stateCb.stream; /// Get a [MediaStatus] change stream. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#player-onmediastatusstdfunctionboolmediastatus-oldvalue-mediastatus-newvalue-cb-callbacktoken-token--nullptr // reply: true to let native code wait for dart callback result, may result in dead lock because when native waiting main isolate reply, main isolate may execute another task(e.g. frequent seekTo) which also acquire the same lock in native - Stream<({MediaStatus oldValue, MediaStatus newValue})> get onMediaStatus => _statusCb.stream; + Stream<({MediaStatus oldValue, MediaStatus newValue})> get onMediaStatus => + _statusCb.stream; void onSubtitleText( void Function(double start, double end, List text)? callback) { @@ -756,8 +764,10 @@ class Player { final _receivePort = ReceivePort(); final _eventCb = StreamController.broadcast(); - final _stateCb = StreamController<({PlaybackState oldValue, PlaybackState newValue})>.broadcast(); - final _statusCb = StreamController<({MediaStatus oldValue, MediaStatus newValue})>.broadcast(); + final _stateCb = StreamController< + ({PlaybackState oldValue, PlaybackState newValue})>.broadcast(); + final _statusCb = StreamController< + ({MediaStatus oldValue, MediaStatus newValue})>.broadcast(); Function(double start, double end, List text)? _subtitleCb; Future Function()? _prepareCb; From b67e16ecf5e6197072037b49a0d2c628aac94b3b Mon Sep 17 00:00:00 2001 From: wangbin Date: Sun, 26 Apr 2026 23:55:33 +0800 Subject: [PATCH 088/109] v0.36.0 --- CHANGELOG.md | 5 +++++ darwin/fvp.podspec | 4 ++-- pubspec.yaml | 20 ++++++++++---------- windows/CMakeLists.txt | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 813b726..e14ec5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.36.0 + +* new ohos platform +* rename native shared lib + ## 0.35.2 * log plugin version diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index 7bec450..cf4d564 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.35.2' + s.version = '0.36.0' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. @@ -25,7 +25,7 @@ Flutter video player plugin. s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' - s.dependency 'mdk', '~> 0.35.1' + s.dependency 'mdk', '~> 0.36.0' # s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } diff --git a/pubspec.yaml b/pubspec.yaml index 8348dd1..a82038c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.35.2 +version: 0.36.0 homepage: https://github.com/wang-bin/fvp topics: - video @@ -13,17 +13,17 @@ environment: sdk: ^3.0.0 flutter: ">=3.0.0" -dependencies: - ffi: ^2.1.0 +dependencies: # TODO: limit max version? + ffi: any flutter: sdk: flutter - logging: ^1.2.0 - path: ^1.8.0 - plugin_platform_interface: ^2.0.0 - video_player: ^2.6.0 - video_player_platform_interface: ^6.2.0 - path_provider: ^2.1.2 - http: ^1.0.0 + logging: any + path: any + plugin_platform_interface: any + video_player: any + video_player_platform_interface: any + path_provider: any + http: any dev_dependencies: flutter_test: diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index d4cee76..e4d8b35 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.35.2 +project(${PROJECT_NAME} VERSION 0.36.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 94364d67df04b382788784cb1e3b5fdb35c91a00 Mon Sep 17 00:00:00 2001 From: qshh <101165772+qinshah@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:59:30 +0800 Subject: [PATCH 089/109] ohos: fix plugin native lib name. (#347) --- ohos/src/main/cpp/fvp_plugin.cpp | 2 +- ohos/src/main/ets/components/plugin/FvpPlugin.ets | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ohos/src/main/cpp/fvp_plugin.cpp b/ohos/src/main/cpp/fvp_plugin.cpp index 6a62ede..6a19219 100644 --- a/ohos/src/main/cpp/fvp_plugin.cpp +++ b/ohos/src/main/cpp/fvp_plugin.cpp @@ -88,7 +88,7 @@ static napi_value Init(napi_env env, napi_value exports) return exports; } -NAPI_MODULE(fvp_plugin, Init) +NAPI_MODULE(fvp, Init) extern "C" bool MdkIsEmulator() { diff --git a/ohos/src/main/ets/components/plugin/FvpPlugin.ets b/ohos/src/main/ets/components/plugin/FvpPlugin.ets index 751d790..c9e7477 100644 --- a/ohos/src/main/ets/components/plugin/FvpPlugin.ets +++ b/ohos/src/main/ets/components/plugin/FvpPlugin.ets @@ -13,7 +13,7 @@ import { MethodResult, SurfaceTextureEntry, } from '@ohos/flutter_ohos'; -import fvpNative from 'libfvp_plugin.so'; +import fvpNative from 'libfvp.so'; const TAG = 'FvpPlugin'; @@ -61,7 +61,7 @@ export default class FvpPlugin implements FlutterPlugin, MethodCallHandler { const surfaceId = entry.getSurfaceId(); textureRegistry.setTextureBufferSize(texId, width, height); - fvpNative.nativeSetSurface(handle, texId, surfaceId, width, height); + fvpNative.nativeSetSurface(handle, texId, surfaceId, width, height); this.textures.set(texId, entry); result.success(texId); From 3fc631fc3e956d9b87b855bd72dd876b8115f853 Mon Sep 17 00:00:00 2001 From: wangbin Date: Mon, 27 Apr 2026 10:15:34 +0800 Subject: [PATCH 090/109] remove unused import. format --- lib/src/video_player_mdk.dart | 1 - ohos/src/main/ets/components/plugin/FvpPlugin.ets | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 16cecc4..38d5a03 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ffi'; import 'dart:io'; import 'package:flutter/widgets.dart'; // import 'package:flutter/services.dart'; diff --git a/ohos/src/main/ets/components/plugin/FvpPlugin.ets b/ohos/src/main/ets/components/plugin/FvpPlugin.ets index c9e7477..1b1843e 100644 --- a/ohos/src/main/ets/components/plugin/FvpPlugin.ets +++ b/ohos/src/main/ets/components/plugin/FvpPlugin.ets @@ -61,7 +61,7 @@ export default class FvpPlugin implements FlutterPlugin, MethodCallHandler { const surfaceId = entry.getSurfaceId(); textureRegistry.setTextureBufferSize(texId, width, height); - fvpNative.nativeSetSurface(handle, texId, surfaceId, width, height); + fvpNative.nativeSetSurface(handle, texId, surfaceId, width, height); this.textures.set(texId, entry); result.success(texId); From 267f3b694993d728767edb3ddf5193e0562c2b31 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 29 Apr 2026 15:22:01 +0800 Subject: [PATCH 091/109] v0.36.1 --- CHANGELOG.md | 4 ++++ README.md | 6 +++--- darwin/fvp.podspec | 2 +- pubspec.yaml | 4 ++-- windows/CMakeLists.txt | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e14ec5f..5ec9b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.36.1 + +* fix ohos plugin native lib name + ## 0.36.0 * new ohos platform diff --git a/README.md b/README.md index 0bdc253..c5ba0c8 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Prebuilt example can be download from artifacts of [github actions](https://gith project is create with `flutter create -t plugin --platforms=linux,macos,windows,android,ios -i objc -a java fvp` ## Features -- All platforms: Windows x64(including win7) and arm64, Linux x64 and arm64, [embedded linux](https://github.com/sony/flutter-elinux), macOS, iOS, Android(requires flutter > 3.19 because of minSdk 21). +- All platforms: Windows x64(including win7) and arm64, Linux x64 and arm64, [embedded linux](https://github.com/sony/flutter-elinux), macOS, iOS, Android(requires flutter > 3.19 because of minSdk 21) and HarmonyOS 5.0+. - You can choose official implementation or this plugin's -- Optimal render api: d3d11 for windows, metal for macOS/iOS, OpenGL for Linux and Android(Impeller support) +- Optimal render api: d3d11 for windows, metal for macOS/iOS, OpenGL for Linux, Android(Impeller support) and HarmonyOS. - Hardware decoders are enabled by default - Dolby Vision support on all platforms - Minimal code change for existing [Video Player](https://pub.dev/packages/video_player) apps @@ -106,7 +106,7 @@ delete libffmpeg.so.* in your app bundle, which is copied from libmdk sdk. # Enable Subtitles -libass is required, and it's added to your app automatically for windows, macOS and android(remove ass.dll, libass.dylib and libass.so from mdk-sdk if you don't need it). For iOS, [download](https://sourceforge.net/projects/mdk-sdk/files/deps/dep.7z/download) and add `ass.framework` to your xcode project. For linux, system libass can be used, you may have to install manually via system package manager. +libass is required, and it's added to your app automatically for windows, macOS, ohos and android(remove ass.dll, libass.dylib and libass.so from mdk-sdk if you don't need it). For iOS, [download](https://sourceforge.net/projects/mdk-sdk/files/deps/dep.7z/download) and add `ass.framework` to your xcode project. For linux, system libass can be used, you may have to install manually via system package manager. If required subtitle font is not found in the system(e.g. android), you can add [assets/subfont.ttf](https://github.com/mpv-android/mpv-android/raw/master/app/src/main/assets/subfont.ttf) in pubspec.yaml assets as the fallback. Optionally you can also download the font file by fvp like this ```dart diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec index cf4d564..426199f 100644 --- a/darwin/fvp.podspec +++ b/darwin/fvp.podspec @@ -5,7 +5,7 @@ # Pod::Spec.new do |s| s.name = 'fvp' - s.version = '0.36.0' + s.version = '0.36.1' s.summary = 'libmdk based Flutter video player plugin' s.description = <<-DESC Flutter video player plugin. diff --git a/pubspec.yaml b/pubspec.yaml index a82038c..bbc09a3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,12 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.36.0 +version: 0.36.1 homepage: https://github.com/wang-bin/fvp topics: - video - player - video-player - - audio-player + - ohos - elinux environment: diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index e4d8b35..7265f5b 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.36.0 +project(${PROJECT_NAME} VERSION 0.36.1 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 10c0b0cf45568fba5baf1cc4c132d7a5654d4f32 Mon Sep 17 00:00:00 2001 From: wangbin Date: Fri, 1 May 2026 09:48:48 +0800 Subject: [PATCH 092/109] disable TCO. #350 affects linux, android, ohos release build --- lib/src/callbacks.cpp | 9 +++++++-- lib/src/callbacks.h | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/src/callbacks.cpp b/lib/src/callbacks.cpp index 26d4acb..6ecba33 100644 --- a/lib/src/callbacks.cpp +++ b/lib/src/callbacks.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2025 Wang Bin. All rights reserved. +// Copyright 2022-2026 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -41,7 +41,12 @@ static unordered_map> players; // global callbacks static int gCallbackTypes = 0; -FVP_EXPORT void MdkSetKey(const char* key) +FVP_EXPORT +#if (__clang__ + 0) +__attribute__((disable_tail_calls)) +//[[clang::disable_tail_calls]] // msvc: C5030 + C2220. clang 15: can not mix gnu and c++ attributes +#endif +void MdkSetKey(const char* key) { if (!key) return; diff --git a/lib/src/callbacks.h b/lib/src/callbacks.h index d19a70c..bb48c74 100644 --- a/lib/src/callbacks.h +++ b/lib/src/callbacks.h @@ -1,4 +1,4 @@ -// Copyright 2022-2025 Wang Bin. All rights reserved. +// Copyright 2022-2026 Wang Bin. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,7 +15,7 @@ #ifdef _WIN32 #define FVP_EXPORT FVP_EXTERN_C __declspec(dllexport) #else -#define FVP_EXPORT FVP_EXTERN_C __attribute__((visibility("default"))) +#define FVP_EXPORT FVP_EXTERN_C __attribute__((visibility("default"))) // will be built with objc compiler, c++ attribute can not be used #endif FVP_EXPORT void MdkSetKey(const char* key); From 8a820efbbcec3e8921d69cd64137fd1621284d07 Mon Sep 17 00:00:00 2001 From: wangbin Date: Fri, 1 May 2026 13:50:26 +0800 Subject: [PATCH 093/109] v0.36.2 --- CHANGELOG.md | 4 ++++ example/windows/CMakeLists.txt | 8 +++++++- pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec9b93..c813d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.36.2 + +* fix MDK_KEY in release build + ## 0.36.1 * fix ohos plugin native lib name diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt index fe4df5b..09b2a60 100644 --- a/example/windows/CMakeLists.txt +++ b/example/windows/CMakeLists.txt @@ -8,7 +8,7 @@ set(BINARY_NAME "fvp_example") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. -cmake_policy(SET CMP0063 NEW) +cmake_policy(VERSION 3.14...3.25) # Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) @@ -86,6 +86,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) COMPONENT Runtime) endif() +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/pubspec.yaml b/pubspec.yaml index bbc09a3..c8dffc2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.36.1 +version: 0.36.2 homepage: https://github.com/wang-bin/fvp topics: - video From 2e46bcde70f7cf89dc8da24eedd161fb39875d20 Mon Sep 17 00:00:00 2001 From: wangbin Date: Mon, 18 May 2026 19:27:45 +0800 Subject: [PATCH 094/109] ohos: rawfile asset --- lib/src/extensions.dart | 3 +-- ohos/src/main/cpp/CMakeLists.txt | 1 + ohos/src/main/cpp/fvp_plugin.cpp | 19 ++++++++++++++++++- .../main/ets/components/plugin/FvpPlugin.ets | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 130cc98..e74aa89 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -50,8 +50,7 @@ extension PlatformEx on Platform { case 'android': return 'assets://flutter_assets/$key'; case 'ohos': - return path.join(path.dirname(Platform.resolvedExecutable), '..', '..', - 'resources', 'flutter_assets', key); + return 'rawfile://flutter_assets/$key'; } return asset; } diff --git a/ohos/src/main/cpp/CMakeLists.txt b/ohos/src/main/cpp/CMakeLists.txt index 1642128..9a56233 100644 --- a/ohos/src/main/cpp/CMakeLists.txt +++ b/ohos/src/main/cpp/CMakeLists.txt @@ -26,6 +26,7 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE libace_napi.z.so libace_ndk.z.so libnative_window.so + librawfile.z.so ) include(../../../../cmake/deps.cmake) diff --git a/ohos/src/main/cpp/fvp_plugin.cpp b/ohos/src/main/cpp/fvp_plugin.cpp index 6a19219..dd917ff 100644 --- a/ohos/src/main/cpp/fvp_plugin.cpp +++ b/ohos/src/main/cpp/fvp_plugin.cpp @@ -6,6 +6,7 @@ #include "include/fvp/fvp_plugin.h" #include #include +#include #include #include #include @@ -78,10 +79,26 @@ static napi_value NativeSetSurface(napi_env env, napi_callback_info info) return nullptr; } +// nativeSetResourceManager(resourceManager: object): void +// Converts an OHOS ResourceManager object to native pointer and passes it to MDK. +static napi_value NativeSetResourceManager(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + NativeResourceManager* nativeRm = OH_ResourceManager_InitNativeResourceManager(env, args[0]); + if (nativeRm) { + mdk::SetGlobalOption("resourceManager", nativeRm); + } + return nullptr; +} + static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { - { "nativeSetSurface", nullptr, NativeSetSurface, nullptr, nullptr, nullptr, napi_default, nullptr } + { "nativeSetSurface", nullptr, NativeSetSurface, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "nativeSetResourceManager", nullptr, NativeSetResourceManager, nullptr, nullptr, nullptr, napi_default, nullptr }, }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); diff --git a/ohos/src/main/ets/components/plugin/FvpPlugin.ets b/ohos/src/main/ets/components/plugin/FvpPlugin.ets index 1b1843e..3ece817 100644 --- a/ohos/src/main/ets/components/plugin/FvpPlugin.ets +++ b/ohos/src/main/ets/components/plugin/FvpPlugin.ets @@ -31,6 +31,7 @@ export default class FvpPlugin implements FlutterPlugin, MethodCallHandler { this.binding = binding; this.channel = new MethodChannel(binding.getBinaryMessenger(), 'fvp'); this.channel.setMethodCallHandler(this); + fvpNative.nativeSetResourceManager(binding.getApplicationContext().resourceManager()); } onDetachedFromEngine(binding: FlutterPluginBinding): void { From 87fc9c85c94cf783c80d0dda28f34013eb3084f6 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 20 May 2026 18:27:51 +0800 Subject: [PATCH 095/109] add mapPoint, setPointMap. #354 --- lib/src/global.dart | 10 ++++++++ lib/src/player.dart | 59 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/lib/src/global.dart b/lib/src/global.dart index e7db5c4..7c50a70 100644 --- a/lib/src/global.dart +++ b/lib/src/global.dart @@ -173,6 +173,16 @@ enum ColorSpace { } } +/// https://github.com/wang-bin/mdk-sdk/wiki/Types#enum-mapdirection +enum MapDirection { + frameToViewport(MDK_MapDirection.MDK_MapDirection_FrameToViewport), + viewportToFrame(MDK_MapDirection.MDK_MapDirection_ViewportToFrame), + ; + + final int rawValue; + const MapDirection(this.rawValue); +} + enum LogLevel { off(MDK_LogLevel.MDK_LogLevel_Off), error(MDK_LogLevel.MDK_LogLevel_Error), diff --git a/lib/src/player.dart b/lib/src/player.dart index c1dbd98..d59cb64 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -616,7 +616,64 @@ class Player { void Function(Pointer, double, Pointer)>()( _player.ref.object, value, _getVid()); - // TODO: mapPoint( List) + /// Set region-of-interest mapping for video display + /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpointmapconst-float-videoroi-const-float-viewroi--nullptr-int-count--2-void-vo_opaque--nullptr + void setPointMap({List? videoRoi, List? viewRoi, int count = 2}) { + final cv = (videoRoi != null) + ? () { + final p = calloc(count * 2); + for (int i = 0; i < videoRoi.length; ++i) { + p[i] = videoRoi[i]; + } + return p; + }() + : nullptr; + final cw = (viewRoi != null) + ? () { + final p = calloc(count * 2); + for (int i = 0; i < viewRoi.length; ++i) { + p[i] = viewRoi[i]; + } + return p; + }() + : nullptr; + _player.ref.setPointMap.asFunction< + void Function(Pointer, Pointer, Pointer, + int, Pointer)>()( + _player.ref.object, cv.cast(), cw.cast(), count, _getVid()); + if (cv != nullptr) { + calloc.free(cv); + } + if (cw != nullptr) { + calloc.free(cw); + } + } + + /// Map a point between viewport coordinates and video frame coordinates. + /// Returns the mapped coordinates as a record `({double x, double y, double z})`. + /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-mappointmapdirection-dir-float-x-float-y-float-z--nullptr-void-vo_opaque--nullptr + ({double x, double y, double z}) mapPoint( + MapDirection direction, { + required double x, + required double y, + double z = 0, + }) { + final cx = calloc(); + final cy = calloc(); + final cz = calloc(); + cx.value = x; + cy.value = y; + cz.value = z; + _player.ref.mapPoint.asFunction< + void Function(Pointer, int, Pointer, + Pointer, Pointer, Pointer)>()( + _player.ref.object, direction.rawValue, cx, cy, cz, _getVid()); + final result = (x: cx.value, y: cy.value, z: cz.value); + calloc.free(cx); + calloc.free(cy); + calloc.free(cz); + return result; + } /// rotate video content around the center. [degree] can be 0, 90, 180, 270 in counterclockwise. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-rotateint-degree-void-vo_opaque--nullptr From bbfcfcf13708ce162dd3083ae8a1766fb6af87b2 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 20 May 2026 21:22:31 +0800 Subject: [PATCH 096/109] remove z in Player.mapPoint() --- lib/src/player.dart | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/src/player.dart b/lib/src/player.dart index d59cb64..ae77f87 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -127,11 +127,11 @@ class Player { Libfvp.registerPort(nativeHandle, NativeApi.postCObject.cast(), _receivePort.sendPort.nativePort); - _stateCb.stream.listen((event) { + onStateChanged.listen((event) { _state = event.newValue; }); Libfvp.registerType(nativeHandle, 1, false); - _statusCb.stream.listen((event) { + onMediaStatus.listen((event) { final oldValue = event.oldValue; final newValue = event.newValue; if (!oldValue.test(MediaStatus.loaded) && @@ -158,7 +158,7 @@ class Player { } }); Libfvp.registerType(nativeHandle, 2, false); - _eventCb.stream.listen((e) { + onEvent.listen((e) { if (_videoSize.isCompleted) { return; } @@ -618,7 +618,8 @@ class Player { /// Set region-of-interest mapping for video display /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-setpointmapconst-float-videoroi-const-float-viewroi--nullptr-int-count--2-void-vo_opaque--nullptr - void setPointMap({List? videoRoi, List? viewRoi, int count = 2}) { + void setPointMap( + {List? videoRoi, List? viewRoi, int count = 2}) { final cv = (videoRoi != null) ? () { final p = calloc(count * 2); @@ -650,25 +651,23 @@ class Player { } /// Map a point between viewport coordinates and video frame coordinates. - /// Returns the mapped coordinates as a record `({double x, double y, double z})`. + /// Returns the mapped coordinates as a record `({double x, double y})`. /// https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs#void-mappointmapdirection-dir-float-x-float-y-float-z--nullptr-void-vo_opaque--nullptr - ({double x, double y, double z}) mapPoint( + ({double x, double y}) mapPoint( MapDirection direction, { required double x, required double y, - double z = 0, }) { final cx = calloc(); final cy = calloc(); final cz = calloc(); cx.value = x; cy.value = y; - cz.value = z; _player.ref.mapPoint.asFunction< void Function(Pointer, int, Pointer, Pointer, Pointer, Pointer)>()( _player.ref.object, direction.rawValue, cx, cy, cz, _getVid()); - final result = (x: cx.value, y: cy.value, z: cz.value); + final result = (x: cx.value, y: cy.value); calloc.free(cx); calloc.free(cy); calloc.free(cz); From 2938485af364e39f5d56a7cd53c02c184fb31d75 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 20 May 2026 21:34:31 +0800 Subject: [PATCH 097/109] controller: prefer playerId over textureId --- lib/src/controller.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 0da6b35..0e4f9f0 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -46,15 +46,14 @@ extension FVPControllerExtensions on VideoPlayerController { } // extension can't override existing method, e.g. `dynamic noSuchMethod(Invocation invocation)` */ -// TODO: prefer playerId in a future version static final int Function(VideoPlayerController c) _getId = () { + // prefer playerId when available(since video_player 2.10.0 to support platform view), fallback to textureId for older video_player versions try { - // try to get textureId. static implies late, but can't access this - final _ = (VideoPlayerController.file(File('')) as dynamic).textureId; - return (dynamic c) => c.textureId as int; - } on NoSuchMethodError { - // since video_player 2.10.0 to support platform view + // try to get playerId. static implies late, but can't access this + final _ = (VideoPlayerController.file(File('')) as dynamic).playerId; return (dynamic c) => c.playerId as int; + } on NoSuchMethodError { + return (dynamic c) => c.textureId as int; } }(); From 43cc0ed403074cf7db74765fdccc64fa24382653 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 21 May 2026 11:56:21 +0800 Subject: [PATCH 098/109] v0.37.0 --- CHANGELOG.md | 6 ++++++ pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c813d16..03df33f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.37.0 + +* add Player.setPointMap() to support ROI rendering +* add Player.pointMap() to map between widget viewport coordinates and frame coordinates +* support asset for OHOS + # 0.36.2 * fix MDK_KEY in release build diff --git a/pubspec.yaml b/pubspec.yaml index c8dffc2..0120732 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.36.2 +version: 0.37.0 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 7265f5b..908e470 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.36.1 +project(${PROJECT_NAME} VERSION 0.37.0 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 601a8d1b1eede2f9bb64c8e1a576347abe691424 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 21 May 2026 13:17:50 +0800 Subject: [PATCH 099/109] cmake: lower cmake version requirement, make ci happy --- cmake/deps.cmake | 4 ++++ linux/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index dcc3af9..d7606f9 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -1,5 +1,9 @@ function(fvp_version) + if(NOT DEFINED CMAKE_CURRENT_FUNCTION_LIST_DIR) + message(WARNING "CMAKE_CURRENT_FUNCTION_LIST_DIR not defined") + set(CMAKE_CURRENT_FUNCTION_LIST_DIR ${CMAKE_CURRENT_LIST_DIR}) + endif() set(PUBSPEC_FILE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../pubspec.yaml") if(NOT EXISTS ${PUBSPEC_FILE}) message(FATAL_ERROR "pubspec.yaml not found: ${PUBSPEC_FILE}") diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 8eecb41..5c9c0ca 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -1,7 +1,7 @@ # The Flutter tooling requires that developers have CMake 3.10 or later # installed. You should not increase this version, as doing so will cause # the plugin to fail to compile for some customers of the plugin. -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.15) # Project-level configuration. set(PROJECT_NAME "fvp") From ce43748e160fc45026505537220b2a9f0aa1c733 Mon Sep 17 00:00:00 2001 From: wangbin Date: Thu, 21 May 2026 17:55:00 +0800 Subject: [PATCH 100/109] support SPM --- darwin/fvp/Package.swift | 52 ++++++++++++++++++++++ darwin/fvp/Resources/PrivacyInfo.xcprivacy | 1 + darwin/fvp/Sources/fvp/FvpPlugin.h | 1 + darwin/fvp/Sources/fvp/FvpPlugin.mm | 1 + darwin/fvp/Sources/fvp/callbacks.cpp | 1 + darwin/fvp/Sources/fvp/callbacks.h | 1 + darwin/fvp/Sources/fvp/dart_api_types.h | 1 + 7 files changed, 58 insertions(+) create mode 100644 darwin/fvp/Package.swift create mode 120000 darwin/fvp/Resources/PrivacyInfo.xcprivacy create mode 120000 darwin/fvp/Sources/fvp/FvpPlugin.h create mode 120000 darwin/fvp/Sources/fvp/FvpPlugin.mm create mode 120000 darwin/fvp/Sources/fvp/callbacks.cpp create mode 120000 darwin/fvp/Sources/fvp/callbacks.h create mode 120000 darwin/fvp/Sources/fvp/dart_api_types.h diff --git a/darwin/fvp/Package.swift b/darwin/fvp/Package.swift new file mode 100644 index 0000000..a69a729 --- /dev/null +++ b/darwin/fvp/Package.swift @@ -0,0 +1,52 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "fvp", + platforms: [ + .iOS("12.0"), + .macOS("10.13"), + ], + products: [ + .library(name: "fvp", targets: ["fvp"]), + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework"), + ], + targets: [ + .target( + name: "fvp", + dependencies: [ + .target(name: "mdk"), + .product(name: "FlutterFramework", package: "FlutterFramework"), + ], + path: ".", + sources: [ + "Sources/fvp/FvpPlugin.mm", + "Sources/fvp/callbacks.cpp", + ], + resources: [ + .process("Resources/PrivacyInfo.xcprivacy"), + ], + publicHeadersPath: "Sources/fvp", + cSettings: [ + .headerSearchPath("Sources/fvp"), + ], + cxxSettings: [ + .unsafeFlags(["-Wno-documentation"]), + ], + linkerSettings: [ + .linkedFramework("AVFoundation"), + .linkedFramework("CoreVideo"), + .linkedFramework("Metal"), + ] + ), + .binaryTarget( + name: "mdk", + url: "https://github.com/wang-bin/mdk-sdk/releases/download/v0.36.0/mdk-sdk-apple.zip", + checksum: "9c73e96a113096c9e8d9d255477e5327301bff6dead2266f7ab5436d37d6ccb6" + ), + ], + cxxLanguageStandard: .cxx20 +) diff --git a/darwin/fvp/Resources/PrivacyInfo.xcprivacy b/darwin/fvp/Resources/PrivacyInfo.xcprivacy new file mode 120000 index 0000000..582146e --- /dev/null +++ b/darwin/fvp/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1 @@ +../../PrivacyInfo.xcprivacy \ No newline at end of file diff --git a/darwin/fvp/Sources/fvp/FvpPlugin.h b/darwin/fvp/Sources/fvp/FvpPlugin.h new file mode 120000 index 0000000..9e6320a --- /dev/null +++ b/darwin/fvp/Sources/fvp/FvpPlugin.h @@ -0,0 +1 @@ +../../../Classes/FvpPlugin.h \ No newline at end of file diff --git a/darwin/fvp/Sources/fvp/FvpPlugin.mm b/darwin/fvp/Sources/fvp/FvpPlugin.mm new file mode 120000 index 0000000..fb23041 --- /dev/null +++ b/darwin/fvp/Sources/fvp/FvpPlugin.mm @@ -0,0 +1 @@ +../../../Classes/FvpPlugin.mm \ No newline at end of file diff --git a/darwin/fvp/Sources/fvp/callbacks.cpp b/darwin/fvp/Sources/fvp/callbacks.cpp new file mode 120000 index 0000000..36e2f60 --- /dev/null +++ b/darwin/fvp/Sources/fvp/callbacks.cpp @@ -0,0 +1 @@ +../../../../lib/src/callbacks.cpp \ No newline at end of file diff --git a/darwin/fvp/Sources/fvp/callbacks.h b/darwin/fvp/Sources/fvp/callbacks.h new file mode 120000 index 0000000..2d2d759 --- /dev/null +++ b/darwin/fvp/Sources/fvp/callbacks.h @@ -0,0 +1 @@ +../../../../lib/src/callbacks.h \ No newline at end of file diff --git a/darwin/fvp/Sources/fvp/dart_api_types.h b/darwin/fvp/Sources/fvp/dart_api_types.h new file mode 120000 index 0000000..2cd0a1a --- /dev/null +++ b/darwin/fvp/Sources/fvp/dart_api_types.h @@ -0,0 +1 @@ +../../../../lib/src/dart_api_types.h \ No newline at end of file From eebfb1cb2da5b025c9a286cc8ff0a3c0f86b6dc3 Mon Sep 17 00:00:00 2001 From: wangbin Date: Sat, 30 May 2026 15:30:38 +0800 Subject: [PATCH 101/109] v0.37.1 --- CHANGELOG.md | 4 ++++ pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03df33f..f0eb59a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.37.1 + +* support swift package manager + # 0.37.0 * add Player.setPointMap() to support ROI rendering diff --git a/pubspec.yaml b/pubspec.yaml index 0120732..64b18fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.37.0 +version: 0.37.1 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 908e470..08e859c 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.37.0 +project(${PROJECT_NAME} VERSION 0.37.1 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From 2e5bf1e4ecb1695d23657a24be0423f9d458921e Mon Sep 17 00:00:00 2001 From: "Jona T. Feucht" Date: Wed, 3 Jun 2026 03:41:29 +0200 Subject: [PATCH 102/109] fix(darwin): change SPM to dynamically link the framework (#359) * fix(darwin): change SPM to dynamically link the framework With the introduction of SPM the framework gets statically linked resulting in '/usr/lib/fvp.framework/fvp' (no such file, not in dyld cache), 'fvp.framework/fvp' (no such file). This PR changes this so that it gets dynamically linked and fixes this issue. Devices tested: - iOS Simulator - Physical iPhone (Debug) - Physical iPhone (Release) * Update darwin/fvp/Package.swift Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- darwin/fvp/Package.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/fvp/Package.swift b/darwin/fvp/Package.swift index a69a729..10bc861 100644 --- a/darwin/fvp/Package.swift +++ b/darwin/fvp/Package.swift @@ -9,7 +9,7 @@ let package = Package( .macOS("10.13"), ], products: [ - .library(name: "fvp", targets: ["fvp"]), + .library(name: "fvp", type: .dynamic, targets: ["fvp"]), ], dependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework"), @@ -37,6 +37,8 @@ let package = Package( .unsafeFlags(["-Wno-documentation"]), ], linkerSettings: [ + .linkedFramework("Flutter", .when(platforms: [.iOS])), + .linkedFramework("FlutterMacOS", .when(platforms: [.macOS])), .linkedFramework("AVFoundation"), .linkedFramework("CoreVideo"), .linkedFramework("Metal"), From c52e677adb2e90854cc2a3a02f310c6e3e132118 Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 3 Jun 2026 09:47:54 +0800 Subject: [PATCH 103/109] v0.37.2 --- CHANGELOG.md | 4 ++++ pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0eb59a..c7670d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.37.2 + +* produce dynamic framework with SPM + # 0.37.1 * support swift package manager diff --git a/pubspec.yaml b/pubspec.yaml index 64b18fd..7f659f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.37.1 +version: 0.37.2 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 08e859c..dcf72b9 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.37.1 +project(${PROJECT_NAME} VERSION 0.37.2 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From ce6236de9b70dce6f69cdc28ba83d8842f4788e1 Mon Sep 17 00:00:00 2001 From: Abdelaziz Mahdy Date: Sat, 20 Jun 2026 19:48:32 -0300 Subject: [PATCH 104/109] fix(windows): extract mdk-sdk via realpath to avoid CMake symlink rejection (#367) * fix(windows): extract mdk-sdk via realpath to avoid CMake symlink rejection On Flutter Windows, CMAKE_CURRENT_SOURCE_DIR is reached through the generated plugin symlink (.../flutter/ephemeral/.plugin_symlinks/fvp). Newer CMake's `cmake -E tar` extracts with ARCHIVE_EXTRACT_SECURE_SYMLINKS and refuses to write entries through that symlinked directory: CMake Error: Problem with archive_write_header(): Cannot extract through symlink ...\.plugin_symlinks\fvp Failed to extract mdk-sdk. Resolve CMAKE_CURRENT_SOURCE_DIR to its real (non-symlinked) path with get_filename_component(... REALPATH) and use it as the tar WORKING_DIRECTORY, so extraction no longer traverses the symlink. REALPATH (not file(REAL_PATH)) keeps this compatible with cmake_minimum_required(VERSION 3.17). * quote paths in mdk-sdk extraction execute_process Per review (paths with spaces): quote MDK_SDK_SAVE and the resolved WORKING_DIRECTORY so extraction doesn't break on paths containing spaces. --- cmake/deps.cmake | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index d7606f9..868ad88 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -76,9 +76,17 @@ macro(fvp_setup_deps) file(MD5 ${MDK_SDK_SAVE} MDK_SDK_MD5_SAVE) message("MDK_SDK_MD5_SAVE: ${MDK_SDK_MD5_SAVE}") endif() + # On Flutter Windows, CMAKE_CURRENT_SOURCE_DIR is reached through the + # generated plugin symlink (.../flutter/ephemeral/.plugin_symlinks/fvp). + # Newer CMake's `cmake -E tar` extracts with ARCHIVE_EXTRACT_SECURE_SYMLINKS, + # which refuses to write entries through a symlinked directory + # ("Cannot extract through symlink"). Resolve the real path so extraction + # targets a non-symlinked directory. REALPATH (vs file(REAL_PATH)) keeps + # this compatible with the cmake_minimum_required(3.17) of this plugin. + get_filename_component(MDK_SDK_EXTRACT_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH) execute_process( - COMMAND ${CMAKE_COMMAND} -E tar "xvf" ${MDK_SDK_SAVE} # "--format=7zip" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${MDK_SDK_SAVE}" # "--format=7zip" + WORKING_DIRECTORY "${MDK_SDK_EXTRACT_DIR}" OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE EXTRACT_RET ) From 203c607dfdb16f4dc2de58333a7ba2851dfdc0ab Mon Sep 17 00:00:00 2001 From: WangBin Date: Sun, 21 Jun 2026 19:05:41 +0800 Subject: [PATCH 105/109] ci: remove 3.19 for win --- .github/workflows/build.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6afcbe8..5abc964 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,11 +82,8 @@ jobs: strategy: fail-fast: false matrix: - version: ['3.19.x', 'any'] # 3.19 for win7 + version: ['any'] # 3.19 for win7, but requires vs2019 on gha channel: ['stable', 'beta'] - exclude: - - version: '3.19.x' - channel: 'beta' steps: - uses: actions/checkout@v6 with: @@ -289,4 +286,4 @@ jobs: cache: true - run: flutter doctor --verbose - run: flutter pub get - - run: flutter build web ${{ matrix.wasm }} --verbose \ No newline at end of file + - run: flutter build web ${{ matrix.wasm }} --verbose From 032b969b625133d2fd72b3f7dff1ee581fea1911 Mon Sep 17 00:00:00 2001 From: Abdelaziz Mahdy Date: Sun, 28 Jun 2026 02:27:21 -0300 Subject: [PATCH 106/109] fix(darwin): ship PrivacyInfo.xcprivacy as a real file for SwiftPM (#373) darwin/fvp/Resources/PrivacyInfo.xcprivacy was a symlink to ../../PrivacyInfo.xcprivacy, which points outside the SwiftPM package root (darwin/fvp). When a host app builds with Swift Package Manager, Flutter symlinks each plugin package into /macos/Flutter/ephemeral/Packages/.packages/. Xcode then resolves the resource's escaping symlink relative to that symlinked package location and looks for it at .packages/PrivacyInfo.xcprivacy, which does not exist: error: .../ephemeral/Packages/.packages/PrivacyInfo.xcprivacy: No such file or directory (in target 'fvp_fvp' from project 'fvp') so `flutter build macos`/`ios` fails for any app using SwiftPM. Source files under Sources/fvp are also symlinks but compile fine, because Xcode follows symlinks for compile inputs; only the .process resource copy phase breaks on the escaping link. Replace the symlink with a real copy of the manifest (the same pattern every first-party plugin uses, e.g. connectivity_plus ships a real PrivacyInfo.xcprivacy inside Sources/). The CocoaPods path is unaffected: the podspec still references darwin/PrivacyInfo.xcprivacy. --- darwin/fvp/Resources/PrivacyInfo.xcprivacy | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) mode change 120000 => 100644 darwin/fvp/Resources/PrivacyInfo.xcprivacy diff --git a/darwin/fvp/Resources/PrivacyInfo.xcprivacy b/darwin/fvp/Resources/PrivacyInfo.xcprivacy deleted file mode 120000 index 582146e..0000000 --- a/darwin/fvp/Resources/PrivacyInfo.xcprivacy +++ /dev/null @@ -1 +0,0 @@ -../../PrivacyInfo.xcprivacy \ No newline at end of file diff --git a/darwin/fvp/Resources/PrivacyInfo.xcprivacy b/darwin/fvp/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..a34b7e2 --- /dev/null +++ b/darwin/fvp/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + From c2a74fb8eb0e58f5b58e5e6dee3e3329a36ab0eb Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 30 Jun 2026 00:00:50 +0800 Subject: [PATCH 107/109] android: ensure jvm is set first. fix #375 --- android/fvp_plugin.cpp | 7 +++---- android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/fvp_plugin.cpp b/android/fvp_plugin.cpp index 2347f62..d573a62 100644 --- a/android/fvp_plugin.cpp +++ b/android/fvp_plugin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 WangBin + * Copyright (c) 2023-2026 WangBin */ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -37,17 +37,16 @@ static unordered_map> players; extern "C" { JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { - + clog << "JNI_OnLoad" << endl; + mdk::javaVM(vm); mdk::SetGlobalOption("profiler.gpu", 1); - clog << "JNI_OnLoad" << endl; JNIEnv *env = nullptr; if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK || !env) { clog << "GetEnv for JNI_VERSION_1_4 failed" << endl; return -1; } - mdk::SetGlobalOption("JavaVM", vm); return JNI_VERSION_1_4; } diff --git a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java index 2e37bd7..f599988 100644 --- a/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java +++ b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java @@ -154,6 +154,7 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { static { try { + System.loadLibrary("mdk"); System.loadLibrary("fvp"); } catch (UnsatisfiedLinkError e) { Log.w("FvpPlugin", "static initializer: loadLibrary fvp error: " + e); From 41dfb5487f835ba321cb73471c2a0a4e62382a6d Mon Sep 17 00:00:00 2001 From: wangbin Date: Wed, 1 Jul 2026 09:52:12 +0800 Subject: [PATCH 108/109] v0.37.3 --- CHANGELOG.md | 6 ++++++ darwin/fvp/Package.swift | 4 ++-- pubspec.yaml | 2 +- windows/CMakeLists.txt | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7670d9..5f75bb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.37.3 + +* fix PrivacyInfo.xcprivacy for SPM +* upgrade libmdk for SPM +* fix new cmake build on windows + # 0.37.2 * produce dynamic framework with SPM diff --git a/darwin/fvp/Package.swift b/darwin/fvp/Package.swift index 10bc861..005870d 100644 --- a/darwin/fvp/Package.swift +++ b/darwin/fvp/Package.swift @@ -46,8 +46,8 @@ let package = Package( ), .binaryTarget( name: "mdk", - url: "https://github.com/wang-bin/mdk-sdk/releases/download/v0.36.0/mdk-sdk-apple.zip", - checksum: "9c73e96a113096c9e8d9d255477e5327301bff6dead2266f7ab5436d37d6ccb6" + url: "https://github.com/wang-bin/mdk-sdk/releases/download/v0.37.0/mdk-sdk-apple.zip", + checksum: "1f92b5318138fdf90dfc2424c0ff751d64f705413ae0d3c7f04aa3faec49c921" ), ], cxxLanguageStandard: .cxx20 diff --git a/pubspec.yaml b/pubspec.yaml index 7f659f9..1fa4f48 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fvp description: video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg -version: 0.37.2 +version: 0.37.3 homepage: https://github.com/wang-bin/fvp topics: - video diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index dcf72b9..4bde588 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.17) # Project-level configuration. set(PROJECT_NAME "fvp") -project(${PROJECT_NAME} VERSION 0.37.2 +project(${PROJECT_NAME} VERSION 0.37.3 DESCRIPTION "Flutter video_player plugin via libmdk") set(PROJECT_VERSION_TWEAK 0) set(CMAKE_CXX_STANDARD 20) From a58bca574da09fc6dd259cdc4119f4679dda136c Mon Sep 17 00:00:00 2001 From: wangbin Date: Tue, 7 Jul 2026 13:33:42 +0800 Subject: [PATCH 109/109] add onSubtitleText for VideoPlayerController extension #377 --- lib/src/controller.dart | 7 +++++++ lib/src/video_player_dummy.dart | 3 +++ lib/src/video_player_mdk.dart | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 0e4f9f0..95c9443 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -210,4 +210,11 @@ extension FVPControllerExtensions on VideoPlayerController { void setExternalSubtitle(String uri) { _platform.setExternalSubtitle(_getId(this), uri); } + + /// Set a callback to receive subtitle text when active subtitle track renders a new piece of text. + /// [start] and [end] are in seconds. [text] is the subtitle text lines. Pass null to disable. + void onSubtitleText( + void Function(double start, double end, List text)? callback) { + _platform.onSubtitleText(_getId(this), callback); + } } diff --git a/lib/src/video_player_dummy.dart b/lib/src/video_player_dummy.dart index 68dad0d..a04127a 100644 --- a/lib/src/video_player_dummy.dart +++ b/lib/src/video_player_dummy.dart @@ -69,4 +69,7 @@ class MdkVideoPlayerPlatform { void setExternalVideo(int playerId, String uri) {} void setExternalSubtitle(int playerId, String uri) {} + + void onSubtitleText(int playerId, + void Function(double start, double end, List text)? callback) {} } diff --git a/lib/src/video_player_mdk.dart b/lib/src/video_player_mdk.dart index 38d5a03..dfcba9d 100644 --- a/lib/src/video_player_mdk.dart +++ b/lib/src/video_player_mdk.dart @@ -522,6 +522,11 @@ class MdkVideoPlayerPlatform extends VideoPlayerPlatform { _players[playerId]?.setMedia(uri, mdk.MediaType.subtitle); } + void onSubtitleText(int playerId, + void Function(double start, double end, List text)? callback) { + _players[playerId]?.onSubtitleText(callback); + } + Future _seekToWithFlags( int playerId, Duration position, mdk.SeekFlag flags) async { final player = _players[playerId];