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
```
+
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"]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d701202..a4add88 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,15 +18,26 @@ on:
- cron: '0 6 * * 0'
jobs:
+ CMake-tests:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v6
+ - run: cmake -S cmake/tests -B build/cmake-tests
+ - run: ctest -C Release --output-on-failure
+ working-directory: build/cmake-tests
+
macOS:
- runs-on: macos-14
+ runs-on: macos-15
defaults:
run:
working-directory: example
strategy:
fail-fast: false
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- uses: subosito/flutter-action@v2
@@ -41,20 +52,20 @@ 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
iOS:
- runs-on: macos-14
+ runs-on: macos-15
defaults:
run:
working-directory: example
strategy:
fail-fast: false
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- uses: subosito/flutter-action@v2
@@ -68,7 +79,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
@@ -82,13 +93,10 @@ jobs:
strategy:
fail-fast: false
matrix:
- version: ['3.19.x', 'any'] # 3.19 for win7
- channel: ['stable', 'master']
- exclude:
- - version: '3.19.x'
- channel: 'master'
+ version: ['any'] # 3.19 for win7, but requires vs2019 on gha
+ channel: ['stable', 'beta']
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- uses: subosito/flutter-action@v2
@@ -102,7 +110,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: |
@@ -118,12 +126,12 @@ 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
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- uses: subosito/flutter-action@v2
@@ -141,11 +149,74 @@ 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
+
+ Linux-arm64:
+ if: true
+ runs-on: ubuntu-24.04-arm
+ defaults:
+ run:
+ working-directory: example
+ strategy:
+ fail-fast: false
+ matrix:
+ version: ['any']
+ channel: ['master']
+ steps:
+ - uses: actions/checkout@v6
+ 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: 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@v6
+ with:
+ name: fvp-example-linux-arm64-${{ matrix.channel }}-${{ matrix.version }}
+ path: example/fvp_example_linux*.tar.xz
+
+
+ Snap-arm64:
+ if: false
+ runs-on: ubuntu-24.04-arm
+ defaults:
+ run:
+ working-directory: example
+ steps:
+ - uses: actions/checkout@v6
+ 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@v6
+ with:
+ name: fvp-example-linux-arm64-snap
+ path: example/fvp_example_linux*.tar.xz
+
Snap:
runs-on: ubuntu-latest
defaults:
@@ -156,10 +227,10 @@ jobs:
matrix:
target: [linux] #, apk]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- - uses: actions/setup-java@v4
+ - uses: actions/setup-java@v5
if: ${{ matrix.target == 'apk' }}
with:
distribution: 'zulu'
@@ -184,7 +255,7 @@ jobs:
host: [ubuntu]
version: ['3.22.x', 'any']
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- uses: subosito/flutter-action@v2
@@ -192,7 +263,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'
@@ -201,7 +272,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
@@ -217,7 +288,7 @@ jobs:
matrix:
wasm: ['--wasm', '']
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'recursive'
- uses: subosito/flutter-action@v2
@@ -226,4 +297,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
diff --git a/.github/workflows/pub.yml b/.github/workflows/pub.yml
index a23275a..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
@@ -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/.gitignore b/.gitignore
index d12f841..3c8e8ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,8 @@ mdk-sdk/
*.log
*.pyc
*.swp
+*.part
+.fvp-mdk-*/
.DS_Store
.atom/
.buildlog/
@@ -30,6 +32,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/CHANGELOG.md b/CHANGELOG.md
index 135632b..5f75bb2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,109 @@
+# 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
+
+# 0.37.1
+
+* support swift package manager
+
+# 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
+
+## 0.36.1
+
+* fix ohos plugin native lib name
+
+## 0.36.0
+
+* new ohos platform
+* rename native shared lib
+
+## 0.35.2
+
+* log plugin version
+
+## 0.35.1
+
+* fix linux arch for cmake 4.0+
+* fix flutter 3.38 android build
+
+## 0.35.0
+
+* add Player.onSubtitleText
+* allow hls & segments w/o or w/ any extension
+
+## 0.34.0
+
+* add rockchip decoder
+* deploy libmdk plugins
+
+## 0.33.1
+
+* fix protocol whitelist
+* enable dav1d, hap
+
+## 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+
+
+## 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
+* 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
+
+## 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
+* 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
+* 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 1a155d2..411959e 100644
--- a/README.md
+++ b/README.md
@@ -9,21 +9,22 @@ 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) 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
-- 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, VP8 and VP9 transparent video
- Small footprint. Only about 10MB size increase per cpu architecture(platform dependent).
## 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
@@ -91,6 +92,16 @@ rm -rf {mac,i}os/Pods
For other platforms, set environment var `FVP_DEPS_LATEST=1` and rebuilt, will upgrade to the latest sdk. If fvp is installed from pub.dev, run `flutter pub cache clean` is another option.
+To use an immutable SDK release or mirror, set its base URL and expected SHA-256. The platform archive name, such as `mdk-sdk-android.7z`, is appended to the URL.
+
+```bash
+FVP_DEPS_URL=https://example.com/mdk-sdk/releases/vX.Y.Z \
+FVP_DEPS_SHA256="replace-with-64-character-sha256" \
+flutter build apk
+```
+
+Replace the SHA-256 placeholder with the archive's 64-character digest. Both options can also be set as CMake cache variables. A non-empty CMake value takes precedence over the corresponding environment variable. When `FVP_DEPS_SHA256` is set, the downloaded archive is verified and the extracted SDK cache is reused only if its recorded checksum matches. `FVP_DEPS_LATEST` is ignored in this mode.
+
# Design
- Playback control api in dart via ffi
@@ -98,12 +109,21 @@ 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
-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 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.
@@ -114,4 +134,4 @@ Flutter can be installed by snap, but it will add some [enviroment vars(`CPLUS_I



