From b9dc150a54df759db4e266431c7720ce7b0aba65 Mon Sep 17 00:00:00 2001 From: ErBW_s Date: Mon, 9 Feb 2026 17:08:52 +0800 Subject: [PATCH] feat: add ohos support (#2) * add ohos libs * add platform support * chore: gitignore * remove sign * fail to load dynamic library * Create VideoOutput.ets * maybe useless * clean lookup * texture is not working * fix ohos texture * work * add cmake download script * clean test code * fix: cannot use wakelock on ohos * fix: another try to fix wakelock --------- Co-authored-by: Aloereed --- libs/ohos/media_kit_libs_ohos/.gitignore | 47 ++++ libs/ohos/media_kit_libs_ohos/.metadata | 30 ++ libs/ohos/media_kit_libs_ohos/CHANGELOG.md | 3 + libs/ohos/media_kit_libs_ohos/README.md | 11 + .../media_kit_libs_ohos/analysis_options.yaml | 28 ++ .../media_kit_libs_ohos/example/README.md | 10 + libs/ohos/media_kit_libs_ohos/ohos/.gitignore | 10 + .../ohos/build-profile.json5 | 14 + .../media_kit_libs_ohos/ohos/hvigorfile.ts | 6 + libs/ohos/media_kit_libs_ohos/ohos/index.ets | 2 + .../media_kit_libs_ohos/ohos/oh-package.json5 | 8 + .../ohos/src/main/cpp/CMakeLists.txt | 88 ++++++ .../src/main/ets/MediaKitLibsOhosPlugin.ets | 16 ++ .../ohos/src/main/module.json5 | 13 + libs/ohos/media_kit_libs_ohos/pubspec.yaml | 24 ++ .../media_kit_libs_video/pubspec.yaml | 10 +- .../pubspec_overrides.yaml | 2 + media_kit/lib/src/media_kit.dart | 2 + .../native/core/execmem_restriction.dart | 2 +- .../player/native/core/native_library.dart | 5 + .../src/player/native/utils/asset_loader.dart | 12 + .../src/player/native/utils/temp_file.dart | 2 + media_kit/lib/src/player/player.dart | 2 + media_kit_test/ohos/.gitignore | 20 ++ media_kit_test/ohos/AppScope/app.json5 | 10 + .../resources/base/element/string.json | 8 + .../resources/base/media/app_icon.png | Bin 0 -> 6790 bytes media_kit_test/ohos/build-profile.json5 | 38 +++ media_kit_test/ohos/entry/.gitignore | 7 + media_kit_test/ohos/entry/build-profile.json5 | 15 + media_kit_test/ohos/entry/hvigorfile.ts | 7 + media_kit_test/ohos/entry/oh-package.json5 | 11 + .../main/ets/entryability/EntryAbility.ets | 10 + .../ohos/entry/src/main/ets/pages/Index.ets | 24 ++ .../ohos/entry/src/main/module.json5 | 46 ++++ .../main/resources/base/element/color.json | 8 + .../main/resources/base/element/string.json | 16 ++ .../src/main/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/main_pages.json | 5 + .../main/resources/rawfile/buildinfo.json5 | 8 + .../ohos/hvigor/hvigor-config.json5 | 6 + media_kit_test/ohos/hvigorconfig.ts | 4 + media_kit_test/ohos/hvigorfile.ts | 8 + media_kit_test/ohos/oh-package.json5 | 13 + media_kit_test/pubspec_overrides.yaml | 8 + .../src/controls/adaptive.dart | 4 + .../lib/src/video/video_texture.dart | 11 +- .../ohos_video_controller.dart | 6 + .../ohos_video_controller/real.dart | 257 ++++++++++++++++++ .../ohos_video_controller/stub.dart | 28 ++ .../platform_video_controller.dart | 3 +- .../video_controller/video_controller.dart | 8 + media_kit_video/ohos/.gitignore | 10 + media_kit_video/ohos/build-profile.json5 | 10 + media_kit_video/ohos/hvigorfile.ts | 7 + media_kit_video/ohos/index.ets | 2 + media_kit_video/ohos/oh-package.json5 | 9 + .../media_kit_video/MediaKitVideoPlugin.ets | 114 ++++++++ .../media_kit_video/TextureUpdateCallback.ets | 11 + .../alexmercerind/media_kit_video/Utils.ets | 61 +++++ .../media_kit_video/VideoOutput.ets | 73 +++++ .../media_kit_video/VideoOutputManager.ets | 53 ++++ media_kit_video/ohos/src/main/module.json5 | 13 + media_kit_video/pubspec.yaml | 4 + 64 files changed, 1295 insertions(+), 8 deletions(-) create mode 100644 libs/ohos/media_kit_libs_ohos/.gitignore create mode 100644 libs/ohos/media_kit_libs_ohos/.metadata create mode 100644 libs/ohos/media_kit_libs_ohos/CHANGELOG.md create mode 100644 libs/ohos/media_kit_libs_ohos/README.md create mode 100644 libs/ohos/media_kit_libs_ohos/analysis_options.yaml create mode 100644 libs/ohos/media_kit_libs_ohos/example/README.md create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/.gitignore create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/build-profile.json5 create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/hvigorfile.ts create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/index.ets create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/oh-package.json5 create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/src/main/cpp/CMakeLists.txt create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/src/main/ets/MediaKitLibsOhosPlugin.ets create mode 100644 libs/ohos/media_kit_libs_ohos/ohos/src/main/module.json5 create mode 100644 libs/ohos/media_kit_libs_ohos/pubspec.yaml create mode 100644 media_kit_test/ohos/.gitignore create mode 100644 media_kit_test/ohos/AppScope/app.json5 create mode 100644 media_kit_test/ohos/AppScope/resources/base/element/string.json create mode 100644 media_kit_test/ohos/AppScope/resources/base/media/app_icon.png create mode 100644 media_kit_test/ohos/build-profile.json5 create mode 100644 media_kit_test/ohos/entry/.gitignore create mode 100644 media_kit_test/ohos/entry/build-profile.json5 create mode 100644 media_kit_test/ohos/entry/hvigorfile.ts create mode 100644 media_kit_test/ohos/entry/oh-package.json5 create mode 100644 media_kit_test/ohos/entry/src/main/ets/entryability/EntryAbility.ets create mode 100644 media_kit_test/ohos/entry/src/main/ets/pages/Index.ets create mode 100644 media_kit_test/ohos/entry/src/main/module.json5 create mode 100644 media_kit_test/ohos/entry/src/main/resources/base/element/color.json create mode 100644 media_kit_test/ohos/entry/src/main/resources/base/element/string.json create mode 100644 media_kit_test/ohos/entry/src/main/resources/base/media/icon.png create mode 100644 media_kit_test/ohos/entry/src/main/resources/base/profile/main_pages.json create mode 100644 media_kit_test/ohos/entry/src/main/resources/rawfile/buildinfo.json5 create mode 100644 media_kit_test/ohos/hvigor/hvigor-config.json5 create mode 100644 media_kit_test/ohos/hvigorconfig.ts create mode 100644 media_kit_test/ohos/hvigorfile.ts create mode 100644 media_kit_test/ohos/oh-package.json5 create mode 100644 media_kit_video/lib/src/video_controller/ohos_video_controller/ohos_video_controller.dart create mode 100644 media_kit_video/lib/src/video_controller/ohos_video_controller/real.dart create mode 100644 media_kit_video/lib/src/video_controller/ohos_video_controller/stub.dart create mode 100644 media_kit_video/ohos/.gitignore create mode 100644 media_kit_video/ohos/build-profile.json5 create mode 100644 media_kit_video/ohos/hvigorfile.ts create mode 100644 media_kit_video/ohos/index.ets create mode 100644 media_kit_video/ohos/oh-package.json5 create mode 100644 media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/MediaKitVideoPlugin.ets create mode 100644 media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/TextureUpdateCallback.ets create mode 100644 media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/Utils.ets create mode 100644 media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutput.ets create mode 100644 media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutputManager.ets create mode 100644 media_kit_video/ohos/src/main/module.json5 diff --git a/libs/ohos/media_kit_libs_ohos/.gitignore b/libs/ohos/media_kit_libs_ohos/.gitignore new file mode 100644 index 000000000..69c1c2bf8 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test diff --git a/libs/ohos/media_kit_libs_ohos/.metadata b/libs/ohos/media_kit_libs_ohos/.metadata new file mode 100644 index 000000000..0e977a0dc --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "aed8f5e4a84a99c5832fedb8ea11bf030644c8cb" + channel: "[user-branch]" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: aed8f5e4a84a99c5832fedb8ea11bf030644c8cb + base_revision: aed8f5e4a84a99c5832fedb8ea11bf030644c8cb + - platform: ohos + create_revision: aed8f5e4a84a99c5832fedb8ea11bf030644c8cb + base_revision: aed8f5e4a84a99c5832fedb8ea11bf030644c8cb + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/libs/ohos/media_kit_libs_ohos/CHANGELOG.md b/libs/ohos/media_kit_libs_ohos/CHANGELOG.md new file mode 100644 index 000000000..275d71fc1 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial release diff --git a/libs/ohos/media_kit_libs_ohos/README.md b/libs/ohos/media_kit_libs_ohos/README.md new file mode 100644 index 000000000..7aa830891 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/README.md @@ -0,0 +1,11 @@ +# [package:media_kit_libs_ohos](https://github.com/media-kit/media-kit) + +[![](https://img.shields.io/discord/1079685977523617792?color=33cd57&label=Discord&logo=discord&logoColor=discord)](https://discord.gg/h7qf2R9n57) [![Github Actions](https://github.com/media-kit/media-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/media-kit/media-kit/actions/workflows/ci.yml) + +GNU/Linux dependency package for [`package:media_kit`](https://github.com/media-kit/media-kit). Necessary for initialization. + +## License + +Copyright © 2021 & onwards, Hitesh Kumar Saini <> + +This project & the work under this repository is governed by MIT license that can be found in the [LICENSE](./LICENSE) file. diff --git a/libs/ohos/media_kit_libs_ohos/analysis_options.yaml b/libs/ohos/media_kit_libs_ohos/analysis_options.yaml new file mode 100644 index 000000000..0d2902135 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/libs/ohos/media_kit_libs_ohos/example/README.md b/libs/ohos/media_kit_libs_ohos/example/README.md new file mode 100644 index 000000000..8a36ae8b4 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/example/README.md @@ -0,0 +1,10 @@ +## Installation + +Add in your `pubspec.yaml`: + +```yaml +dependencies: + media_kit_libs_ohos: ^1.0.0 +``` + +This will automatically invoke the necessary initialization code. diff --git a/libs/ohos/media_kit_libs_ohos/ohos/.gitignore b/libs/ohos/media_kit_libs_ohos/ohos/.gitignore new file mode 100644 index 000000000..ea50ce406 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/.gitignore @@ -0,0 +1,10 @@ +/node_modules +/oh_modules +/local.properties +/.preview +/.idea +/build +/.cxx +/.test +**/BuildProfile.ets +**/oh-package-lock.json5 diff --git a/libs/ohos/media_kit_libs_ohos/ohos/build-profile.json5 b/libs/ohos/media_kit_libs_ohos/ohos/build-profile.json5 new file mode 100644 index 000000000..efb9d6e57 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/build-profile.json5 @@ -0,0 +1,14 @@ +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "abiFilters": ["arm64-v8a"], + } + }, + "targets": [ + { + "name": "default" + } + ] +} diff --git a/libs/ohos/media_kit_libs_ohos/ohos/hvigorfile.ts b/libs/ohos/media_kit_libs_ohos/ohos/hvigorfile.ts new file mode 100644 index 000000000..421870714 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/libs/ohos/media_kit_libs_ohos/ohos/index.ets b/libs/ohos/media_kit_libs_ohos/ohos/index.ets new file mode 100644 index 000000000..2dfb19ba5 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/index.ets @@ -0,0 +1,2 @@ +import MediaKitLibsOhosPlugin from "./src/main/ets/MediaKitLibsOhosPlugin"; +export default MediaKitLibsOhosPlugin; diff --git a/libs/ohos/media_kit_libs_ohos/ohos/oh-package.json5 b/libs/ohos/media_kit_libs_ohos/ohos/oh-package.json5 new file mode 100644 index 000000000..b347a84b3 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/oh-package.json5 @@ -0,0 +1,8 @@ +{ + "name": "media_kit_libs_ohos", + "version": "1.0.0", + "description": "Please describe the basic information.", + "author": "", + "license": "Apache-2.0", + "dependencies": {} +} diff --git a/libs/ohos/media_kit_libs_ohos/ohos/src/main/cpp/CMakeLists.txt b/libs/ohos/media_kit_libs_ohos/ohos/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..840445682 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/src/main/cpp/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.15) + +# ------------------------------------------------------------------------------ +function(download_and_verify url sha locationForArchive) + # Check if the archive exists. + if(EXISTS "${locationForArchive}") + file(SHA256 "${locationForArchive}" ARCHIVE_SHA) + + # If SHA256 doesn't match, delete the archive to download again. + if(NOT sha STREQUAL ARCHIVE_SHA) + file(REMOVE "${locationForArchive}") + message(STATUS "MD5 mismatch. File deleted.") + endif() + endif() + + # Download the archive if it doesn't exist. + if(NOT EXISTS "${locationForArchive}") + message(STATUS "Downloading archive from ${url}...") + file(DOWNLOAD "${url}" "${locationForArchive}") + message(STATUS "Downloaded archive to ${locationForArchive}.") + + # Verify SHA256 of the newly downloaded file. + file(SHA256 "${locationForArchive}" ARCHIVE_SHA) + + if(sha STREQUAL ARCHIVE_SHA) + message(STATUS "${locationForArchive} Verification successful.") + else() + message(FATAL_ERROR "${locationForArchive} Integrity check failed, please try to re-build project again.") + endif() + endif() +endfunction() + +function(check_directory_exists_and_not_empty dir result_var) + # Check if the directory exists + if(EXISTS "${dir}") + # Check if the directory is not empty + file(GLOB dir_contents "${dir}/*") + + if(dir_contents) + set(${result_var} TRUE PARENT_SCOPE) + else() + set(${result_var} FALSE PARENT_SCOPE) + message(STATUS "Directory ${dir} exists but is empty!") + endif() + else() + set(${result_var} FALSE PARENT_SCOPE) + message(STATUS "Directory ${dir} does not exist!") + endif() +endfunction() + +# libmpv archive containing the pre-built shared libraries. +set(LIBMPV "libmpv_aarch64.zip") + +# Download URL & SHA256 hash of the libmpv archive. +set(LIBMPV_URL "https://github.com/ErBWs/libmpv-ohos-build/releases/download/20251110/${LIBMPV}") +set(LIBMPV_SHA "5e9c41ef80996c6691f4d860c24278c43933511559d71683f24488a2b5bd6d7d") + +# Download location of the libmpv archive. +set(LIBMPV_ARCHIVE "${CMAKE_CURRENT_SOURCE_DIR}/${LIBMPV}") +set(LIBMPV_SRC "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/arm64-v8a/") + +download_and_verify( + ${LIBMPV_URL} + ${LIBMPV_SHA} + ${LIBMPV_ARCHIVE} +) + +check_directory_exists_and_not_empty(${LIBMPV_SRC} LIBMPV_SRC_VALID) + +# Extract the libmpv archive. +if(NOT LIBMPV_SRC_VALID) + message(STATUS "Extracting ${LIBMPV}...") + make_directory("${LIBMPV_SRC}") + add_custom_target("LIBMPV_EXTRACT" ALL) + add_custom_command( + TARGET "LIBMPV_EXTRACT" + COMMAND "${CMAKE_COMMAND}" -E tar -xf "${LIBMPV_ARCHIVE}" + WORKING_DIRECTORY "${LIBMPV_SRC}" + ) +endif() +# ------------------------------------------------------------------------------ + +project(mediakit_libs_ohos) + +set(LIBS + ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/arm64-v8a/libmpv.so.2) + +link_libraries(DART_SHARED_LIB ${LIBS}) diff --git a/libs/ohos/media_kit_libs_ohos/ohos/src/main/ets/MediaKitLibsOhosPlugin.ets b/libs/ohos/media_kit_libs_ohos/ohos/src/main/ets/MediaKitLibsOhosPlugin.ets new file mode 100644 index 000000000..7d0075029 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/src/main/ets/MediaKitLibsOhosPlugin.ets @@ -0,0 +1,16 @@ +import { + FlutterPlugin, + FlutterPluginBinding, +} from "@ohos/flutter_ohos"; + +export default class MediaKitLibsOhosPlugin implements FlutterPlugin { + getUniqueClassName(): string { + return "MediaKitLibsOhosPlugin" + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + } +} \ No newline at end of file diff --git a/libs/ohos/media_kit_libs_ohos/ohos/src/main/module.json5 b/libs/ohos/media_kit_libs_ohos/ohos/src/main/module.json5 new file mode 100644 index 000000000..8d7c10441 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/ohos/src/main/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "media_kit_libs_ohos", + "type": "har", + "deviceTypes": [ + "phone", + "tablet", + "2in1", + "car", + "tv" + ] + } +} diff --git a/libs/ohos/media_kit_libs_ohos/pubspec.yaml b/libs/ohos/media_kit_libs_ohos/pubspec.yaml new file mode 100644 index 000000000..d5a71be45 --- /dev/null +++ b/libs/ohos/media_kit_libs_ohos/pubspec.yaml @@ -0,0 +1,24 @@ +name: media_kit_libs_ohos +description: HarmonyOS dependency package for package:media_kit. Necessary for initialization. +version: 1.0.0 +homepage: https://github.com/media-kit/media-kit.git +repository: https://github.com/media-kit/media-kit.git + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + plugin: + platforms: + ohos: + pluginClass: MediaKitLibsOhosPlugin diff --git a/libs/universal/media_kit_libs_video/pubspec.yaml b/libs/universal/media_kit_libs_video/pubspec.yaml index 15555443c..0a400f344 100644 --- a/libs/universal/media_kit_libs_video/pubspec.yaml +++ b/libs/universal/media_kit_libs_video/pubspec.yaml @@ -10,13 +10,15 @@ environment: flutter: ">=3.7.0" dependencies: - media_kit_libs_android_video: + media_kit_libs_android_video: path: ../../android/media_kit_libs_android_video media_kit_libs_ios_video: path: ../../ios/media_kit_libs_ios_video - media_kit_libs_macos_video: + media_kit_libs_macos_video: path: ../../macos/media_kit_libs_macos_video - media_kit_libs_windows_video: + media_kit_libs_windows_video: path: ../../windows/media_kit_libs_windows_video - media_kit_libs_linux: + media_kit_libs_linux: path: ../../linux/media_kit_libs_linux + media_kit_libs_ohos: + path: ../../ohos/media_kit_libs_ohos diff --git a/libs/universal/media_kit_libs_video/pubspec_overrides.yaml b/libs/universal/media_kit_libs_video/pubspec_overrides.yaml index 0ac008080..9817ac404 100644 --- a/libs/universal/media_kit_libs_video/pubspec_overrides.yaml +++ b/libs/universal/media_kit_libs_video/pubspec_overrides.yaml @@ -10,3 +10,5 @@ dependency_overrides: path: ../../macos/media_kit_libs_macos_video media_kit_libs_windows_video: path: ../../windows/media_kit_libs_windows_video + media_kit_libs_ohos: + path: ../../ohos/media_kit_libs_ohos diff --git a/media_kit/lib/src/media_kit.dart b/media_kit/lib/src/media_kit.dart index 752045e50..9e6e10bc3 100644 --- a/media_kit/lib/src/media_kit.dart +++ b/media_kit/lib/src/media_kit.dart @@ -34,6 +34,8 @@ abstract class MediaKit { nativeEnsureInitialized(libmpv: libmpv); } else if (UniversalPlatform.isAndroid) { nativeEnsureInitialized(libmpv: libmpv); + } else if (UniversalPlatform.operatingSystem == 'ohos') { + nativeEnsureInitialized(libmpv: libmpv); } else if (UniversalPlatform.isWeb) { webEnsureInitialized(libmpv: libmpv); } diff --git a/media_kit/lib/src/player/native/core/execmem_restriction.dart b/media_kit/lib/src/player/native/core/execmem_restriction.dart index 32bb6a542..b766cc590 100644 --- a/media_kit/lib/src/player/native/core/execmem_restriction.dart +++ b/media_kit/lib/src/player/native/core/execmem_restriction.dart @@ -14,7 +14,7 @@ final bool isExecmemRestricted = _checkIfExecmemRestricted(); /// Checks if creating new anonymous executable memory mappings is blocked by the system. /// Only applies to Linux-based systems, since Dart doesn't use them for [NativeCallback]s on Fuchsia and Apple systems. bool _checkIfExecmemRestricted() { - if (Platform.isLinux || Platform.isAndroid) { + if (Platform.isLinux || Platform.isAndroid || Platform.operatingSystem == 'ohos') { try { final libs = DynamicLibrary.process(); final mmap = libs.lookupFunction( diff --git a/media_kit/lib/src/player/native/core/native_library.dart b/media_kit/lib/src/player/native/core/native_library.dart index 367d3d23c..106c6761e 100644 --- a/media_kit/lib/src/player/native/core/native_library.dart +++ b/media_kit/lib/src/player/native/core/native_library.dart @@ -64,6 +64,9 @@ abstract class NativeLibrary { 'android': [ 'libmpv.so', ], + 'ohos': [ + 'libmpv.so.2', + ], }[Platform.operatingSystem]; if (names != null) { // Try to load the dynamic library from the system using [DynamicLibrary.open]. @@ -88,6 +91,8 @@ abstract class NativeLibrary { 'Cannot find Mpv.framework/Mpv. Please ensure it\'s presence in the Frameworks folder of the application.', 'android': 'Cannot find libmpv.so. Please ensure it\'s presence in the APK.', + 'ohos': + 'Cannot find libmpv.so.2. Please ensure it\'s presence in the HAP.', }[Platform.operatingSystem]!, ); } diff --git a/media_kit/lib/src/player/native/utils/asset_loader.dart b/media_kit/lib/src/player/native/utils/asset_loader.dart index 56590b8ef..df9363cca 100644 --- a/media_kit/lib/src/player/native/utils/asset_loader.dart +++ b/media_kit/lib/src/player/native/utils/asset_loader.dart @@ -70,6 +70,18 @@ class AssetLoader { ), ), ); + } else if (Platform.operatingSystem == 'ohos') { + asset = path.normalize( + path.join( + path.dirname(Platform.resolvedExecutable), + '..', + '..', + 'resources', + 'rawfile', + 'flutter_assets', + key, + ), + ); } else { throw UnimplementedError( '$_kAssetScheme is not supported on ${Platform.operatingSystem}', diff --git a/media_kit/lib/src/player/native/utils/temp_file.dart b/media_kit/lib/src/player/native/utils/temp_file.dart index 6782dd2c3..af116d094 100644 --- a/media_kit/lib/src/player/native/utils/temp_file.dart +++ b/media_kit/lib/src/player/native/utils/temp_file.dart @@ -31,6 +31,8 @@ abstract class TempFile { result = Directory.systemTemp.path; } else if (Platform.isAndroid) { result = AndroidHelper.filesDir; + } else if (Platform.operatingSystem == 'ohos') { + result = Directory.systemTemp.path; } if (result != null) { return result; diff --git a/media_kit/lib/src/player/player.dart b/media_kit/lib/src/player/player.dart index 0d11ea7f0..aa950d29e 100644 --- a/media_kit/lib/src/player/player.dart +++ b/media_kit/lib/src/player/player.dart @@ -113,6 +113,8 @@ class Player { platform = NativePlayer(configuration: configuration); } else if (UniversalPlatform.isAndroid) { platform = NativePlayer(configuration: configuration); + } else if (UniversalPlatform.operatingSystem == 'ohos') { + platform = NativePlayer(configuration: configuration); } else if (UniversalPlatform.isWeb) { platform = WebPlayer(configuration: configuration); } diff --git a/media_kit_test/ohos/.gitignore b/media_kit_test/ohos/.gitignore new file mode 100644 index 000000000..e32649140 --- /dev/null +++ b/media_kit_test/ohos/.gitignore @@ -0,0 +1,20 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +**/BuildProfile.ets +**/oh-package-lock.json5 +/package.json +/package-lock.json + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/**/libapp.so +**/libs/**/libflutter.so +**/libs/**/libvmservice_snapshot.so diff --git a/media_kit_test/ohos/AppScope/app.json5 b/media_kit_test/ohos/AppScope/app.json5 new file mode 100644 index 000000000..ed0367074 --- /dev/null +++ b/media_kit_test/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.github.alexmercerind.media_kit_test", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/media_kit_test/ohos/AppScope/resources/base/element/string.json b/media_kit_test/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 000000000..160ea9251 --- /dev/null +++ b/media_kit_test/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "media_kit_test" + } + ] +} diff --git a/media_kit_test/ohos/AppScope/resources/base/media/app_icon.png b/media_kit_test/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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 with WidgetsBindingObserver { late bool _visible = (_width ?? 0) > 0 && (_height ?? 0) > 0; bool _pauseDueToPauseUponEnteringBackgroundMode = false; + // Public API: bool isFullscreen() { return media_kit_video_controls.isFullscreen(_contextNotifier.value!); @@ -483,7 +484,10 @@ Future defaultEnterNativeFullscreen() async { ), ], ); - } else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) { + } else if (Platform.isMacOS || + Platform.isWindows || + Platform.isLinux || + Platform.operatingSystem == 'ohos') { await const MethodChannel('com.alexmercerind/media_kit_video') .invokeMethod( 'Utils.EnterNativeFullscreen', @@ -510,7 +514,10 @@ Future defaultExitNativeFullscreen() async { ), ], ); - } else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) { + } else if (Platform.isMacOS || + Platform.isWindows || + Platform.isLinux || + Platform.operatingSystem == 'ohos') { await const MethodChannel('com.alexmercerind/media_kit_video') .invokeMethod( 'Utils.ExitNativeFullscreen', diff --git a/media_kit_video/lib/src/video_controller/ohos_video_controller/ohos_video_controller.dart b/media_kit_video/lib/src/video_controller/ohos_video_controller/ohos_video_controller.dart new file mode 100644 index 000000000..4b75e1328 --- /dev/null +++ b/media_kit_video/lib/src/video_controller/ohos_video_controller/ohos_video_controller.dart @@ -0,0 +1,6 @@ +/// This file is a part of media_kit (https://github.com/media-kit/media-kit). +/// +/// Copyright © 2021 & onwards, Hitesh Kumar Saini . +/// All rights reserved. +/// Use of this source code is governed by MIT license that can be found in the LICENSE file. +export 'real.dart' if (dart.library.js_interop) 'stub.dart'; diff --git a/media_kit_video/lib/src/video_controller/ohos_video_controller/real.dart b/media_kit_video/lib/src/video_controller/ohos_video_controller/real.dart new file mode 100644 index 000000000..4bc7ceba3 --- /dev/null +++ b/media_kit_video/lib/src/video_controller/ohos_video_controller/real.dart @@ -0,0 +1,257 @@ +/// This file is a part of media_kit (https://github.com/media-kit/media-kit). +/// +/// Copyright © 2021 & onwards, Hitesh Kumar Saini . +/// All rights reserved. +/// Use of this source code is governed by MIT license that can be found in the LICENSE file. +import 'dart:io'; +import 'dart:async'; +import 'dart:collection'; +import 'package:flutter/services.dart'; +import 'package:flutter/foundation.dart'; +import 'package:synchronized/synchronized.dart'; + +import 'package:media_kit/media_kit.dart'; + +import 'package:media_kit_video/src/video_controller/platform_video_controller.dart'; + +/// {@template ohos_video_controller} +/// +/// OhosVideoController +/// ---------------------- +/// +/// The [PlatformVideoController] implementation based on native C/C++ used on Ohos. +/// +/// {@endtemplate} +class OhosVideoController extends PlatformVideoController { + /// Whether [OhosVideoController] is supported on the current platform or not. + static bool get supported => Platform.operatingSystem == 'ohos'; + + /// Pointer address to the global object reference of `OHNativeWindow`. + final ValueNotifier wid = ValueNotifier(null); + + /// [Lock] used to synchronize [onLoadHooks], [onUnloadHooks] & [subscription]. + final lock = Lock(); + + NativePlayer get platform => player.platform as NativePlayer; + + Future setProperty(String key, String value) async { + await platform.setProperty(key, value, waitForInitialization: false); + } + + Future setProperties(Map properties) async { + for (final entry in properties.entries) { + await setProperty(entry.key, entry.value); + } + } + + /// Listener for updating the --wid property. + Future widListener() { + return lock.synchronized(() async { + final widValue = wid.value?.toString() ?? '0'; + await setProperties({'wid': widValue}); + // Instead of seeking to the start (Duration.zero), seek to the current playback position + // without jumping the user to the start of the media. + final currentPosition = player.state.position; + await player.seek(currentPosition); + }); + } + + /// [StreamSubscription] for listening to video [Rect]. + StreamSubscription? videoParamsSubscription; + + /// {@macro ohos_video_controller} + OhosVideoController._( + super.player, + super.configuration, + ) { + wid.addListener(widListener); + videoParamsSubscription = player.stream.videoParams.listen( + (event) => lock.synchronized(() async { + final int width; + final int height; + if (event.rotate == 0 || event.rotate == 180) { + width = event.dw ?? 0; + height = event.dh ?? 0; + } else { + // width & height are swapped for 90 or 270 degrees rotation. + width = event.dh ?? 0; + height = event.dw ?? 0; + } + + final isZero = width == 0 || height == 0; + final isSame = width == rect.value?.width.toInt() && + height == rect.value?.height.toInt(); + if (isZero || isSame) { + return; + } + + final handle = await player.handle; + + await _channel.invokeMethod( + 'VideoOutputManager.SetSurfaceSize', + { + 'handle': handle.toString(), + 'width': width.toString(), + 'height': height.toString(), + }, + ); + await setProperties({ + 'ohos-surface-size': [width, height].join('x'), + }); + + rect.value = Rect.fromLTWH( + 0.0, + 0.0, + width.toDouble(), + height.toDouble(), + ); + + if (!waitUntilFirstFrameRenderedCompleter.isCompleted) { + waitUntilFirstFrameRenderedCompleter.complete(); + } + }), + ); + } + + /// {@macro ohos_video_controller} + static Future create( + Player player, + VideoControllerConfiguration configuration, + ) async { + final bool isEmulator = await _channel.invokeMethod('Utils.IsEmulator'); + if (isEmulator) { + throw UnsupportedError( + '[VideoController] does not support emulator.' + ' ' + 'Please use actual device.', + ); + } + + Future getDefaultHwdec() async { + bool hw = configuration.enableHardwareAcceleration; + return hw ? 'auto' : 'no'; + } + + // Update [configuration] to have default values. + configuration = configuration.copyWith( + vo: configuration.vo ?? 'gpu-next', + hwdec: configuration.hwdec ?? await getDefaultHwdec(), + ); + + // Retrieve the native handle of the [Player]. + final handle = await player.handle; + // Return the existing [VideoController] if it's already created. + if (_controllers.containsKey(handle)) { + return _controllers[handle]!; + } + + // Creation: + final controller = OhosVideoController._( + player, + configuration, + ); + + // Register [_dispose] for execution upon [Player.dispose]. + player.platform?.release.add(controller._dispose); + + // Store the [VideoController] in the [_controllers]. + _controllers[handle] = controller; + + await _channel.invokeMethod( + 'VideoOutputManager.Create', + { + 'handle': handle.toString(), + }, + ); + + await controller.setProperties( + { + 'vo': configuration.vo!, + 'hwdec': configuration.hwdec!, + 'vid': 'auto', + 'force-window': 'yes', + 'sub-use-margins': 'no', + 'sub-scale-with-window': 'no', + 'osd-font': 'HarmonyOS Sans SC', + }, + ); + + await controller.setProperties({'ohos-surface-size': '1x1'}); + + // Return the [PlatformVideoController]. + return controller; + } + + /// Sets the required size of the video output. + /// This may yield substantial performance improvements if a small [width] & [height] is specified. + /// + /// Remember: + /// * “Premature optimization is the root of all evil” + /// * “With great power comes great responsibility” + @override + Future setSize({ + int? width, + int? height, + }) { + throw UnsupportedError( + '[OhosVideoController.setSize] is not available on Ohos', + ); + } + + /// Disposes the instance. Releases allocated resources back to the system. + Future _dispose() async { + super.dispose(); + wid.dispose(); + wid.removeListener(widListener); + await videoParamsSubscription?.cancel(); + final handle = await player.handle; + _controllers.remove(handle); + await _channel.invokeMethod( + 'VideoOutputManager.Dispose', + { + 'handle': handle.toString(), + }, + ); + } + + /// Currently created [OhosVideoController]s. + static final _controllers = HashMap(); + + /// [MethodChannel] for invoking platform specific native implementation. + static final _channel = + const MethodChannel('com.alexmercerind/media_kit_video') + ..setMethodCallHandler( + (MethodCall call) async { + try { + debugPrint(call.method.toString()); + debugPrint(call.arguments.toString()); + switch (call.method) { + case 'VideoOutput.Resize': + { + // Notify about updated texture ID & [Rect]. + final int handle = call.arguments['handle']; + final Rect rect = Rect.fromLTWH( + call.arguments['rect']['left'] * 1.0, + call.arguments['rect']['top'] * 1.0, + call.arguments['rect']['width'] * 1.0, + call.arguments['rect']['height'] * 1.0, + ); + final int id = call.arguments['id']; + final int wid = call.arguments['wid']; + _controllers[handle]?.rect.value = rect; + _controllers[handle]?.id.value = id; + _controllers[handle]?.wid.value = wid; + break; + } + default: + { + break; + } + } + } catch (exception, stacktrace) { + debugPrint(exception.toString()); + debugPrint(stacktrace.toString()); + } + }, + ); +} diff --git a/media_kit_video/lib/src/video_controller/ohos_video_controller/stub.dart b/media_kit_video/lib/src/video_controller/ohos_video_controller/stub.dart new file mode 100644 index 000000000..bea360759 --- /dev/null +++ b/media_kit_video/lib/src/video_controller/ohos_video_controller/stub.dart @@ -0,0 +1,28 @@ +/// This file is a part of media_kit (https://github.com/media-kit/media-kit). +/// +/// Copyright © 2021 & onwards, Hitesh Kumar Saini . +/// All rights reserved. +/// Use of this source code is governed by MIT license that can be found in the LICENSE file. +import 'package:media_kit/media_kit.dart'; + +import 'package:media_kit_video/src/video_controller/platform_video_controller.dart'; + +// Stub declaration for avoiding compilation errors on Dart JS using conditional imports. + +class OhosVideoController extends PlatformVideoController { + static const bool supported = false; + + OhosVideoController._( + super.player, + super.configuration, + ); + + static Future create( + Player player, + VideoControllerConfiguration configuration, + ) => + throw UnimplementedError(); + + @override + Future setSize({int? width, int? height}) => throw UnimplementedError(); +} diff --git a/media_kit_video/lib/src/video_controller/platform_video_controller.dart b/media_kit_video/lib/src/video_controller/platform_video_controller.dart index f3bbbc8e1..bf65b8026 100644 --- a/media_kit_video/lib/src/video_controller/platform_video_controller.dart +++ b/media_kit_video/lib/src/video_controller/platform_video_controller.dart @@ -79,12 +79,13 @@ class VideoControllerConfiguration { /// Default: Platform specific. /// * Windows, GNU/Linux, macOS & iOS: `libmpv` /// * Android: `gpu` + /// * Ohos: `gpu-next` final String? vo; /// Sets the [`--hwdec`](https://mpv.io/manual/stable/#options-hwdec) property on native backend. /// /// Default: Platform specific. - /// * Windows, GNU/Linux, macOS & iOS : `auto` + /// * Windows, GNU/Linux, macOS & iOS, Ohos : `auto` /// * Android: `auto-safe` final String? hwdec; diff --git a/media_kit_video/lib/src/video_controller/video_controller.dart b/media_kit_video/lib/src/video_controller/video_controller.dart index 9ece6388a..da15a5d8e 100644 --- a/media_kit_video/lib/src/video_controller/video_controller.dart +++ b/media_kit_video/lib/src/video_controller/video_controller.dart @@ -11,6 +11,7 @@ import 'package:media_kit_video/src/video_controller/platform_video_controller.d import 'package:media_kit_video/src/video_controller/native_video_controller/native_video_controller.dart'; import 'package:media_kit_video/src/video_controller/android_video_controller/android_video_controller.dart'; +import 'package:media_kit_video/src/video_controller/ohos_video_controller/ohos_video_controller.dart'; import 'package:media_kit_video/src/video_controller/web_video_controller/web_video_controller.dart'; /// {@template video_controller} @@ -96,6 +97,13 @@ class VideoController { ); platform.complete(result); notifier.value = result; + } else if (OhosVideoController.supported) { + final result = await OhosVideoController.create( + player, + configuration, + ); + platform.complete(result); + notifier.value = result; } else if (WebVideoController.supported) { final result = await WebVideoController.create( player, diff --git a/media_kit_video/ohos/.gitignore b/media_kit_video/ohos/.gitignore new file mode 100644 index 000000000..a653a701c --- /dev/null +++ b/media_kit_video/ohos/.gitignore @@ -0,0 +1,10 @@ +/node_modules +/oh_modules +/local.properties +/.preview +/.idea +/build +/.cxx +/.test +/BuildProfile.ets +/oh-package-lock.json5 diff --git a/media_kit_video/ohos/build-profile.json5 b/media_kit_video/ohos/build-profile.json5 new file mode 100644 index 000000000..79961f96a --- /dev/null +++ b/media_kit_video/ohos/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} diff --git a/media_kit_video/ohos/hvigorfile.ts b/media_kit_video/ohos/hvigorfile.ts new file mode 100644 index 000000000..c50b28d4b --- /dev/null +++ b/media_kit_video/ohos/hvigorfile.ts @@ -0,0 +1,7 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/media_kit_video/ohos/index.ets b/media_kit_video/ohos/index.ets new file mode 100644 index 000000000..348d53ba8 --- /dev/null +++ b/media_kit_video/ohos/index.ets @@ -0,0 +1,2 @@ +import MediaKitVideoPlugin from './src/main/ets/com/alexmercerind/media_kit_video/MediaKitVideoPlugin'; +export default MediaKitVideoPlugin; diff --git a/media_kit_video/ohos/oh-package.json5 b/media_kit_video/ohos/oh-package.json5 new file mode 100644 index 000000000..674bd90c1 --- /dev/null +++ b/media_kit_video/ohos/oh-package.json5 @@ -0,0 +1,9 @@ +{ + "name": "media_kit_video", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": {} +} diff --git a/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/MediaKitVideoPlugin.ets b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/MediaKitVideoPlugin.ets new file mode 100644 index 000000000..96e1bdea9 --- /dev/null +++ b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/MediaKitVideoPlugin.ets @@ -0,0 +1,114 @@ +/** + * This file is a part of media_kit (https://github.com/media-kit/media-kit). + * + * Copyright © 2025 & onwards, Bao Han . + * All rights reserved. + * Use of this source code is governed by MIT license that can be found in the LICENSE file. + */ + +import { + FlutterPlugin, + FlutterPluginBinding, + MethodCall, + MethodCallHandler, + MethodChannel, + MethodResult, + AbilityAware, + AbilityPluginBinding +} from '@ohos/flutter_ohos'; +import { TextureUpdateCallback } from './TextureUpdateCallback'; +import { Rect, SizeData, Utils } from './Utils'; +import { VideoOutputManager } from './VideoOutputManager'; +import { common } from '@kit.AbilityKit'; + +export default class MediaKitVideoPlugin implements FlutterPlugin, MethodCallHandler, AbilityAware { + private channel: MethodChannel | null = null; + private videoOutputManager: VideoOutputManager | null = null; + binding: FlutterPluginBinding | null = null; + context: common.UIAbilityContext | null = null; + + onAttachedToAbility(binding: AbilityPluginBinding): void { + this.videoOutputManager = new VideoOutputManager(this.binding!.getTextureRegistry()); + this.context = binding.getAbility().context; + } + + onDetachedFromAbility(): void { + return; + } + + getUniqueClassName(): string { + return "MediaKitVideoPlugin" + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.channel = new MethodChannel(binding.getBinaryMessenger(), "com.alexmercerind/media_kit_video"); + this.channel.setMethodCallHandler(this); + this.binding = binding; + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + if (this.channel != null) { + this.channel.setMethodCallHandler(null); + } + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + switch (call.method) { + case "VideoOutputManager.Create": { + const handle = parseInt(call.argument("handle")); + const textureUpdateCallback: TextureUpdateCallback = { + onTextureUpdate: (id: number, wid: number, width: number, height: number): void => { + const rect: Rect = { + left: 0, + top: 0, + width: width, + height: height, + } + const args: SizeData = { + handle: handle, + id: id, + wid: wid, + rect: rect, + } + this.channel?.invokeMethod("VideoOutput.Resize", args); + } + } + this.videoOutputManager?.create(handle, textureUpdateCallback); + result.success(null); + break; + } + case "VideoOutputManager.SetSurfaceSize": { + const handle = parseInt(call.argument("handle")); + const width = parseInt(call.argument("width")); + const height = parseInt(call.argument("height")); + this.videoOutputManager?.setSurfaceSize(handle, width, height); + result.success(null); + break; + } + case "VideoOutputManager.Dispose": { + const handle = parseInt(call.argument("handle")); + this.videoOutputManager?.dispose(handle); + result.success(null); + break; + } + case "Utils.IsEmulator": { + result.success(Utils.isEmulator()); + break; + } + case "Utils.EnterNativeFullscreen": { + Utils.enterFullScreen(this.context!); + result.success(null); + break; + } + case "Utils.ExitNativeFullscreen": { + Utils.exitFullScreen(this.context!); + result.success(null); + break; + } + default: { + result.notImplemented(); + break; + } + } + } +} \ No newline at end of file diff --git a/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/TextureUpdateCallback.ets b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/TextureUpdateCallback.ets new file mode 100644 index 000000000..ccfbaec9a --- /dev/null +++ b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/TextureUpdateCallback.ets @@ -0,0 +1,11 @@ +/** + * This file is a part of media_kit (https://github.com/media-kit/media-kit). + * + * Copyright © 2025 & onwards, Bao Han . + * All rights reserved. + * Use of this source code is governed by MIT license that can be found in the LICENSE file. + */ + +export interface TextureUpdateCallback { + onTextureUpdate: (id: number, wid: number, width: number, height: number) => void; +} \ No newline at end of file diff --git a/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/Utils.ets b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/Utils.ets new file mode 100644 index 000000000..5ad3534ed --- /dev/null +++ b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/Utils.ets @@ -0,0 +1,61 @@ +/** + * This file is a part of media_kit (https://github.com/media-kit/media-kit). + * + * Copyright © 2025 & onwards, Bao Han . + * All rights reserved. + * Use of this source code is governed by MIT license that can be found in the LICENSE file. + */ + +import deviceInfo from '@ohos.deviceInfo'; +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +export class Utils { + static isEmulator(): boolean { + if (deviceInfo.productModel == "emulator") { + return true; + } else { + return false; + } + } + + static enterFullScreen(context: common.UIAbilityContext): void { + window.getLastWindow(context).then((lastWindow) => { + if (deviceInfo.deviceType == '2in1') { + lastWindow.maximize(); + return; + } + lastWindow.enableLandscapeMultiWindow(); + lastWindow.setPreferredOrientation(window.Orientation.USER_ROTATION_LANDSCAPE); + lastWindow.setWindowSystemBarEnable([]); + }) + } + + static exitFullScreen(context: common.UIAbilityContext): void { + window.getLastWindow(context).then((lastWindow) => { + if (deviceInfo.deviceType == '2in1') { + lastWindow.recover(); + return; + } + lastWindow.disableLandscapeMultiWindow(); + if (deviceInfo.deviceType == 'phone') { + lastWindow.setPreferredOrientation(window.Orientation.USER_ROTATION_PORTRAIT); + } + lastWindow.setWindowSystemBarEnable(['status']); + }) + } +} + +export interface Rect { + left: number, + top: number, + width: number, + height: number, +} + +export interface SizeData { + handle: number, + id: number, + wid: number, + rect: Rect, +} \ No newline at end of file diff --git a/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutput.ets b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutput.ets new file mode 100644 index 000000000..44e719786 --- /dev/null +++ b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutput.ets @@ -0,0 +1,73 @@ +/** + * This file is a part of media_kit (https://github.com/media-kit/media-kit). + * + * Copyright © 2025 & onwards, Bao Han . + * All rights reserved. + * Use of this source code is governed by MIT license that can be found in the LICENSE file. + */ + +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { SurfaceTextureEntry } from '@ohos/flutter_ohos'; +import { TextureUpdateCallback } from './TextureUpdateCallback'; +import { TextureRegistry } from '@ohos/flutter_ohos'; + +const TAG = "VideoOutput"; + +export class VideoOutput { + private id: number = 0; + private wid: number = 0; + private w: number = 0; + private h: number = 0; + private textureUpdateCallback: TextureUpdateCallback; + private textureRegistry: TextureRegistry; + private surfaceProducer: SurfaceTextureEntry; + + constructor(textureRegistry: TextureRegistry, surfaceProducer: SurfaceTextureEntry, + textureUpdateCallback: TextureUpdateCallback) { + this.surfaceProducer = surfaceProducer; + this.textureUpdateCallback = textureUpdateCallback; + this.textureRegistry = textureRegistry; + this.setSurfaceSize(0, 0, true); + } + + dispose(): void { + try { + this.surfaceProducer.release(); + this.onSurfaceDestroyed(); + } catch (e) { + Log.e(TAG, `dispose: ${e}`); + } + } + + async setSurfaceSize(width: number, height: number, force: boolean = false): Promise { + try { + if (!force && this.w == width && this.h == height) { + return; + } + + this.w = width; + this.h = height; + this.textureRegistry.setTextureBufferSize(this.id, width, height); + this.onSurfaceCreated(); + } catch (e) { + Log.e(TAG, `setSurfaceSize error: ${e}`); + } + } + + private async onSurfaceCreated(): Promise { + Log.i(TAG, "onSurfaceCreated"); + try { + this.id = this.surfaceProducer.getTextureId(); + this.wid = this.surfaceProducer.getSurfaceId(); + this.textureUpdateCallback.onTextureUpdate(this.id, this.wid, this.w, this.h); + } catch (e) { + Log.e(TAG, `onSurfaceCreated error: ${e}`); + } + } + + private async onSurfaceDestroyed(): Promise { + Log.i(TAG, "onSurfaceDestroyed"); + this.textureUpdateCallback.onTextureUpdate(this.id, 0, 0, 0); + this.surfaceProducer.release(); + } +} \ No newline at end of file diff --git a/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutputManager.ets b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutputManager.ets new file mode 100644 index 000000000..023e50a8f --- /dev/null +++ b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutputManager.ets @@ -0,0 +1,53 @@ +/** + * This file is a part of media_kit (https://github.com/media-kit/media-kit). + * + * Copyright © 2025 & onwards, Bao Han . + * All rights reserved. + * Use of this source code is governed by MIT license that can be found in the LICENSE file. + */ + +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { TextureRegistry } from '@ohos/flutter_ohos'; +import { VideoOutput } from './VideoOutput'; +import { TextureUpdateCallback } from './TextureUpdateCallback'; +import { common } from '@kit.AbilityKit'; + +const TAG = "VideoOutputManager"; + +export class VideoOutputManager { + private videoOutputs: Map = new Map(); + private textureRegistryReference: TextureRegistry; + + constructor(textureRegistryReference: TextureRegistry) { + this.textureRegistryReference = textureRegistryReference; + } + + async create(handle: number, textureUpdateCallback: TextureUpdateCallback): Promise { + Log.i(TAG, `com.alexmercerind.media_kit_video.VideoOutputManager.create: ${handle}`); + + if (!this.videoOutputs.has(handle)) { + const textureId = this.textureRegistryReference.getTextureId(); + const videoOutput = + new VideoOutput(this.textureRegistryReference, this.textureRegistryReference.registerTexture(textureId), + textureUpdateCallback); + this.videoOutputs.set(handle, videoOutput); + } + } + + async dispose(handle: number): Promise { + Log.i(TAG, `com.alexmercerind.media_kit_video.VideoOutputManager.dispose: ${handle}`); + + if (this.videoOutputs.has(handle)) { + this.videoOutputs.get(handle)?.dispose(); + this.videoOutputs.delete(handle); + } + } + + async setSurfaceSize(handle: number, width: number, height: number): Promise { + Log.i(TAG, `com.alexmercerind.media_kit_video.VideoOutputManager.setSurfaceSize: ${handle} ${width} ${height}`); + + if (this.videoOutputs.has(handle)) { + this.videoOutputs.get(handle)?.setSurfaceSize(width, height); + } + } +} \ No newline at end of file diff --git a/media_kit_video/ohos/src/main/module.json5 b/media_kit_video/ohos/src/main/module.json5 new file mode 100644 index 000000000..c45552dd0 --- /dev/null +++ b/media_kit_video/ohos/src/main/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "media_kit_video", + "type": "har", + "deviceTypes": [ + "phone", + "tablet", + "2in1", + "car", + "tv" + ] + } +} diff --git a/media_kit_video/pubspec.yaml b/media_kit_video/pubspec.yaml index 806870182..75c3b13cc 100644 --- a/media_kit_video/pubspec.yaml +++ b/media_kit_video/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: synchronized: ^3.1.0 wakelock_plus: 1.3.3 + wakelock_plus_ohos: ^0.0.3 universal_platform: ^1.0.0+1 plugin_platform_interface: ^2.0.2 @@ -46,4 +47,7 @@ flutter: android: package: com.alexmercerind.media_kit_video pluginClass: MediaKitVideoPlugin + ohos: + package: com.alexmercerind.media_kit_video + pluginClass: MediaKitVideoPlugin web: