Skip to content

Put constant-frame-rate video on an exact frame grid when the source time base is coarse (Matroska's 1 ms), for an even 3:2 cadence at 60 Hz #886

Open

Description

@akhanwav

What should the engine do?

When a constant-frame-rate video stream arrives on a coarse time base (Matroska's 1/1000 s), remux its timestamps onto the exact frame grid (n x 1001/24000 s for 23.976) in a fine timescale, instead of passing the millisecond-rounded values to AVPlayer.

Motivating media or use case

With Match Content off, so the panel stays at its 59.94 Hz home mode, 24p MKVs play with uneven motion through the native path. A player that presents frames itself (Infuse) plays the same files smoothly at the same output.

What we measured on the native path, from setNativeVideoFrameTimeObserver (item-axis times, sorted, 1,200 frames per window):

Source Spacing handed to AVPlayer Exact spacing
4K HEVC DV P7 MKV, 23.976 fps 41 ms x350, 42 ms x848 (one 84 ms gap) 41.708 ms
1080p H.264 MKV, 24.000 fps 41 ms x400, 42 ms x799 41.667 ms

The muxer keeps the source time base (MP4SegmentMuxer.swift:587, videoStream.pointee.time_base = video.timeBase, from HLSVideoEngine.swift:1443), so the fMP4 carries timescale 16000 with millisecond-quantized values. Each frame is up to 0.5 ms off its true time. The panel measured 59.944 Hz over 600 vsyncs.

Why that matters at 59.94 Hz: a 23.976 frame lasts exactly 2.5 refreshes, so every second frame's true time sits on a refresh boundary, the worst case for a 3:2 cadence. Rounding of up to 0.5 ms is enough to move those frames to the neighbouring refresh, which would turn the steady 3, 2, 3, 2 into bursts of 3, 2, 2, 3. With exact timestamps the cadence stays regular, and slips once where the content and panel rates differ.

What we have not shown: we did not capture the on-screen cadence with a high-speed camera, so the jitter is measured but its effect on screen is inferred. The timestamps were measured on 7.32.3; the muxer lines above are unchanged in 7.47.0. With Match Frame Rate on (one refresh per frame), no difference was seen and AVPlayer reported 0 dropped frames over 6 minutes.

Implementation thought: rounding each PTS and DTS to the nearest grid slot (round(t x fps)) keeps reorder order and moves video at most half a millisecond against audio. It could be limited to streams whose observed deltas all fall within one tick of the detected rate, so VFR and edited sources keep their own timestamps.

Area

Performance

Host app / integration context

Rivulet (tvOS), native loopback path, Apple TV 4K (3rd generation), tvOS 26.6

Would you be willing to open a PR?

No

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions