refactor(windows): rewrite native player#204
Merged
kdroidFilter merged 4 commits intomasterfrom Apr 17, 2026
Merged
Conversation
… and robust seek Large refactor of the Windows backend (native DLL + Kotlin state). Native: - Migrate all COM usage to ComPtr; wrap HANDLE/CRITICAL_SECTION in RAII helpers (ComHelpers.h, Utils) - Split responsibilities across AudioManager / MediaFoundationManager / HLSPlayer / NativeVideoPlayer - Audio becomes the timing master; video compensates via audioLatencyMs (no wall-clock drift correction, no sample dropping) - AcquireNextSample: drop late frames in a tight native loop, cache early ones, never replay stale frames - 300 ms safety timeout on the cached sample so a stalled audio clock cannot freeze the picture - Dedicated audio IMFSourceReader so video ReadSample never serialises with audio - RGB32 alpha byte forced opaque (SIMD / AVX2 with scalar fallback) - ShutdownMediaFoundation can now be forced on JVM shutdown so MF worker threads exit before DLL unload Kotlin (WindowsVideoPlayerState): - JVM shutdown hook calls ShutdownMediaFoundation to fix exit code 0x87A crash on window close - dispose() cleans up natively synchronously and joins the producer coroutine before freeing the reader - Seek coalescing via pendingSeekTarget + seekInFlight; producer keeps running across seeks (videoReaderMutex + isSeeking) - consumeFrames no longer clobbers _progress while userDragging is true (drag now commits to the right position) - processOneFrame stops closing the double-buffer bitmaps on resolution change to avoid a Skia use-after-free - Proper volume persistence per instance; graceful error reporting on loop/seek failures Fixes #139 (video freezes after seek under GraalVM native-image).
…ng resolution changes - Add deferred-close queue to safely manage bitmap lifecycle during adaptive bitrate (HLS) resolution changes. - Ensure old bitmaps are cleared only after a safe number of frames to prevent Skia use-after-free crashes. - Implement `drainPendingCloseBitmaps` to manage and clean pending bitmaps effectively. - Extend JVM buildArgs to enable custom URL protocols (`--enable-url-protocols=http,https`).
Bugs: - Guard against clock-skew underflow when computing cached-sample heldMs - Stop audio thread before the presentation clock in StopPlayback - InitWASAPI no longer takes ownership of pSourceAudioFormat; caller transfers - Triple-buffer Skia bitmaps to remove producer/Compose read race - dispose() avoids runBlocking on AWT EDT to prevent potential deadlock Performance: - SeekMedia wakes the audio thread via event before taking csAudioFeed - Underflow-safe UINT32 arithmetic on framesFree Cleanup: - HLSPlayer stored via ComPtr<HLSPlayer> instead of raw pointer - HLSPlayer frame buffer uses std::vector<BYTE> instead of new[]/delete[] - Replace 0xC00D36C4 magic with MF_E_UNSUPPORTED_BYTESTREAM_TYPE - Extract NVP_MIN/MAX_PLAYBACK_SPEED constants in NativeVideoPlayer.h - Extract startVideoPipeline() helper to dedupe producer/consumer launches - Replace goto cleanup in InitWASAPI with a RAII scope guard - GetMediaDuration returns S_FALSE when duration is absent (vs hard error) - Shutdown logs a warning when live instances remain - Skip redundant 2ch/48kHz audio fallback when already canonical - Drop the default nullptr argument on InitWASAPI - Remove dead clearAllResourcesSync path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #139.
Summary
Large refactor of the Windows backend (native DLL + Kotlin state). Cleans up a number of long-standing stability issues along the way.
Native
ComHelpers.h); no more manualRelease()/CloseHandle.AudioManager,MediaFoundationManager,HLSPlayer,NativeVideoPlayer.audioLatencyMs; the old wall-clock drift correction / sleep / sample dropping is gone.AcquireNextSampledrops late frames in a tight native loop and caches early ones — it never replays stale frames.IMFSourceReader— videoReadSampleno longer serialises with audio.MFVideoFormat_RGB32alpha byte forced opaque with AVX2 (scalar fallback) to avoid washed-out colours in Skia compositing.ShutdownMediaFoundationcan now be forced on JVM shutdown so MF worker threads exit before the DLL is unloaded.Kotlin (
WindowsVideoPlayerState)ShutdownMediaFoundation()— fixes a0x87AKERNELBASE crash on window close.dispose()runs native cleanup synchronously and joins the producer coroutine before freeing the reader (no more UAF race withpSourceReader.Reset()).pendingSeekTarget+seekInFlight. The producer stays alive across seeks (serialised withvideoReaderMutex+isSeeking).consumeFramesno longer clobbers_progresswhileuserDraggingis true, so dragging the slider commits the seek where the user released.processOneFramestops closing the double-buffer bitmaps on resolution change — fixes a Skia use-after-free visible asImage.makeFromBitmapnull deref.Test plan
0x87Ahs_err