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 index 0a3645f62..894e09117 100644 --- 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 @@ -52,8 +52,8 @@ endfunction() 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/20260702/${LIBMPV}") -set(LIBMPV_SHA "e528917a9d2ba7c7d47182ddafa15c1dc025387ae0916ed2ad8e0a108017d48d") +set(LIBMPV_URL "https://github.com/ErBWs/libmpv-ohos-build/releases/download/20260811/${LIBMPV}") +set(LIBMPV_SHA "2bfb9844a7552c450694581b315b26cdf6202eaa57724e9f70f3facfad75163b") # Download location of the libmpv archive. set(LIBMPV_ARCHIVE "${CMAKE_CURRENT_SOURCE_DIR}/${LIBMPV}") 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 106c6761e..b01393d6c 100644 --- a/media_kit/lib/src/player/native/core/native_library.dart +++ b/media_kit/lib/src/player/native/core/native_library.dart @@ -65,6 +65,7 @@ abstract class NativeLibrary { 'libmpv.so', ], 'ohos': [ + 'libmpv.so', 'libmpv.so.2', ], }[Platform.operatingSystem]; @@ -92,7 +93,7 @@ abstract class NativeLibrary { '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.', + 'Cannot find libmpv. Please ensure it\'s presence in the HAP.', }[Platform.operatingSystem]!, ); } 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 index 4bc7ceba3..6d44ac81a 100644 --- 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 @@ -44,18 +44,6 @@ class OhosVideoController extends PlatformVideoController { } } - /// 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; @@ -64,7 +52,6 @@ class OhosVideoController extends PlatformVideoController { super.player, super.configuration, ) { - wid.addListener(widListener); videoParamsSubscription = player.stream.videoParams.listen( (event) => lock.synchronized(() async { final int width; @@ -157,26 +144,48 @@ class OhosVideoController extends PlatformVideoController { // Store the [VideoController] in the [_controllers]. _controllers[handle] = controller; - await _channel.invokeMethod( + final Map? data = 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', - }, + if (data == null) { + throw StateError('[OhosVideoController] failed to create video output.'); + } + + final id = (data['id'] as num).toInt(); + final wid = (data['wid'] as num).toInt(); + final rect = Rect.fromLTWH( + (data['rect']['left'] as num).toDouble(), + (data['rect']['top'] as num).toDouble(), + (data['rect']['width'] as num).toDouble(), + (data['rect']['height'] as num).toDouble(), ); - await controller.setProperties({'ohos-surface-size': '1x1'}); + controller.id.value = id; + controller.rect.value = rect; + controller.wid.value = wid; + + await controller.lock.synchronized(() async { + // MPV's HarmonyOS video output requires a valid surface ID before the + // GPU video output is initialized. + await controller.setProperty('vo', 'null'); + await controller.setProperties( + { + 'ohos-surface-size': '${rect.width.toInt()}x${rect.height.toInt()}', + 'wid': wid.toString(), + 'hwdec': configuration.hwdec!, + 'vid': 'auto', + 'force-window': 'yes', + 'sub-use-margins': 'no', + 'sub-scale-with-window': 'no', + 'osd-font': 'HarmonyOS Sans SC', + }, + ); + await controller.setProperty('vo', configuration.vo!); + }); // Return the [PlatformVideoController]. return controller; @@ -200,9 +209,6 @@ class OhosVideoController extends PlatformVideoController { /// 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); @@ -212,46 +218,13 @@ class OhosVideoController extends PlatformVideoController { 'handle': handle.toString(), }, ); + wid.dispose(); + super.dispose(); } /// 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()); - } - }, - ); + static const _channel = MethodChannel('com.alexmercerind/media_kit_video'); } 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 index 96e1bdea9..da9fe6fc9 100644 --- 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 @@ -16,8 +16,7 @@ import { AbilityAware, AbilityPluginBinding } from '@ohos/flutter_ohos'; -import { TextureUpdateCallback } from './TextureUpdateCallback'; -import { Rect, SizeData, Utils } from './Utils'; +import { Utils } from './Utils'; import { VideoOutputManager } from './VideoOutputManager'; import { common } from '@kit.AbilityKit'; @@ -33,7 +32,9 @@ export default class MediaKitVideoPlugin implements FlutterPlugin, MethodCallHan } onDetachedFromAbility(): void { - return; + this.videoOutputManager?.disposeAll(); + this.videoOutputManager = null; + this.context = null; } getUniqueClassName(): string { @@ -50,31 +51,21 @@ export default class MediaKitVideoPlugin implements FlutterPlugin, MethodCallHan if (this.channel != null) { this.channel.setMethodCallHandler(null); } + this.videoOutputManager?.disposeAll(); + this.videoOutputManager = null; + this.channel = null; + this.binding = 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); - } + if (this.videoOutputManager == null) { + result.error("unavailable", "VideoOutputManager is not attached to an ability.", null); + break; } - this.videoOutputManager?.create(handle, textureUpdateCallback); - result.success(null); + result.success(this.videoOutputManager.create(handle)); break; } case "VideoOutputManager.SetSurfaceSize": { @@ -111,4 +102,4 @@ export default class MediaKitVideoPlugin implements FlutterPlugin, MethodCallHan } } } -} \ 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 deleted file mode 100644 index ccfbaec9a..000000000 --- a/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/TextureUpdateCallback.ets +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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/VideoOutput.ets b/media_kit_video/ohos/src/main/ets/com/alexmercerind/media_kit_video/VideoOutput.ets index 44e719786..22c1a75de 100644 --- 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 @@ -8,8 +8,8 @@ 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'; +import { SizeData } from './Utils'; const TAG = "VideoOutput"; @@ -18,28 +18,36 @@ export class VideoOutput { private wid: number = 0; private w: number = 0; private h: number = 0; - private textureUpdateCallback: TextureUpdateCallback; private textureRegistry: TextureRegistry; private surfaceProducer: SurfaceTextureEntry; + private disposed: boolean = false; - constructor(textureRegistry: TextureRegistry, surfaceProducer: SurfaceTextureEntry, - textureUpdateCallback: TextureUpdateCallback) { + constructor(textureRegistry: TextureRegistry, surfaceProducer: SurfaceTextureEntry) { this.surfaceProducer = surfaceProducer; - this.textureUpdateCallback = textureUpdateCallback; this.textureRegistry = textureRegistry; - this.setSurfaceSize(0, 0, true); + this.id = this.surfaceProducer.getTextureId(); + this.wid = this.surfaceProducer.getSurfaceId(); + this.setSurfaceSize(1, 1, true); } dispose(): void { + if (this.disposed) { + return; + } + try { - this.surfaceProducer.release(); - this.onSurfaceDestroyed(); + this.textureRegistry.unregisterTexture(this.id); + this.disposed = true; + this.id = 0; + this.wid = 0; + this.w = 0; + this.h = 0; } catch (e) { Log.e(TAG, `dispose: ${e}`); } } - async setSurfaceSize(width: number, height: number, force: boolean = false): Promise { + setSurfaceSize(width: number, height: number, force: boolean = false): void { try { if (!force && this.w == width && this.h == height) { return; @@ -48,26 +56,22 @@ export class VideoOutput { 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}`); + getSizeData(handle: number): SizeData { + return { + handle: handle, + id: this.id, + wid: this.wid, + rect: { + left: 0, + top: 0, + width: this.w, + height: this.h, + }, } } - - 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 index 023e50a8f..3a02fcde5 100644 --- 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 @@ -9,8 +9,7 @@ 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'; +import { SizeData } from './Utils'; const TAG = "VideoOutputManager"; @@ -22,19 +21,20 @@ export class VideoOutputManager { this.textureRegistryReference = textureRegistryReference; } - async create(handle: number, textureUpdateCallback: TextureUpdateCallback): Promise { + create(handle: number): SizeData { 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); + new VideoOutput(this.textureRegistryReference, this.textureRegistryReference.registerTexture(textureId)); this.videoOutputs.set(handle, videoOutput); } + + return this.videoOutputs.get(handle)!.getSizeData(handle); } - async dispose(handle: number): Promise { + dispose(handle: number): void { Log.i(TAG, `com.alexmercerind.media_kit_video.VideoOutputManager.dispose: ${handle}`); if (this.videoOutputs.has(handle)) { @@ -43,11 +43,18 @@ export class VideoOutputManager { } } - async setSurfaceSize(handle: number, width: number, height: number): Promise { + disposeAll(): void { + this.videoOutputs.forEach((videoOutput: VideoOutput) => { + videoOutput.dispose(); + }); + this.videoOutputs.clear(); + } + + setSurfaceSize(handle: number, width: number, height: number): void { 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 +}