You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(windows): address review findings on native player refactor
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
Copy file name to clipboardExpand all lines: mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/windows/WindowsVideoPlayerState.kt
+66-89Lines changed: 66 additions & 89 deletions
Original file line number
Diff line number
Diff line change
@@ -269,11 +269,12 @@ class WindowsVideoPlayerState : VideoPlayerState {
269
269
privateval videoReaderMutex =Mutex()
270
270
privateval isSeeking =AtomicBoolean(false)
271
271
272
-
// Memory optimization for frame processing
273
-
privateval frameQueueSize =1
272
+
// Frame channel: one slot, drop-oldest. With triple-buffering on the
273
+
// producer side, overflow simply means the consumer was slow — safe to
274
+
// drop. Capacity >1 would just let the pipeline pile up.
274
275
privateval frameChannel =
275
276
Channel<FrameData>(
276
-
capacity =frameQueueSize,
277
+
capacity =1,
277
278
onBufferOverflow =BufferOverflow.DROP_OLDEST,
278
279
)
279
280
@@ -283,10 +284,13 @@ class WindowsVideoPlayerState : VideoPlayerState {
283
284
valtimestamp:Double,
284
285
)
285
286
286
-
// Double-buffering for zero-copy frame rendering
287
-
privatevar skiaBitmapA:Bitmap?=null
288
-
privatevar skiaBitmapB:Bitmap?=null
289
-
privatevar nextSkiaBitmapA:Boolean=true
287
+
// Triple-buffering for zero-copy frame rendering: the consumer may still
288
+
// be driving a frame onto Compose (via currentFrameState) when the
289
+
// producer writes the next frame. Two bitmaps is racy — with three, the
290
+
// buffer the producer writes is guaranteed to be distinct from both the
291
+
// one currently bound to ImageBitmap and the one Compose just finished.
292
+
privateval skiaBitmaps = arrayOfNulls<Bitmap>(3)
293
+
privatevar nextBitmapIndex:Int=0
290
294
@Volatile
291
295
privatevar lastFrameHash:Int=Int.MIN_VALUE
292
296
privatevar skiaBitmapWidth:Int=0
@@ -359,13 +363,26 @@ class WindowsVideoPlayerState : VideoPlayerState {
359
363
// StopAudioThread + MF teardown) but guarantees a clean shutdown.
360
364
if (instance !=0L) {
361
365
if (jobToJoin !=null) {
362
-
try {
363
-
kotlinx.coroutines.runBlocking {
364
-
kotlinx.coroutines.withTimeoutOrNull(500) {
365
-
jobToJoin.join()
366
-
}
366
+
// Avoid runBlocking on the AWT Event Dispatch Thread: if any
367
+
// child coroutine of `scope` ever chains on Dispatchers.Main
368
+
// (even indirectly, e.g. Compose effects), joining here would
369
+
// deadlock. Fall back to a plain Thread.join on EDT — the
370
+
// 500 ms cap keeps the UI from hanging if the native side
371
+
// stalls.
372
+
val deadlineNs =System.nanoTime() +500_000_000L
373
+
if (java.awt.EventQueue.isDispatchThread()) {
374
+
while (jobToJoin.isActive &&System.nanoTime() < deadlineNs) {
0 commit comments