What happened?
On a progressive H.264 MP4 VOD with reordered pictures but no composition-time table, the native stream-copy path produces continuous forward/backward judder from the first frame. Seeking is not required to trigger it. The same source plays smoothly in another FFmpeg-based player.
The container presents every sampled video packet with PTS == DTS even though codecpar.video_delay == 1. A bounded libavcodec decode of the same head sample shows the contradiction directly: raw decoded AVFrame.pts regresses 16 times while best_effort_timestamp advances 58 times with zero regressions. Because the PTS values are present rather than AV_NOPTS_VALUE, the existing +genpts open flag and the #407 fallback do not repair them. Stream-copying those packet timestamps into fMP4 preserves decode order as presentation order, so AVPlayer displays future reference pictures before the B pictures that precede them.
Routing only this confirmed shape through the software decoder and scheduling decoded frames on best_effort_timestamp removes the judder.
Steps to reproduce
- Open a seekable MP4 containing progressive H.264 with B-frame reordering, but whose sample table has no
ctts composition offsets.
- Play through the normal native VOD path.
- Observe continuous small forward/backward motion from playback start.
- Inspect the first 64 video packets and decoded frames. The reporting source produced:
valid_pairs=64, PTS==DTS for all pairs, nonkey=62, decoded=59, best_effort_advances=58, best_effort_regressions=0, raw_pts_regressions=16.
A tiny 96x64 H.264 Main, 30000/1001 fps, 3-B-frame synthetic fixture and its healthy ctts twin are ready for a regression test in the accompanying PR.
AetherEngine version or commit SHA
6.39.0 / f70d4aee595f0947ef2354fbae01c76b41a7857b
Host app
Custom integration
Platform
tvOS
OS version
tvOS 26.6 beta (23L773)
Device / chip
Apple TV 4K (3rd generation)
Playback path
Native AVPlayer
Source media (for playback bugs)
MP4 / progressive H.264 with B-frame reordering / SDR. The source has no usable composition offsets: sampled packet PTS and DTS are equal despite video_delay=1. Audio is not involved in the failure. The synthetic regression fixture is MP4 / H.264 Main / 96x64 / 30000/1001 fps / no audio / SDR.
Error codes / log lines
No AVPlayer or VideoToolbox error is emitted. The diagnostic signature is:
video_packets=64 valid_pairs=64 nonkey=62 decoded_frames=59
best_advances=58 best_regressions=0 raw_regressions=16
raw_best_differences=16
Anything else
This is adjacent to #407 but not the same case. #407 repairs only AVFrame.pts == AV_NOPTS_VALUE; here the raw PTS is always present, but it is the wrong decode-order axis. A focused engine patch with bounded fail-closed detection, automatic per-session software routing, and good/bad MP4 fixtures is being prepared.
What happened?
On a progressive H.264 MP4 VOD with reordered pictures but no composition-time table, the native stream-copy path produces continuous forward/backward judder from the first frame. Seeking is not required to trigger it. The same source plays smoothly in another FFmpeg-based player.
The container presents every sampled video packet with
PTS == DTSeven thoughcodecpar.video_delay == 1. A bounded libavcodec decode of the same head sample shows the contradiction directly: raw decodedAVFrame.ptsregresses 16 times whilebest_effort_timestampadvances 58 times with zero regressions. Because the PTS values are present rather thanAV_NOPTS_VALUE, the existing+genptsopen flag and the #407 fallback do not repair them. Stream-copying those packet timestamps into fMP4 preserves decode order as presentation order, so AVPlayer displays future reference pictures before the B pictures that precede them.Routing only this confirmed shape through the software decoder and scheduling decoded frames on
best_effort_timestampremoves the judder.Steps to reproduce
cttscomposition offsets.valid_pairs=64,PTS==DTSfor all pairs,nonkey=62,decoded=59,best_effort_advances=58,best_effort_regressions=0,raw_pts_regressions=16.A tiny 96x64 H.264 Main, 30000/1001 fps, 3-B-frame synthetic fixture and its healthy
cttstwin are ready for a regression test in the accompanying PR.AetherEngine version or commit SHA
6.39.0 /
f70d4aee595f0947ef2354fbae01c76b41a7857bHost app
Custom integration
Platform
tvOS
OS version
tvOS 26.6 beta (23L773)
Device / chip
Apple TV 4K (3rd generation)
Playback path
Native AVPlayer
Source media (for playback bugs)
MP4 / progressive H.264 with B-frame reordering / SDR. The source has no usable composition offsets: sampled packet PTS and DTS are equal despite
video_delay=1. Audio is not involved in the failure. The synthetic regression fixture is MP4 / H.264 Main / 96x64 / 30000/1001 fps / no audio / SDR.Error codes / log lines
No AVPlayer or VideoToolbox error is emitted. The diagnostic signature is:
Anything else
This is adjacent to #407 but not the same case. #407 repairs only
AVFrame.pts == AV_NOPTS_VALUE; here the raw PTS is always present, but it is the wrong decode-order axis. A focused engine patch with bounded fail-closed detection, automatic per-session software routing, and good/bad MP4 fixtures is being prepared.