Skip to content

Migrate macOS player from JNA to JNI with output scaling and performance improvements#184

Merged
kdroidFilter merged 3 commits into
masterfrom
feature/mac-jni-migration-and-output-scaling
Apr 10, 2026
Merged

Migrate macOS player from JNA to JNI with output scaling and performance improvements#184
kdroidFilter merged 3 commits into
masterfrom
feature/mac-jni-migration-and-output-scaling

Conversation

@kdroidFilter

Copy link
Copy Markdown
Owner

Summary

  • JNA → JNI: Replace JNA native bridge with a thin C JNI bridge (jni_bridge.c) that uses JNI_OnLoad / RegisterNatives to bind Swift @_cdecl exports to Kotlin external declarations; library is loaded via System.load() from bundled resources
  • Output scaling: Add setOutputSize() in Swift — recreates AVPlayerItemVideoOutput at the display surface dimensions, significantly reducing memory for high-resolution (4K+) video by decoding at render size instead of native resolution; MacVideoPlayerSurface reports its size via onSizeChanged with a 120 ms debounce
  • KVO observer fix: Move timeControlStatus observer from the HLS-only path (setupHLSMonitoring) to setupVideoOutputAndPlayer so it covers all media types, not just HLS streams
  • Serial frame dispatcher: Replace Mutex-based frame serialization with Dispatchers.Default.limitedParallelism(1) — more idiomatic and removes the need for withLock in hot paths
  • Remove frame hash: Drop the redundant per-frame hash computation; the serial dispatcher already prevents processing duplicate frames
  • URI path fix: checkExistsIfLocalFile now uses File() directly instead of URI.create(), supporting paths with spaces and non-ASCII characters (e.g. accented filenames)
  • Build script: Update build.sh to compile jni_bridge.c with JNI headers (-I${JAVA_HOME}/include) and link the resulting .o into the dylib alongside the Swift source

Test plan

  • Run mediaplayer/src/jvmMain/native/macos/build.sh and confirm libNativeVideoPlayer.dylib builds for both arm64 and x86_64
  • Play a local file — verify video renders correctly
  • Play an HLS stream — verify isPlaying / isLoading state transitions correctly (KVO fix)
  • Play a 4K video — confirm memory usage is lower than before (output scaling)
  • Resize the player window — confirm native layer re-scales output after debounce
  • Open a file with spaces or accented characters in its name — no "File not found" error
  • Seek, pause, and resume — no regressions

…rovements

- Replace JNA native bridge with a C JNI bridge (jni_bridge.c) and JNI_OnLoad registration
- Load libNativeVideoPlayer.dylib via System.load() from bundled resources
- Add setOutputSize() to Swift layer: recreates AVPlayerItemVideoOutput at display size,
  reducing memory usage for high-resolution video (never upscales)
- Track surface dimensions via onSizeChanged in MacVideoPlayerSurface; debounce resize
  events before applying output scaling to the native layer
- Move timeControlStatus KVO observer from HLS-only path to cover all media types
- Replace Mutex-based frame serialization with limitedParallelism(1) dispatcher
- Remove redundant frame hash computation — serial dispatcher already prevents duplicate work
- Fix checkExistsIfLocalFile to use File() directly instead of URI.create(), supporting
  paths with spaces and non-ASCII characters
- Update build.sh to compile jni_bridge.c with JNI headers and link into the dylib
…k callback

- Replace Kotlin Mutex + var playerPtr with AtomicLong: removes lock overhead from
  every JNI call in the frame hot path; cleanup uses getAndSet(0L), initialization
  uses compareAndSet to safely handle concurrent calls
- Add AVPlayerItemDidPlayToEndTime observer in Swift (all media types, not just HLS):
  sets a didPlayToEnd flag consumed once via nConsumeDidPlayToEnd() JNI call
- Replace the fragile "current >= duration - 0.5" position check in checkLoopingAsync
  with the native end-of-playback callback; keep position check as fallback
- Add consumeDidPlayToEnd @_cdecl export, jni_ConsumeDidPlayToEnd in jni_bridge.c,
  and nConsumeDidPlayToEnd external declaration in AvPlayerLib.kt
Refactor player state handling and JNI bridge implementation across
Kotlin, Swift, and C layers for improved maintainability.
@kdroidFilter kdroidFilter merged commit de423fc into master Apr 10, 2026
2 of 3 checks passed
@kdroidFilter kdroidFilter deleted the feature/mac-jni-migration-and-output-scaling branch April 11, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant