Skip to content

Commit 5f81607

Browse files
committed
fix(macos): rewrite player state to mirror Windows frame pipeline
Replace the timer-driven frame/position loops with a producer/consumer coroutine pipeline backed by a drop-oldest channel, content-hash dedup, and triple-buffered Skia bitmaps to fix tearing and stalls. Drive the timeline from a dedicated AVPlayer clock poll since AVFoundation reuses CVPixelBuffers and frame-derived position freezes on low-motion content. Honor row padding in calculateFrameHash so true pixels are sampled, and correct display aspect ratio for anamorphic content.
1 parent d8dc923 commit 5f81607

2 files changed

Lines changed: 1091 additions & 840 deletions

File tree

mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/mac/MacFrameUtils.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,23 @@ import java.nio.ByteBuffer
44

55
internal fun calculateFrameHash(
66
buffer: ByteBuffer,
7-
pixelCount: Int,
7+
width: Int,
8+
height: Int,
9+
rowBytes: Int,
810
): Int {
9-
if (pixelCount <= 0) return 0
11+
if (width <= 0 || height <= 0) return 0
1012

13+
val pixelCount = width * height
1114
var hash = 1
1215
val step = if (pixelCount <= 200) 1 else pixelCount / 200
13-
for (i in 0 until pixelCount step step) {
14-
hash = 31 * hash + buffer.getInt(i * 4)
16+
var i = 0
17+
while (i < pixelCount) {
18+
// Map the linear sample index to its real byte offset, honoring row padding
19+
// (rowBytes may exceed width*4) so we sample true pixels rather than padding bytes.
20+
val x = i % width
21+
val y = i / width
22+
hash = 31 * hash + buffer.getInt(y * rowBytes + x * 4)
23+
i += step
1524
}
1625
return hash
1726
}

0 commit comments

Comments
 (0)