From ebad8402bc9449f5f90ac54d94338954e8138237 Mon Sep 17 00:00:00 2001 From: "Elie G." Date: Fri, 30 May 2025 08:07:47 +0300 Subject: [PATCH] Refactor WindowsVideoPlayerState initialization logic Renamed and simplified Media Foundation initialization methods for better clarity. Removed redundant `isInitialized` flag and introduced `readyForPlayback` check to ensure readiness for playback. Improved code structure and comments to streamline initialization and enhance maintainability. --- gradle/libs.versions.toml | 2 +- .../windows/WindowsVideoPlayerState.kt | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index de9294f7..489cc797 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ filekit = "0.10.0-beta03" gst1JavaCore = "1.4.0" kermit = "2.0.5" kotlin = "2.1.21" -agp = "8.9.2" +agp = "8.9.3" kotlinx-coroutines = "1.10.2" kotlinxBrowserWasmJs = "0.3" kotlinxDatetime = "0.6.2" diff --git a/mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/windows/WindowsVideoPlayerState.kt b/mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/windows/WindowsVideoPlayerState.kt index b2ef59e4..de864a63 100644 --- a/mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/windows/WindowsVideoPlayerState.kt +++ b/mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/windows/WindowsVideoPlayerState.kt @@ -55,7 +55,7 @@ internal val windowsLogger = Logger.withTag("WindowsVideoPlayerState") */ class WindowsVideoPlayerState : PlatformVideoPlayerState { companion object { - private val mediaFoundationInitialized = AtomicBoolean(false) + private val isMfBootstrapped = AtomicBoolean(false) /** Map to store volume settings for each player instance */ private val instanceVolumes = ConcurrentHashMap() @@ -64,8 +64,8 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { * Initialize Media Foundation only once for all instances. * This is called automatically when the class is loaded. */ - private fun initializeMediaFoundation() { - if (!mediaFoundationInitialized.getAndSet(true)) { + private fun ensureMfInitialized() { + if (!isMfBootstrapped.getAndSet(true)) { val hr = MediaFoundationLib.INSTANCE.InitMediaFoundation() if (hr < 0) { windowsLogger.e { "Media Foundation initialization failed (hr=0x${hr.toString(16)})" } @@ -75,7 +75,7 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { init { // Initialize Media Foundation when class is loaded - initializeMediaFoundation() + ensureMfInitialized() } } @@ -85,12 +85,6 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { /** Coroutine scope for all async operations */ private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) - /** Whether the player has been initialized */ - private var isInitialized by mutableStateOf(false) - - private val initReady = CompletableDeferred() - - /** Whether media has been loaded */ private var _hasMedia by mutableStateOf(false) override val hasMedia get() = _hasMedia @@ -105,6 +99,9 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { /** Video player instance handle */ private var videoPlayerInstance: Pointer? = null + /** Deferred completed when initialization is ready */ + private val initReady = CompletableDeferred() + /** Current volume level (0.0 to 1.0) */ private var _volume by mutableStateOf(1f) @@ -249,9 +246,9 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { private var lastUri: String? = null init { + // Kick off native initialization immediately scope.launch { try { - // Media Foundation is already initialized in companion object val instance = MediaFoundationLib.createInstance() if (instance == null) { setError("Failed to create video player instance") @@ -259,9 +256,8 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { } videoPlayerInstance = instance - // Set initial volume for this instance + // Store default volume so that later instances inherit it instanceVolumes[instance] = _volume - isInitialized = true initReady.complete(Unit) } catch (e: Exception) { initReady.completeExceptionally(e) @@ -304,7 +300,6 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { windowsLogger.e { "Error during dispose: ${e.message}" } } finally { // Mark player as uninitialized - isInitialized = false _hasMedia = false scope.cancel() // Cancel the scope to clean up any remaining jobs } @@ -342,7 +337,6 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { } } - /** * Opens a media file or URL for playback * @@ -354,10 +348,10 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { scope.launch { try { - // si l'init est déjà terminée, initReady est complété : await() reprend aussitôt + // Wait for initialization to complete with a timeout withTimeout(10_000) { initReady.await() } - // ici l'instance native est garantie non-nulle + // Here the native instance is guaranteed to be non-null openUriInternal(uri) } catch (_: TimeoutCancellationException) { setError("Player initialization timed out after 10 s.") @@ -707,12 +701,12 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { * If no media is loaded but a previous URI exists, it will try to open and play it */ override fun play() { - if (!isInitialized || videoPlayerInstance == null || !_hasMedia) { + if (!readyForPlayback()) { lastUri?.takeIf { it.isNotEmpty() }?.let { uri -> scope.launch { openUri(uri) delay(100) - if (isInitialized && videoPlayerInstance != null) { + if (readyForPlayback()) { executeMediaOperation( operation = "play after init", precondition = true @@ -986,6 +980,15 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { return false } + /** + * Checks if the player is ready for playback + * + * @return True if the player is initialized and has media loaded, false otherwise + */ + private fun readyForPlayback(): Boolean { + return initReady.isCompleted && videoPlayerInstance != null && _hasMedia + } + /** * Executes a media operation with proper error handling and mutex locking * @@ -1035,4 +1038,4 @@ class WindowsVideoPlayerState : PlatformVideoPlayerState { override fun toggleFullscreen() { isFullscreen = !isFullscreen } -} +} \ No newline at end of file