Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ jobs:
- name: swift test
run: swift test

- name: Timestamp policy and generated media regressions
run: |
brew list ffmpeg >/dev/null 2>&1 || brew install ffmpeg
bash Scripts/test-h264-matroska-timestamps.sh
bash Scripts/test-h264-timestamp-controls.sh

# The transport probe is a device harness and runs nowhere in CI, so nothing else would keep
# it compiling. A harness that stopped building is discovered by the person who needed it.
- name: build the transport probe (AE#377)
Expand Down
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ the public-API contract.

## [Unreleased]

_Nothing yet._
### Fixed

- Positively identified Matroska H.264 coding-order timestamp ladders now keep
their original presentation slots but assign each slot to its parsed picture
order. This removes persistent judder on the affected native/hardware VOD path
at startup and after seeks. The IDR-bounded policy leaves healthy/unproven
input untouched and preserves packet payloads, audio, and the seek-index axis.
It is separate from the existing missing-MP4-composition-offset policy.

## [6.71.0] - 2026-09-06

Expand Down
10 changes: 10 additions & 0 deletions Scripts/test-h264-matroska-timestamps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
TASK_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
TASK_TMP=$(mktemp -d "${TMPDIR:-/tmp}/aether-matroska-time.XXXXXX")
trap 'rm -f "$TASK_TMP/check"; rmdir "$TASK_TMP"' EXIT
# Focused Foundation CLI test, no Apple app target or package resolution.
xcrun swiftc -swift-version 6 \
"$TASK_ROOT/Sources/AetherEngine/Video/H264MatroskaTimestampRepair.swift" \
"$TASK_ROOT/Scripts/tests/H264MatroskaTimestampStandalone.swift" -o "$TASK_TMP/check"
"$TASK_TMP/check" "$TASK_ROOT/Scripts/tests/Data/MatroskaCodingOrderTimestamps.json" "$@"
17 changes: 17 additions & 0 deletions Scripts/test-h264-timestamp-controls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
TASK_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
TASK_TMP=$(mktemp -d "${TMPDIR:-/tmp}/aether-timestamp-controls.XXXXXX")
trap 'rm -f "$TASK_TMP/healthy.mp4" "$TASK_TMP/healthy.mkv" "$TASK_TMP/missing.mp4" "$TASK_TMP/coding-order.mkv"; rmdir "$TASK_TMP"' EXIT
# Generated solid-colour/AAC silence fixtures only; no private media is copied or committed.
ffmpeg -v error -f lavfi -i 'color=c=blue:s=96x64:r=30000/1001' \
-f lavfi -i 'anullsrc=r=48000:cl=stereo' -t 8 \
-c:v libx264 -preset ultrafast -pix_fmt yuv420p -bf 3 -b_strategy 0 -g 60 \
-sc_threshold 0 -c:a aac -movflags +faststart "$TASK_TMP/healthy.mp4"
ffmpeg -v error -i "$TASK_TMP/healthy.mp4" -c copy "$TASK_TMP/healthy.mkv"
ffmpeg -v error -i "$TASK_TMP/healthy.mp4" -c copy -bsf:v 'setts=pts=DTS' -movflags +faststart "$TASK_TMP/missing.mp4"
ffmpeg -v error -i "$TASK_TMP/healthy.mp4" -c copy -bsf:v 'setts=pts=DTS' "$TASK_TMP/coding-order.mkv"
AETHER_EXPECT_TIMESTAMP_REPAIR=0 bash "$TASK_ROOT/Scripts/test-h264-timestamp-runtime.sh" "$TASK_TMP/healthy.mp4" 0 3
AETHER_EXPECT_TIMESTAMP_REPAIR=0 bash "$TASK_ROOT/Scripts/test-h264-timestamp-runtime.sh" "$TASK_TMP/healthy.mkv" 0 3
AETHER_EXPECT_TIMESTAMP_REPAIR=1 bash "$TASK_ROOT/Scripts/test-h264-timestamp-runtime.sh" "$TASK_TMP/missing.mp4" 0 3
AETHER_EXPECT_TIMESTAMP_REPAIR=1 bash "$TASK_ROOT/Scripts/test-h264-timestamp-runtime.sh" "$TASK_TMP/coding-order.mkv" 0 3
30 changes: 30 additions & 0 deletions Scripts/test-h264-timestamp-runtime.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
set -euo pipefail
TASK_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
TASK_FFMPEG_ROOT="${AETHER_FFMPEG_CHECKOUT:-$TASK_ROOT/.build/checkouts/FFmpegBuild}"
TASK_EXPECTED_REVISION=$(/usr/bin/ruby -rjson -e \
'puts JSON.parse(File.read(ARGV.fetch(0))).fetch("pins").find { |p| p.fetch("identity") == "ffmpegbuild" }.fetch("state").fetch("revision")' "$TASK_ROOT/Package.resolved")
[[ $(git -C "$TASK_FFMPEG_ROOT" rev-parse HEAD) == "$TASK_EXPECTED_REVISION" ]] \
|| { echo 'Frozen FFmpegBuild revision mismatch' >&2; exit 2; }
TASK_TMP=$(mktemp -d "${TMPDIR:-/tmp}/aether-timestamp-runtime.XXXXXX")
trap 'rm -f "$TASK_TMP/check"; rmdir "$TASK_TMP"' EXIT
TASK_FRAMEWORK_ARGS=()
for TASK_LIBRARY in AetherLibavformat AetherLibavcodec AetherLibavutil AetherLibswresample AetherLibdav1d AetherLibzvbi; do
TASK_DIR="$TASK_FFMPEG_ROOT/Sources/$TASK_LIBRARY.xcframework/macos-arm64_x86_64"
[[ -f "$TASK_DIR/$TASK_LIBRARY.framework/$TASK_LIBRARY" ]] || exit 2
TASK_FRAMEWORK_ARGS+=(-F "$TASK_DIR" -Xlinker -rpath -Xlinker "$TASK_DIR")
done
# Compile the actual timestamp sessions and parser against the exact bundled dependency.
# This is a focused command-line library check, never an iOS/macOS app build or SwiftPM resolve.
xcrun swiftc -swift-version 6 \
"${TASK_FRAMEWORK_ARGS[@]}" -framework AetherLibavformat -framework AetherLibavcodec -framework AetherLibavutil \
"$TASK_ROOT/Sources/AetherEngine/Diagnostics/PacketBalanceTracker.swift" \
"$TASK_ROOT/Sources/AetherEngine/Diagnostics/EngineLog.swift" \
"$TASK_ROOT/Sources/AetherEngine/Diagnostics/LogRedaction.swift" \
"$TASK_ROOT/Sources/AetherEngine/Decoder/A53SEIParser.swift" \
"$TASK_ROOT/Sources/AetherEngine/Decoder/CCDataParser.swift" \
"$TASK_ROOT/Sources/AetherEngine/Video/H264CompositionOffsetRepair.swift" \
"$TASK_ROOT/Sources/AetherEngine/Video/H264MatroskaTimestampRepair.swift" \
"$TASK_ROOT/Sources/AetherEngine/Video/H264MatroskaTimestampRepairSession.swift" \
"$TASK_ROOT/Scripts/tests/H264TimestampRuntimeStandalone.swift" -o "$TASK_TMP/check"
"$TASK_TMP/check" "$@"
249 changes: 249 additions & 0 deletions Scripts/tests/Data/MatroskaCodingOrderTimestamps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
{
"video_delay": 1,
"packets": [
{
"pts": 0,
"poc": 0
},
{
"pts": 40,
"poc": 8
},
{
"pts": 73,
"poc": 2
},
{
"pts": 107,
"poc": 4
},
{
"pts": 140,
"poc": 6
},
{
"pts": 173,
"poc": 14
},
{
"pts": 207,
"poc": 10
},
{
"pts": 240,
"poc": 12
},
{
"pts": 274,
"poc": 20
},
{
"pts": 307,
"poc": 16
},
{
"pts": 340,
"poc": 18
},
{
"pts": 374,
"poc": 24
},
{
"pts": 407,
"poc": 22
},
{
"pts": 440,
"poc": 30
},
{
"pts": 474,
"poc": 26
},
{
"pts": 507,
"poc": 28
},
{
"pts": 541,
"poc": 36
},
{
"pts": 574,
"poc": 32
},
{
"pts": 607,
"poc": 34
},
{
"pts": 641,
"poc": 42
},
{
"pts": 674,
"poc": 38
},
{
"pts": 707,
"poc": 40
},
{
"pts": 741,
"poc": 48
},
{
"pts": 774,
"poc": 44
},
{
"pts": 807,
"poc": 46
},
{
"pts": 841,
"poc": 56
},
{
"pts": 874,
"poc": 50
},
{
"pts": 908,
"poc": 52
},
{
"pts": 941,
"poc": 54
},
{
"pts": 974,
"poc": 60
},
{
"pts": 1008,
"poc": 58
},
{
"pts": 1041,
"poc": 66
},
{
"pts": 1074,
"poc": 62
},
{
"pts": 1108,
"poc": 64
},
{
"pts": 1141,
"poc": 72
},
{
"pts": 1174,
"poc": 68
},
{
"pts": 1208,
"poc": 70
},
{
"pts": 1241,
"poc": 78
},
{
"pts": 1275,
"poc": 74
},
{
"pts": 1308,
"poc": 76
},
{
"pts": 1341,
"poc": 84
},
{
"pts": 1375,
"poc": 80
},
{
"pts": 1408,
"poc": 82
},
{
"pts": 1441,
"poc": 90
},
{
"pts": 1475,
"poc": 86
},
{
"pts": 1508,
"poc": 88
},
{
"pts": 1542,
"poc": 96
},
{
"pts": 1575,
"poc": 92
},
{
"pts": 1608,
"poc": 94
},
{
"pts": 1642,
"poc": 102
},
{
"pts": 1675,
"poc": 98
},
{
"pts": 1708,
"poc": 100
},
{
"pts": 1742,
"poc": 108
},
{
"pts": 1775,
"poc": 104
},
{
"pts": 1808,
"poc": 106
},
{
"pts": 1842,
"poc": 114
},
{
"pts": 1875,
"poc": 110
},
{
"pts": 1909,
"poc": 112
},
{
"pts": 1942,
"poc": 118
},
{
"pts": 1975,
"poc": 116
},
{
"pts": 2009,
"poc": 0
}
]
}
Loading