-
\ No newline at end of file
+
diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt
index dbbf890..b638b8e 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")
@@ -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
@@ -39,7 +40,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/android/build.gradle b/android/build.gradle
index 8f0b13a..b63aae5 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.13.2")
}
}
@@ -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 = 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_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ 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 {
@@ -93,11 +93,22 @@ 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) }
+ flutterSdkPath = properties.getProperty("flutter.sdk")
+ }
}
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) {
@@ -122,4 +133,13 @@ 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'])
+ }
+ if (flutterSdkVersionInt < 32800) {
+ println 'rename onSurfaceCleanup to onSurfaceDestroyed'
+ preprocessJava(['onSurfaceCleanup': 'onSurfaceDestroyed'])
+ }
}
diff --git a/android/fvp_plugin.cpp b/android/fvp_plugin.cpp
index e7cdcbb..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.
@@ -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:
};
@@ -36,18 +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);
- mdk::SetGlobalOption("MDK_KEY", "980B9623276F746C5FBB5EC5120D4A99A0B58B635592EAEE41F6817FDF3B28B96AC4A49866257726C19B246863B5ADAF5D17464E86D72A90634E8AE8418F810967F469DCD8908B93A044A13AEDF2B566E0B5810523E2B59E2D83E616B1B807B66253E1607A79BC86AEDE1AEF46F79AA60F36BE44DDEE47B84E165AF2788F8109");
return JNI_VERSION_1_4;
}
@@ -82,6 +81,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 +111,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/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java b/android/src/main/java/com/mediadevkit/fvp/FvpPlugin.java
index a32892f..f599988 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-2026 WangBin
*/
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -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
@@ -102,8 +102,8 @@ public void onSurfaceCreated() {
}
@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);
}
@@ -154,9 +154,10 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
static {
try {
- System.loadLibrary("fvp_plugin");
+ System.loadLibrary("mdk");
+ 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);
}
}
}
diff --git a/cmake/deps.cmake b/cmake/deps.cmake
index 7fb50a6..be0e6d8 100644
--- a/cmake/deps.cmake
+++ b/cmake/deps.cmake
@@ -1,58 +1,318 @@
+if(NOT DEFINED FVP_DEPS_URL)
+ set(FVP_DEPS_URL "" CACHE STRING "Base URL of MDK SDK archives")
+endif()
+if(NOT DEFINED FVP_DEPS_SHA256)
+ set(FVP_DEPS_SHA256 "" CACHE STRING "Expected SHA-256 of the MDK SDK archive")
+endif()
+
+
+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}")
+ 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_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")
+ endif()
+endfunction(fvp_version)
+
+
+function(_fvp_resolve_deps_options DEFAULT_URL OUT_URL OUT_SHA256)
+ set(DEPS_URL "${FVP_DEPS_URL}")
+ string(STRIP "${DEPS_URL}" DEPS_URL)
+ if("${DEPS_URL}" STREQUAL "" AND "$ENV{FVP_DEPS_URL}" MATCHES "^http")
+ set(DEPS_URL "$ENV{FVP_DEPS_URL}")
+ string(STRIP "${DEPS_URL}" DEPS_URL)
+ endif()
+ if("${DEPS_URL}" STREQUAL "")
+ set(DEPS_URL "${DEFAULT_URL}")
+ string(STRIP "${DEPS_URL}" DEPS_URL)
+ endif()
+ string(REGEX REPLACE "/+$" "" DEPS_URL "${DEPS_URL}")
+
+ set(DEPS_SHA256 "${FVP_DEPS_SHA256}")
+ string(STRIP "${DEPS_SHA256}" DEPS_SHA256)
+ if("${DEPS_SHA256}" STREQUAL "")
+ set(DEPS_SHA256 "$ENV{FVP_DEPS_SHA256}")
+ string(STRIP "${DEPS_SHA256}" DEPS_SHA256)
+ endif()
+ if(NOT "${DEPS_SHA256}" STREQUAL "")
+ string(LENGTH "${DEPS_SHA256}" DEPS_SHA256_LENGTH)
+ if(NOT DEPS_SHA256_LENGTH EQUAL 64 OR NOT "${DEPS_SHA256}" MATCHES "^[0-9A-Fa-f]+$")
+ message(FATAL_ERROR "FVP_DEPS_SHA256 must be a 64-character hexadecimal SHA-256 value")
+ endif()
+ string(TOLOWER "${DEPS_SHA256}" DEPS_SHA256)
+ endif()
+
+ set(${OUT_URL} "${DEPS_URL}" PARENT_SCOPE)
+ set(${OUT_SHA256} "${DEPS_SHA256}" PARENT_SCOPE)
+endfunction()
+
+
+function(_fvp_download_file URL OUTPUT EXPECTED_SHA256)
+ get_filename_component(OUTPUT_DIR "${OUTPUT}" DIRECTORY)
+ file(MAKE_DIRECTORY "${OUTPUT_DIR}")
+ set(PARTIAL_OUTPUT "${OUTPUT}.part")
+ file(REMOVE "${PARTIAL_OUTPUT}")
+
+ message(STATUS "Downloading ${URL}")
+ file(DOWNLOAD "${URL}" "${PARTIAL_OUTPUT}"
+ SHOW_PROGRESS
+ STATUS DOWNLOAD_STATUS
+ )
+ list(GET DOWNLOAD_STATUS 0 DOWNLOAD_STATUS_CODE)
+ list(GET DOWNLOAD_STATUS 1 DOWNLOAD_STATUS_MESSAGE)
+ if(NOT DOWNLOAD_STATUS_CODE EQUAL 0)
+ file(REMOVE "${PARTIAL_OUTPUT}")
+ message(FATAL_ERROR "Failed to download ${URL}: ${DOWNLOAD_STATUS_MESSAGE}")
+ endif()
+
+ if(NOT "${EXPECTED_SHA256}" STREQUAL "")
+ file(SHA256 "${PARTIAL_OUTPUT}" DOWNLOADED_SHA256)
+ string(TOLOWER "${DOWNLOADED_SHA256}" DOWNLOADED_SHA256)
+ if(NOT "${DOWNLOADED_SHA256}" STREQUAL "${EXPECTED_SHA256}")
+ file(REMOVE "${PARTIAL_OUTPUT}")
+ message(FATAL_ERROR
+ "SHA-256 mismatch for ${URL}\n"
+ " expected: ${EXPECTED_SHA256}\n"
+ " actual: ${DOWNLOADED_SHA256}"
+ )
+ endif()
+ endif()
+
+ file(RENAME "${PARTIAL_OUTPUT}" "${OUTPUT}")
+endfunction()
+
+
+function(_fvp_prepare_mdk_sdk SDK_URL SDK_ARCHIVE EXTRACT_ROOT EXPECTED_SHA256 UPDATE_LATEST)
+ get_filename_component(REAL_EXTRACT_ROOT "${EXTRACT_ROOT}" REALPATH)
+ file(MAKE_DIRECTORY "${REAL_EXTRACT_ROOT}")
+ file(LOCK "${REAL_EXTRACT_ROOT}/fvp-deps.lock"
+ GUARD FUNCTION
+ TIMEOUT 300
+ RESULT_VARIABLE LOCK_RESULT
+ )
+ if(NOT "${LOCK_RESULT}" STREQUAL "0")
+ message(FATAL_ERROR "Failed to lock the MDK SDK cache: ${LOCK_RESULT}")
+ endif()
+
+ set(SDK_DIR "${REAL_EXTRACT_ROOT}/mdk-sdk")
+ set(SDK_MARKER "${SDK_DIR}/lib/cmake/FindMDK.cmake")
+ set(SDK_STAMP "${SDK_DIR}/.fvp-deps.sha256")
+ set(SDK_BACKUP_DIR "${REAL_EXTRACT_ROOT}/.fvp-mdk-backup")
+
+ if(EXISTS "${SDK_BACKUP_DIR}")
+ if(EXISTS "${SDK_DIR}")
+ file(REMOVE_RECURSE "${SDK_BACKUP_DIR}")
+ else()
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E rename "${SDK_BACKUP_DIR}" "${SDK_DIR}"
+ ERROR_VARIABLE RECOVERY_ERROR
+ RESULT_VARIABLE RECOVERY_RESULT
+ )
+ if(NOT RECOVERY_RESULT EQUAL 0)
+ message(FATAL_ERROR "Failed to recover the cached mdk-sdk: ${RECOVERY_ERROR}")
+ endif()
+ endif()
+ endif()
+
+ if(NOT "${EXPECTED_SHA256}" STREQUAL "" AND EXISTS "${SDK_MARKER}" AND EXISTS "${SDK_STAMP}")
+ file(READ "${SDK_STAMP}" CACHED_SHA256)
+ string(STRIP "${CACHED_SHA256}" CACHED_SHA256)
+ string(TOLOWER "${CACHED_SHA256}" CACHED_SHA256)
+ if("${CACHED_SHA256}" STREQUAL "${EXPECTED_SHA256}")
+ message(STATUS "Using cached mdk-sdk (SHA256: ${CACHED_SHA256})")
+ return()
+ endif()
+ endif()
+
+ set(FORCE_DOWNLOAD OFF)
+ if("${EXPECTED_SHA256}" STREQUAL "" AND UPDATE_LATEST)
+ if(EXISTS "${SDK_ARCHIVE}")
+ message(STATUS "Downloading latest MD5")
+ _fvp_download_file("${SDK_URL}.md5" "${SDK_ARCHIVE}.md5" "")
+ file(READ "${SDK_ARCHIVE}.md5" LATEST_MD5)
+ string(STRIP "${LATEST_MD5}" LATEST_MD5)
+ file(MD5 "${SDK_ARCHIVE}" ARCHIVE_MD5)
+ message(STATUS "MD5 [${ARCHIVE_MD5}] => [${LATEST_MD5}]")
+ if(NOT "${LATEST_MD5}" STREQUAL "${ARCHIVE_MD5}")
+ set(FORCE_DOWNLOAD ON)
+ endif()
+ else()
+ set(FORCE_DOWNLOAD ON)
+ endif()
+ endif()
+
+ if("${EXPECTED_SHA256}" STREQUAL "" AND NOT FORCE_DOWNLOAD AND EXISTS "${SDK_MARKER}")
+ set(CAN_REUSE_SDK ON)
+ if(UPDATE_LATEST)
+ if(EXISTS "${SDK_ARCHIVE}" AND EXISTS "${SDK_STAMP}")
+ file(SHA256 "${SDK_ARCHIVE}" ARCHIVE_SHA256)
+ file(READ "${SDK_STAMP}" CACHED_SHA256)
+ string(STRIP "${CACHED_SHA256}" CACHED_SHA256)
+ string(TOLOWER "${CACHED_SHA256}" CACHED_SHA256)
+ if(NOT "${ARCHIVE_SHA256}" STREQUAL "${CACHED_SHA256}")
+ message(STATUS "Cached mdk-sdk does not match the downloaded archive; re-extracting")
+ set(CAN_REUSE_SDK OFF)
+ endif()
+ else()
+ message(STATUS "Cached mdk-sdk checksum is unavailable; re-extracting")
+ set(CAN_REUSE_SDK OFF)
+ endif()
+ endif()
+ if(CAN_REUSE_SDK AND EXISTS "${SDK_STAMP}")
+ file(READ "${SDK_STAMP}" CACHED_SHA256)
+ string(STRIP "${CACHED_SHA256}" CACHED_SHA256)
+ message(STATUS "Using cached mdk-sdk (SHA256: ${CACHED_SHA256})")
+ elseif(CAN_REUSE_SDK)
+ message(STATUS "Using cached mdk-sdk (checksum unavailable)")
+ endif()
+ if(CAN_REUSE_SDK)
+ return()
+ endif()
+ endif()
+
+ set(NEED_DOWNLOAD "${FORCE_DOWNLOAD}")
+ if(EXISTS "${SDK_ARCHIVE}" AND NOT NEED_DOWNLOAD)
+ file(SHA256 "${SDK_ARCHIVE}" ARCHIVE_SHA256)
+ string(TOLOWER "${ARCHIVE_SHA256}" ARCHIVE_SHA256)
+ if(NOT "${EXPECTED_SHA256}" STREQUAL "" AND NOT "${ARCHIVE_SHA256}" STREQUAL "${EXPECTED_SHA256}")
+ message(STATUS "Cached MDK SDK archive does not match FVP_DEPS_SHA256")
+ set(NEED_DOWNLOAD ON)
+ endif()
+ else()
+ set(NEED_DOWNLOAD ON)
+ endif()
+
+ if(NEED_DOWNLOAD)
+ _fvp_download_file("${SDK_URL}" "${SDK_ARCHIVE}" "${EXPECTED_SHA256}")
+ file(SHA256 "${SDK_ARCHIVE}" ARCHIVE_SHA256)
+ string(TOLOWER "${ARCHIVE_SHA256}" ARCHIVE_SHA256)
+ endif()
+ message(STATUS "MDK SDK archive SHA256: ${ARCHIVE_SHA256}")
+
+ set(TEMP_EXTRACT_DIR "${REAL_EXTRACT_ROOT}/.fvp-mdk-extract")
+ file(REMOVE_RECURSE "${TEMP_EXTRACT_DIR}")
+ file(MAKE_DIRECTORY "${TEMP_EXTRACT_DIR}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E tar "xvf" "${SDK_ARCHIVE}"
+ WORKING_DIRECTORY "${TEMP_EXTRACT_DIR}"
+ OUTPUT_QUIET
+ ERROR_VARIABLE EXTRACT_ERROR
+ RESULT_VARIABLE EXTRACT_RESULT
+ )
+ if(NOT EXTRACT_RESULT EQUAL 0 OR NOT EXISTS "${TEMP_EXTRACT_DIR}/mdk-sdk/lib/cmake/FindMDK.cmake")
+ file(REMOVE_RECURSE "${TEMP_EXTRACT_DIR}")
+ if("${EXPECTED_SHA256}" STREQUAL "")
+ file(REMOVE "${SDK_ARCHIVE}")
+ endif()
+ message(FATAL_ERROR
+ "Failed to extract mdk-sdk from ${SDK_ARCHIVE}: ${EXTRACT_ERROR}"
+ )
+ endif()
+ file(WRITE "${TEMP_EXTRACT_DIR}/mdk-sdk/.fvp-deps.sha256" "${ARCHIVE_SHA256}\n")
+
+ if(EXISTS "${SDK_DIR}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E rename "${SDK_DIR}" "${SDK_BACKUP_DIR}"
+ ERROR_VARIABLE BACKUP_ERROR
+ RESULT_VARIABLE BACKUP_RESULT
+ )
+ if(NOT BACKUP_RESULT EQUAL 0)
+ file(REMOVE_RECURSE "${TEMP_EXTRACT_DIR}")
+ message(FATAL_ERROR "Failed to back up the cached mdk-sdk: ${BACKUP_ERROR}")
+ endif()
+ endif()
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E rename "${TEMP_EXTRACT_DIR}/mdk-sdk" "${SDK_DIR}"
+ ERROR_VARIABLE INSTALL_ERROR
+ RESULT_VARIABLE INSTALL_RESULT
+ )
+ if(NOT INSTALL_RESULT EQUAL 0)
+ if(EXISTS "${SDK_BACKUP_DIR}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E rename "${SDK_BACKUP_DIR}" "${SDK_DIR}"
+ ERROR_VARIABLE RESTORE_ERROR
+ RESULT_VARIABLE RESTORE_RESULT
+ )
+ if(NOT RESTORE_RESULT EQUAL 0)
+ file(REMOVE_RECURSE "${TEMP_EXTRACT_DIR}")
+ message(FATAL_ERROR
+ "Failed to install the extracted mdk-sdk: ${INSTALL_ERROR}\n"
+ "The previous SDK is preserved at ${SDK_BACKUP_DIR}, but could not be restored: ${RESTORE_ERROR}"
+ )
+ endif()
+ endif()
+ file(REMOVE_RECURSE "${TEMP_EXTRACT_DIR}")
+ message(FATAL_ERROR "Failed to install the extracted mdk-sdk: ${INSTALL_ERROR}")
+ endif()
+ file(REMOVE_RECURSE "${SDK_BACKUP_DIR}")
+ file(REMOVE_RECURSE "${TEMP_EXTRACT_DIR}")
+ message(STATUS "Extracted mdk-sdk (SHA256: ${ARCHIVE_SHA256})")
+endfunction()
+
+
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)
+ 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_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()
endif()
- set(MDK_SDK_URL https://sourceforge.net/projects/mdk-sdk/files/nightly/${MDK_SDK_PKG})
- set(MDK_SDK_SAVE "${CMAKE_CURRENT_SOURCE_DIR}/${MDK_SDK_PKG}")
+ _fvp_resolve_deps_options(
+ "https://sourceforge.net/projects/mdk-sdk/files/nightly"
+ FVP_DEPS_URL_EFFECTIVE
+ FVP_DEPS_SHA256_EFFECTIVE
+ )
+ set(MDK_SDK_URL "${FVP_DEPS_URL_EFFECTIVE}/${MDK_SDK_PKG}")
+ get_filename_component(MDK_SDK_EXTRACT_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
+ set(MDK_SDK_SAVE "${MDK_SDK_EXTRACT_DIR}/${MDK_SDK_PKG}")
+ message(STATUS "MDK SDK URL: ${MDK_SDK_URL}")
+ if(NOT "${FVP_DEPS_SHA256_EFFECTIVE}" STREQUAL "")
+ message(STATUS "MDK SDK expected SHA256: ${FVP_DEPS_SHA256_EFFECTIVE}")
+ endif()
- set(DOWNLOAD_MDK_SDK OFF)
- message("FVP_DEPS_LATEST=$ENV{FVP_DEPS_LATEST}")
+ set(UPDATE_MDK_SDK_LATEST OFF)
+ set(FVP_DEPS_LATEST_EFFECTIVE "$ENV{FVP_DEPS_LATEST}")
+ message(STATUS "FVP_DEPS_LATEST=${FVP_DEPS_LATEST_EFFECTIVE}")
# TODO: download from github option FVP_DEPS_LATEST_RELEASE=1
- if($ENV{FVP_DEPS_LATEST})
- if(EXISTS ${MDK_SDK_SAVE})
- message("Downloading latest md5")
- file(DOWNLOAD ${MDK_SDK_URL}.md5 ${MDK_SDK_SAVE}.md5 SHOW_PROGRESS)
- file(READ ${MDK_SDK_SAVE}.md5 MDK_SDK_MD5_LATEST)
- string(STRIP "${MDK_SDK_MD5_LATEST}" MDK_SDK_MD5_LATEST)
- file(MD5 ${MDK_SDK_SAVE} MDK_SDK_MD5_SAVE)
- message("md5 [${MDK_SDK_MD5_SAVE}] => [${MDK_SDK_MD5_LATEST}]")
- if(NOT MDK_SDK_MD5_LATEST STREQUAL MDK_SDK_MD5_SAVE)
- set(DOWNLOAD_MDK_SDK ON)
- endif()
- else()
- set(DOWNLOAD_MDK_SDK ON)
- endif()
+ if(NOT "${FVP_DEPS_SHA256_EFFECTIVE}" STREQUAL "" AND FVP_DEPS_LATEST_EFFECTIVE)
+ message(STATUS "Ignoring FVP_DEPS_LATEST because FVP_DEPS_SHA256 is set")
+ elseif(FVP_DEPS_LATEST_EFFECTIVE)
+ set(UPDATE_MDK_SDK_LATEST ON)
endif()
- if(DOWNLOAD_MDK_SDK OR NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/mdk-sdk/lib/cmake/FindMDK.cmake)
- if(DOWNLOAD_MDK_SDK OR NOT EXISTS ${MDK_SDK_SAVE})
- message("Downloading mdk-sdk from ${MDK_SDK_URL}")
- file(DOWNLOAD ${MDK_SDK_URL} ${MDK_SDK_SAVE} SHOW_PROGRESS)
- file(MD5 ${MDK_SDK_SAVE} MDK_SDK_MD5_SAVE)
- message("MDK_SDK_MD5_SAVE: ${MDK_SDK_MD5_SAVE}")
- endif()
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar "xvf" ${MDK_SDK_SAVE} # "--format=7zip"
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- OUTPUT_STRIP_TRAILING_WHITESPACE
- RESULT_VARIABLE EXTRACT_RET
- )
- # EXTRACT_RET is 0 even for empty files
- if(NOT EXTRACT_RET EQUAL 0 OR NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/mdk-sdk/lib/cmake/FindMDK.cmake)
- file(REMOVE ${MDK_SDK_SAVE})
- message(FATAL_ERROR "Failed to extract mdk-sdk. You can download manually from ${MDK_SDK_URL} and extract to ${CMAKE_CURRENT_SOURCE_DIR}")
- endif()
- endif()
- include(${CMAKE_CURRENT_SOURCE_DIR}/mdk-sdk/lib/cmake/FindMDK.cmake)
+ _fvp_prepare_mdk_sdk(
+ "${MDK_SDK_URL}"
+ "${MDK_SDK_SAVE}"
+ "${MDK_SDK_EXTRACT_DIR}"
+ "${FVP_DEPS_SHA256_EFFECTIVE}"
+ "${UPDATE_MDK_SDK_LATEST}"
+ )
+ include("${CMAKE_CURRENT_SOURCE_DIR}/mdk-sdk/lib/cmake/FindMDK.cmake")
endmacro()
diff --git a/cmake/tests/CMakeLists.txt b/cmake/tests/CMakeLists.txt
new file mode 100644
index 0000000..a8fd84f
--- /dev/null
+++ b/cmake/tests/CMakeLists.txt
@@ -0,0 +1,92 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(fvp_deps_tests NONE)
+
+enable_testing()
+
+function(create_mdk_fixture NAME PAYLOAD OUT_ARCHIVE OUT_SHA256)
+ set(FIXTURE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/fixtures/${NAME}")
+ set(FIXTURE_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/fixtures/${NAME}.tar")
+ file(REMOVE_RECURSE "${FIXTURE_ROOT}")
+ file(MAKE_DIRECTORY "${FIXTURE_ROOT}/mdk-sdk/lib/cmake")
+ file(WRITE "${FIXTURE_ROOT}/mdk-sdk/lib/cmake/FindMDK.cmake"
+ "set(FVP_TEST_FIND_MDK TRUE)\n"
+ )
+ file(WRITE "${FIXTURE_ROOT}/mdk-sdk/payload.txt" "${PAYLOAD}\n")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E tar "cf" "${FIXTURE_ARCHIVE}" "--format=gnutar" "mdk-sdk"
+ WORKING_DIRECTORY "${FIXTURE_ROOT}"
+ RESULT_VARIABLE ARCHIVE_RESULT
+ ERROR_VARIABLE ARCHIVE_ERROR
+ )
+ if(NOT ARCHIVE_RESULT EQUAL 0)
+ message(FATAL_ERROR "Failed to create ${NAME} fixture: ${ARCHIVE_ERROR}")
+ endif()
+ file(SHA256 "${FIXTURE_ARCHIVE}" FIXTURE_SHA256)
+ file(MD5 "${FIXTURE_ARCHIVE}" FIXTURE_MD5)
+ file(WRITE "${FIXTURE_ARCHIVE}.md5" "${FIXTURE_MD5}\n")
+ set(${OUT_ARCHIVE} "${FIXTURE_ARCHIVE}" PARENT_SCOPE)
+ set(${OUT_SHA256} "${FIXTURE_SHA256}" PARENT_SCOPE)
+endfunction()
+
+create_mdk_fixture("v1" "fixture-v1" FIXTURE_V1_ARCHIVE FIXTURE_V1_SHA256)
+create_mdk_fixture("v2" "fixture-v2" FIXTURE_V2_ARCHIVE FIXTURE_V2_SHA256)
+
+if(WIN32)
+ set(FILE_URL_PREFIX "file:///")
+else()
+ set(FILE_URL_PREFIX "file://")
+endif()
+set(FIXTURE_V1_URL "${FILE_URL_PREFIX}${FIXTURE_V1_ARCHIVE}")
+set(FIXTURE_V2_URL "${FILE_URL_PREFIX}${FIXTURE_V2_ARCHIVE}")
+
+set(INTEGRATION_RELEASE_DIR "${CMAKE_CURRENT_BINARY_DIR}/integration-release")
+set(INTEGRATION_ROOT "${CMAKE_CURRENT_BINARY_DIR}/integration root")
+set(INTEGRATION_BAD_ROOT "${CMAKE_CURRENT_BINARY_DIR}/integration bad root")
+file(MAKE_DIRECTORY "${INTEGRATION_RELEASE_DIR}" "${INTEGRATION_ROOT}" "${INTEGRATION_BAD_ROOT}")
+configure_file(
+ "${FIXTURE_V1_ARCHIVE}"
+ "${INTEGRATION_RELEASE_DIR}/mdk-sdk-linux-x64.tar.xz"
+ COPYONLY
+)
+configure_file(
+ "${FIXTURE_V1_ARCHIVE}"
+ "${INTEGRATION_RELEASE_DIR}/mdk-sdk-windows.7z"
+ COPYONLY
+)
+
+add_test(
+ NAME fvp_deps
+ COMMAND "${CMAKE_COMMAND}"
+ "-DDEPS_FILE=${CMAKE_CURRENT_LIST_DIR}/../deps.cmake"
+ "-DINVOKE_FILE=${CMAKE_CURRENT_LIST_DIR}/invoke_fetch.cmake"
+ "-DTEST_ROOT=${CMAKE_CURRENT_BINARY_DIR}/test root"
+ "-DFIXTURE_V1_URL=${FIXTURE_V1_URL}"
+ "-DFIXTURE_V1_SHA256=${FIXTURE_V1_SHA256}"
+ "-DFIXTURE_V2_URL=${FIXTURE_V2_URL}"
+ "-DFIXTURE_V2_SHA256=${FIXTURE_V2_SHA256}"
+ -P "${CMAKE_CURRENT_LIST_DIR}/deps_test.cmake"
+)
+
+add_test(
+ NAME fvp_deps_integration
+ COMMAND "${CMAKE_COMMAND}"
+ "-DDEPS_FILE=${CMAKE_CURRENT_LIST_DIR}/../deps.cmake"
+ "-DFVP_DEPS_URL=${FILE_URL_PREFIX}${INTEGRATION_RELEASE_DIR}"
+ "-DFVP_DEPS_SHA256=${FIXTURE_V1_SHA256}"
+ -P "${CMAKE_CURRENT_LIST_DIR}/macro_test.cmake"
+)
+set_tests_properties(fvp_deps_integration PROPERTIES
+ ENVIRONMENT "FVP_DEPS_LATEST=1"
+ WORKING_DIRECTORY "${INTEGRATION_ROOT}"
+)
+
+add_test(
+ NAME fvp_deps_integration_bad_sha
+ COMMAND "${CMAKE_COMMAND}"
+ "-DDEPS_FILE=${CMAKE_CURRENT_LIST_DIR}/../deps.cmake"
+ "-DMACRO_TEST_FILE=${CMAKE_CURRENT_LIST_DIR}/macro_test.cmake"
+ "-DSDK_BASE_URL=${FILE_URL_PREFIX}${INTEGRATION_RELEASE_DIR}"
+ "-DTEST_ROOT=${INTEGRATION_BAD_ROOT}"
+ -P "${CMAKE_CURRENT_LIST_DIR}/macro_bad_sha_test.cmake"
+)
diff --git a/cmake/tests/deps_test.cmake b/cmake/tests/deps_test.cmake
new file mode 100644
index 0000000..df422ca
--- /dev/null
+++ b/cmake/tests/deps_test.cmake
@@ -0,0 +1,238 @@
+cmake_minimum_required(VERSION 3.15)
+
+foreach(REQUIRED_VARIABLE
+ DEPS_FILE
+ INVOKE_FILE
+ TEST_ROOT
+ FIXTURE_V1_URL
+ FIXTURE_V1_SHA256
+ FIXTURE_V2_URL
+ FIXTURE_V2_SHA256)
+ if(NOT DEFINED ${REQUIRED_VARIABLE})
+ message(FATAL_ERROR "${REQUIRED_VARIABLE} is required")
+ endif()
+endforeach()
+
+include("${DEPS_FILE}")
+
+function(assert_equal ACTUAL EXPECTED DESCRIPTION)
+ if(NOT "${ACTUAL}" STREQUAL "${EXPECTED}")
+ message(FATAL_ERROR
+ "${DESCRIPTION}\n"
+ " expected: ${EXPECTED}\n"
+ " actual: ${ACTUAL}"
+ )
+ endif()
+endfunction()
+
+function(assert_exists PATH DESCRIPTION)
+ if(NOT EXISTS "${PATH}")
+ message(FATAL_ERROR "${DESCRIPTION}: ${PATH}")
+ endif()
+endfunction()
+
+function(assert_not_exists PATH DESCRIPTION)
+ if(EXISTS "${PATH}")
+ message(FATAL_ERROR "${DESCRIPTION}: ${PATH}")
+ endif()
+endfunction()
+
+function(assert_payload ROOT EXPECTED)
+ set(PAYLOAD_FILE "${ROOT}/mdk-sdk/payload.txt")
+ assert_exists("${PAYLOAD_FILE}" "MDK SDK payload is missing")
+ file(READ "${PAYLOAD_FILE}" ACTUAL_PAYLOAD)
+ string(STRIP "${ACTUAL_PAYLOAD}" ACTUAL_PAYLOAD)
+ assert_equal("${ACTUAL_PAYLOAD}" "${EXPECTED}" "Unexpected MDK SDK payload")
+endfunction()
+
+file(REMOVE_RECURSE "${TEST_ROOT}")
+file(MAKE_DIRECTORY "${TEST_ROOT}")
+
+# CMake variables take precedence over environment variables. Empty values
+# fall back to the environment and then to the default URL.
+set(ENV{FVP_DEPS_URL} "https://env.example.invalid/releases")
+string(TOUPPER "${FIXTURE_V2_SHA256}" FIXTURE_V2_SHA256_UPPER)
+set(ENV{FVP_DEPS_SHA256} "${FIXTURE_V2_SHA256_UPPER}")
+set(FVP_DEPS_URL " ")
+set(FVP_DEPS_SHA256 " ")
+_fvp_resolve_deps_options("https://default.example.invalid/" RESOLVED_URL RESOLVED_SHA256)
+assert_equal("${RESOLVED_URL}" "https://env.example.invalid/releases" "Environment URL was not used")
+assert_equal("${RESOLVED_SHA256}" "${FIXTURE_V2_SHA256}" "Environment SHA-256 was not used")
+
+set(FVP_DEPS_URL "https://cache.example.invalid/releases/")
+set(FVP_DEPS_SHA256 "${FIXTURE_V1_SHA256}")
+_fvp_resolve_deps_options("https://default.example.invalid/" RESOLVED_URL RESOLVED_SHA256)
+assert_equal("${RESOLVED_URL}" "https://cache.example.invalid/releases" "CMake URL did not take precedence")
+assert_equal("${RESOLVED_SHA256}" "${FIXTURE_V1_SHA256}" "CMake SHA-256 did not take precedence")
+
+set(ENV{FVP_DEPS_URL} "")
+set(ENV{FVP_DEPS_SHA256} "")
+set(FVP_DEPS_URL "")
+set(FVP_DEPS_SHA256 "")
+_fvp_resolve_deps_options("https://default.example.invalid/" RESOLVED_URL RESOLVED_SHA256)
+assert_equal("${RESOLVED_URL}" "https://default.example.invalid" "Default URL was not used")
+assert_equal("${RESOLVED_SHA256}" "" "Default SHA-256 must be empty")
+
+set(ENV{FVP_DEPS_URL} "file:///legacy-environment-value-is-ignored")
+_fvp_resolve_deps_options("https://default.example.invalid/" RESOLVED_URL RESOLVED_SHA256)
+assert_equal("${RESOLVED_URL}" "https://default.example.invalid" "Non-HTTP environment URL behavior changed")
+set(ENV{FVP_DEPS_URL} "")
+
+# A valid pin downloads, verifies, and stamps the extracted SDK.
+set(PINNED_ROOT "${TEST_ROOT}/pinned cache")
+set(PINNED_ARCHIVE "${PINNED_ROOT}/mdk-sdk.tar")
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V1_URL}"
+ "${PINNED_ARCHIVE}"
+ "${PINNED_ROOT}"
+ "${FIXTURE_V1_SHA256}"
+ OFF
+)
+assert_payload("${PINNED_ROOT}" "fixture-v1")
+file(READ "${PINNED_ROOT}/mdk-sdk/.fvp-deps.sha256" PINNED_STAMP)
+string(STRIP "${PINNED_STAMP}" PINNED_STAMP)
+assert_equal("${PINNED_STAMP}" "${FIXTURE_V1_SHA256}" "Incorrect SHA-256 stamp")
+
+# A matching stamp can be reused offline without the downloaded archive.
+file(WRITE "${PINNED_ROOT}/mdk-sdk/local-marker.txt" "keep\n")
+file(REMOVE "${PINNED_ARCHIVE}")
+_fvp_prepare_mdk_sdk(
+ "file:///does/not/exist/mdk-sdk.tar"
+ "${PINNED_ARCHIVE}"
+ "${PINNED_ROOT}"
+ "${FIXTURE_V1_SHA256}"
+ OFF
+)
+assert_exists("${PINNED_ROOT}/mdk-sdk/local-marker.txt" "Pinned cache was unexpectedly re-extracted")
+
+# Changing the pin downloads and replaces the extracted SDK without stale files.
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V2_URL}"
+ "${PINNED_ARCHIVE}"
+ "${PINNED_ROOT}"
+ "${FIXTURE_V2_SHA256}"
+ OFF
+)
+assert_payload("${PINNED_ROOT}" "fixture-v2")
+assert_not_exists("${PINNED_ROOT}/mdk-sdk/local-marker.txt" "Stale extracted files were retained")
+
+# Recover a last-known-good SDK if a process stopped between backup and swap.
+file(RENAME "${PINNED_ROOT}/mdk-sdk" "${PINNED_ROOT}/.fvp-mdk-backup")
+_fvp_prepare_mdk_sdk(
+ "file:///does/not/exist/mdk-sdk.tar"
+ "${PINNED_ARCHIVE}"
+ "${PINNED_ROOT}"
+ "${FIXTURE_V2_SHA256}"
+ OFF
+)
+assert_payload("${PINNED_ROOT}" "fixture-v2")
+assert_not_exists("${PINNED_ROOT}/.fvp-mdk-backup" "Recovered SDK backup was not cleaned up")
+
+# A bad pin fails without replacing the last valid archive or extracted SDK.
+set(BAD_SHA256 "0000000000000000000000000000000000000000000000000000000000000000")
+execute_process(
+ COMMAND "${CMAKE_COMMAND}"
+ "-DDEPS_FILE=${DEPS_FILE}"
+ "-DSDK_URL=${FIXTURE_V1_URL}"
+ "-DSDK_ARCHIVE=${PINNED_ARCHIVE}"
+ "-DEXTRACT_ROOT=${PINNED_ROOT}"
+ "-DEXPECTED_SHA256=${BAD_SHA256}"
+ -P "${INVOKE_FILE}"
+ RESULT_VARIABLE BAD_HASH_RESULT
+ OUTPUT_VARIABLE BAD_HASH_OUTPUT
+ ERROR_VARIABLE BAD_HASH_ERROR
+)
+if(BAD_HASH_RESULT EQUAL 0)
+ message(FATAL_ERROR "An incorrect SHA-256 unexpectedly succeeded")
+endif()
+set(BAD_HASH_LOG "${BAD_HASH_OUTPUT}\n${BAD_HASH_ERROR}")
+if(NOT "${BAD_HASH_LOG}" MATCHES "SHA-256 mismatch")
+ message(FATAL_ERROR "Incorrect SHA-256 failed for an unexpected reason:\n${BAD_HASH_LOG}")
+endif()
+assert_not_exists("${PINNED_ARCHIVE}.part" "Partial download was not removed")
+file(SHA256 "${PINNED_ARCHIVE}" ARCHIVE_AFTER_FAILURE)
+assert_equal("${ARCHIVE_AFTER_FAILURE}" "${FIXTURE_V2_SHA256}" "Valid archive was replaced after hash failure")
+assert_payload("${PINNED_ROOT}" "fixture-v2")
+
+# Without a pin, the existing download and cache behavior remains available.
+set(LEGACY_ROOT "${TEST_ROOT}/legacy cache")
+set(LEGACY_ARCHIVE "${LEGACY_ROOT}/mdk-sdk.tar")
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V1_URL}"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ OFF
+)
+assert_payload("${LEGACY_ROOT}" "fixture-v1")
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V2_URL}"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ ON
+)
+assert_payload("${LEGACY_ROOT}" "fixture-v2")
+
+# Existing caches created by older fvp versions have no SHA-256 stamp. A
+# requested latest check re-extracts the matching archive to establish one.
+file(REMOVE "${LEGACY_ROOT}/mdk-sdk/.fvp-deps.sha256")
+file(WRITE "${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "remove\n")
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V2_URL}"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ ON
+)
+assert_not_exists("${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "Unstamped latest SDK was not re-extracted")
+assert_exists("${LEGACY_ROOT}/mdk-sdk/.fvp-deps.sha256" "Latest SDK stamp was not created")
+
+file(WRITE "${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "keep\n")
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V2_URL}"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ ON
+)
+assert_exists("${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "Unchanged latest SDK was unexpectedly re-extracted")
+
+# If a previous update replaced only the archive, the stamp forces extraction
+# even when the remote MD5 matches that archive.
+file(WRITE "${LEGACY_ROOT}/mdk-sdk/.fvp-deps.sha256" "${FIXTURE_V1_SHA256}\n")
+_fvp_prepare_mdk_sdk(
+ "${FIXTURE_V2_URL}"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ ON
+)
+assert_not_exists("${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "Stale SDK survived an archive/stamp mismatch")
+file(READ "${LEGACY_ROOT}/mdk-sdk/.fvp-deps.sha256" LATEST_STAMP)
+string(STRIP "${LATEST_STAMP}" LATEST_STAMP)
+assert_equal("${LATEST_STAMP}" "${FIXTURE_V2_SHA256}" "Latest SDK stamp was not repaired")
+
+# Ordinary unpinned builds preserve the legacy cache behavior even if a
+# leftover archive no longer matches the extracted SDK.
+_fvp_download_file("${FIXTURE_V1_URL}" "${LEGACY_ARCHIVE}" "")
+file(WRITE "${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "keep\n")
+_fvp_prepare_mdk_sdk(
+ "file:///does/not/exist/mdk-sdk.tar"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ OFF
+)
+assert_exists("${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "Ordinary unpinned cache behavior changed")
+assert_payload("${LEGACY_ROOT}" "fixture-v2")
+
+file(REMOVE "${LEGACY_ARCHIVE}")
+_fvp_prepare_mdk_sdk(
+ "file:///does/not/exist/mdk-sdk.tar"
+ "${LEGACY_ARCHIVE}"
+ "${LEGACY_ROOT}"
+ ""
+ OFF
+)
+assert_exists("${LEGACY_ROOT}/mdk-sdk/local-marker.txt" "Unpinned cache behavior changed")
diff --git a/cmake/tests/invoke_fetch.cmake b/cmake/tests/invoke_fetch.cmake
new file mode 100644
index 0000000..2666ec9
--- /dev/null
+++ b/cmake/tests/invoke_fetch.cmake
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.15)
+
+foreach(REQUIRED_VARIABLE DEPS_FILE SDK_URL SDK_ARCHIVE EXTRACT_ROOT EXPECTED_SHA256)
+ if(NOT DEFINED ${REQUIRED_VARIABLE})
+ message(FATAL_ERROR "${REQUIRED_VARIABLE} is required")
+ endif()
+endforeach()
+
+include("${DEPS_FILE}")
+_fvp_prepare_mdk_sdk(
+ "${SDK_URL}"
+ "${SDK_ARCHIVE}"
+ "${EXTRACT_ROOT}"
+ "${EXPECTED_SHA256}"
+ OFF
+)
diff --git a/cmake/tests/macro_bad_sha_test.cmake b/cmake/tests/macro_bad_sha_test.cmake
new file mode 100644
index 0000000..dc3b173
--- /dev/null
+++ b/cmake/tests/macro_bad_sha_test.cmake
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.15)
+
+foreach(REQUIRED_VARIABLE DEPS_FILE MACRO_TEST_FILE SDK_BASE_URL TEST_ROOT)
+ if(NOT DEFINED ${REQUIRED_VARIABLE})
+ message(FATAL_ERROR "${REQUIRED_VARIABLE} is required")
+ endif()
+endforeach()
+
+set(BAD_SHA256 "0000000000000000000000000000000000000000000000000000000000000000")
+file(REMOVE_RECURSE "${TEST_ROOT}")
+file(MAKE_DIRECTORY "${TEST_ROOT}")
+execute_process(
+ COMMAND "${CMAKE_COMMAND}"
+ "-DDEPS_FILE=${DEPS_FILE}"
+ "-DFVP_DEPS_URL=${SDK_BASE_URL}"
+ "-DFVP_DEPS_SHA256=${BAD_SHA256}"
+ -P "${MACRO_TEST_FILE}"
+ WORKING_DIRECTORY "${TEST_ROOT}"
+ RESULT_VARIABLE BAD_HASH_RESULT
+ OUTPUT_VARIABLE BAD_HASH_OUTPUT
+ ERROR_VARIABLE BAD_HASH_ERROR
+)
+if(BAD_HASH_RESULT EQUAL 0)
+ message(FATAL_ERROR "fvp_setup_deps() accepted an incorrect SHA-256")
+endif()
+set(BAD_HASH_LOG "${BAD_HASH_OUTPUT}\n${BAD_HASH_ERROR}")
+if(NOT "${BAD_HASH_LOG}" MATCHES "SHA-256 mismatch")
+ message(FATAL_ERROR "Incorrect SHA-256 failed for an unexpected reason:\n${BAD_HASH_LOG}")
+endif()
+file(GLOB PART_FILES "${TEST_ROOT}/*.part")
+if(PART_FILES)
+ message(FATAL_ERROR "Partial macro integration download was not removed: ${PART_FILES}")
+endif()
diff --git a/cmake/tests/macro_test.cmake b/cmake/tests/macro_test.cmake
new file mode 100644
index 0000000..9f6f7a9
--- /dev/null
+++ b/cmake/tests/macro_test.cmake
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.15)
+
+if(NOT DEFINED DEPS_FILE)
+ message(FATAL_ERROR "DEPS_FILE is required")
+endif()
+
+set(CMAKE_SYSTEM_NAME "Linux")
+set(CMAKE_SYSTEM_PROCESSOR "x86_64")
+include("${DEPS_FILE}")
+
+# The dependency integration test runs outside the plugin source tree. Avoid
+# generating version.h; fvp_version() is independent from dependency setup.
+function(fvp_version)
+endfunction()
+
+fvp_setup_deps()
+
+if(NOT FVP_TEST_FIND_MDK)
+ message(FATAL_ERROR "fvp_setup_deps() did not include FindMDK.cmake")
+endif()
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/Classes/FvpPlugin.mm b/darwin/Classes/FvpPlugin.mm
index 21c9395..d530d1c 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,12 +130,17 @@ + (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];
+#if TARGET_OS_OSX
+#else
+ [registrar addApplicationDelegate:instance];
+#endif
[registrar publish:instance];
[registrar addMethodCallDelegate:instance channel:channel];
- SetGlobalOption("MDK_KEY", "C03BFF5306AB39058A767105F82697F42A00FE970FB0E641D306DEFF3F220547E5E5377A3C504DC30D547890E71059BC023A4DD91A95474D1F33CA4C26C81B0FC73B00ACF954C6FA75898EFA07D9680B6A00FDF179C0A15381101D01124498AF55B069BD4B0156D5CF5A56DEDE782E5F3930AD47C8F40BFBA379231142E31B0F");
}
- (instancetype)initWithRegistrar:(NSObject*)registrar {
@@ -181,4 +186,11 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
- (void)detachFromEngineForRegistrar:(NSObject *)registrar {
players.clear();
}
+
+#if TARGET_OS_OSX
+#else
+- (void)applicationWillTerminate:(UIApplication *)application {
+ players.clear();
+}
+#endif
@end
diff --git a/darwin/fvp.podspec b/darwin/fvp.podspec
index d0faf60..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.27.0'
+ s.version = '0.36.1'
s.summary = 'libmdk based Flutter video player plugin'
s.description = <<-DESC
Flutter video player plugin.
@@ -25,10 +25,14 @@ 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.36.0'
# 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'
+ 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/darwin/fvp/Package.swift b/darwin/fvp/Package.swift
new file mode 100644
index 0000000..005870d
--- /dev/null
+++ b/darwin/fvp/Package.swift
@@ -0,0 +1,54 @@
+// 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", type: .dynamic, 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("Flutter", .when(platforms: [.iOS])),
+ .linkedFramework("FlutterMacOS", .when(platforms: [.macOS])),
+ .linkedFramework("AVFoundation"),
+ .linkedFramework("CoreVideo"),
+ .linkedFramework("Metal"),
+ ]
+ ),
+ .binaryTarget(
+ name: "mdk",
+ url: "https://github.com/wang-bin/mdk-sdk/releases/download/v0.37.0/mdk-sdk-apple.zip",
+ checksum: "1f92b5318138fdf90dfc2424c0ff751d64f705413ae0d3c7f04aa3faec49c921"
+ ),
+ ],
+ cxxLanguageStandard: .cxx20
+)
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
+
+
+
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
diff --git a/elinux/CMakeLists.txt b/elinux/CMakeLists.txt
new file mode 100644
index 0000000..91998e0
--- /dev/null
+++ b/elinux/CMakeLists.txt
@@ -0,0 +1,52 @@
+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.cc"
+ ../lib/src/callbacks.cpp
+)
+apply_standard_settings(${PLUGIN_NAME})
+set_target_properties(${PLUGIN_NAME} PROPERTIES
+ 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")
+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_PLUGINS}
+ ${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..40e36ba
--- /dev/null
+++ b/elinux/fvp_plugin.cc
@@ -0,0 +1,282 @@
+/*
+ * 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