diff --git a/CHANGELOG.md b/CHANGELOG.md index 311afd129..41a702fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,24 @@ the public-API contract. ### Fixed +- **MP4s whose writer dropped `ctts` and forced the decode ladder monotonic play in order.** + Such a file reports `PTS == DTS` on every sample and pushes each B picture (on some files each anchor + too) 1001 ticks past the picture decoded before it, so the ladder has no single + step and the #409 probe left the stream untouched ("decode ladder is not uniform"). Both routes then + showed the B pictures in decode order, as QuickTime and the Files app do. The repair now takes the + step from the stream's average frame rate, remeasures it on the anchors (or, when the anchors were + pushed as well, snaps it to the nearest standard frame rate within 1%) and rebuilds DTS from each + picture's decode slot, under the same picture-order checks. A keyframe the writer pushed late + continues the GOP before it instead of leaving a gap or overlapping it, and a head that opens on a + run of P pictures is sampled on to its first B picture (up to 48 pictures). The rate is the one the + bitstream's VUI states when it agrees with the stream (24 is no longer read as 23.976 off a short + clip), applied as an exact fraction of a tick, and a continued sequence leans toward where the + writer's own pictures say it starts, so a writer clock slightly off its stated rate no longer drifts + the picture 61 ms and then overlaps a GOP. Over HLS, a keyframe placed back onto its sequence sits + before the index entry the segment plan names, and the cutter and the restart gate now reach for + it: before, most segments of such a file opened mid-GOP and could not be decoded on their own. + `Scripts/make-clamped-ladder-fixture.sh` generates the twins. See `docs/formats.md`. + - **A refused HDR master no longer latches when the panel is shown to present HDR (AE#887).** A live PQ master refused with -11868 on an Apple TV whose output was fixed to 4K HDR latched the process-wide panel refusal, so every later HDR title went media-direct without its subtitle diff --git a/Scripts/make-clamped-ladder-fixture.sh b/Scripts/make-clamped-ladder-fixture.sh new file mode 100755 index 000000000..b9a9d3874 --- /dev/null +++ b/Scripts/make-clamped-ladder-fixture.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# Reproduces an MP4 whose writer dropped `ctts` and then forced its decode-order presentation times +# to be monotonic: every sample reports PTS == DTS, an anchor keeps its own time, and each B picture +# that follows it in decode order is pushed 1001 ticks (a third of a 29.97 fps frame) past the picture +# decoded before it. The decode ladder then has no single step, which the #409 classifier needs. +# QuickTime and the Files app (AVFoundation) show such a file with its pictures stepping back and +# forth; FFmpeg-based players reorder by picture order count and look right. +# +# Some writers push the anchors as well (the reporting file has P pictures 1001 ticks past the picture +# before them), so the anchors no longer measure a step either. pushed-anchors.mp4 reproduces that by +# adding 1001 ticks to every other picture before the clamp, away from the end of each GOP so the +# keyframes keep their own time. +# +# bframes-healthy.mp4 H.264 High 1280x720 29.97 fps, B pyramid, GOP 90, 30 s + AAC (the twin) +# clamped-ladder.mp4 the same stream, ctts dropped, ladder clamped as above +# pushed-anchors.mp4 the same stream, ctts dropped, anchors pushed too +# p-run-healthy.mp4 (x264 CLI only) every GOP opens on twelve P pictures before its first B +# p-run-clamped.mp4 the same stream, ctts dropped, clamped as clamped-ladder.mp4 +# +# The clamp also pushes some keyframes (the 630th picture here, by 1001 ticks), as the reporting +# files do on most GOP boundaries; the repair continues the sequence before them instead. +# p-run-healthy.mp4 is written through Matroska, so its timestamps sit on whole milliseconds and the +# twins agree in display order rather than in value. +# +# testsrc2 draws a frame counter and a moving bar, so the order is visible by eye. +# +# Usage: +# ./Scripts/make-clamped-ladder-fixture.sh [output-dir] [--check] +# +# --check builds aetherctl and prints the #409 verdict (pktdump) and the decoded order (swdecode) +# for both clamped files. Requires ffmpeg with libx264 (macOS: `brew install ffmpeg`). + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +OUT="$REPO_ROOT/Fixtures/clamped-ladder" +CHECK=0 +for arg in "$@"; do + case "$arg" in + --check) CHECK=1 ;; + *) OUT="$arg" ;; + esac +done +mkdir -p "$OUT" +FF=(ffmpeg -hide_banner -loglevel error -y) + +"${FF[@]}" \ + -f lavfi -i "testsrc2=size=1280x720:rate=30000/1001:duration=30" \ + -f lavfi -i "sine=frequency=440:duration=30" \ + -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p -bf 3 -g 90 -x264-params b-pyramid=normal \ + -c:a aac -b:a 128k -video_track_timescale 90000 -movflags +faststart \ + "$OUT/bframes-healthy.mp4" + +# setts runs per packet in decode order, so PREV_OUTDTS is the timestamp just written: the clamp +# keeps every anchor (its PTS is already past everything before it) and pushes each B picture. +"${FF[@]}" -i "$OUT/bframes-healthy.mp4" -map 0 -c copy \ + -bsf:v 'setts=ts=max(PTS\,PREV_OUTDTS+1001)' -video_track_timescale 90000 -movflags +faststart \ + "$OUT/clamped-ladder.mp4" + +# N counts packets in decode order, so mod(N, 2) pushes P and B pictures alike. The last ten pictures +# of each GOP are left alone so the push does not run on into the next keyframe. +"${FF[@]}" -i "$OUT/bframes-healthy.mp4" -map 0 -c copy \ + -bsf:v 'setts=ts=max(PTS+1001*mod(N\,2)*lt(mod(N\,90)\,80)\,PREV_OUTDTS+1001)' \ + -video_track_timescale 90000 -movflags +faststart \ + "$OUT/pushed-anchors.mp4" + +FIXTURES=(clamped-ladder.mp4 pushed-anchors.mp4) + +# A writer that opens each GOP on a run of P pictures leaves the first twelve sampled pictures in +# order, so the probe has to sample on to the first B picture. ffmpeg's libx264 wrapper cannot force +# frame types; the x264 CLI does it with a qpfile. +if command -v x264 >/dev/null 2>&1; then + TMP="$(mktemp -d)" + { echo "0 I -1"; for i in $(seq 1 12); do echo "$i P -1"; echo "$((i + 90)) P -1"; echo "$((i + 180)) P -1"; done; echo "90 I -1"; echo "180 I -1"; } \ + | sort -n > "$TMP/qp.txt" + "${FF[@]}" -f lavfi -i "testsrc2=size=1280x720:rate=30000/1001:duration=10" -pix_fmt yuv420p \ + -f yuv4mpegpipe "$TMP/src.y4m" + x264 --quiet --preset veryfast --crf 23 --bframes 3 --b-pyramid normal --b-adapt 0 --keyint 90 \ + --min-keyint 90 --scenecut 0 --qpfile "$TMP/qp.txt" -o "$TMP/p-run.mkv" "$TMP/src.y4m" 2>/dev/null + "${FF[@]}" -i "$TMP/p-run.mkv" -c:v copy -video_track_timescale 90000 -movflags +faststart \ + "$OUT/p-run-healthy.mp4" + "${FF[@]}" -i "$OUT/p-run-healthy.mp4" -map 0 -c copy \ + -bsf:v 'setts=ts=max(PTS\,PREV_OUTDTS+1001)' -video_track_timescale 90000 -movflags +faststart \ + "$OUT/p-run-clamped.mp4" + rm -rf "$TMP" + echo "wrote $OUT/p-run-healthy.mp4" + FIXTURES+=(p-run-clamped.mp4) +else + echo "x264 not found: skipping p-run-*.mp4" +fi +echo "wrote $OUT/bframes-healthy.mp4" +for name in "${FIXTURES[@]}"; do + echo "wrote $OUT/$name" + ffprobe -v error -select_streams v:0 -show_entries packet=pts,dts -of csv=p=0 "$OUT/$name" \ + | awk -F, -v name="$name" 'NR > 1 { step[$2 - prev]++ } { if ($1 == $2) same++; prev = $2; n++ } + END { printf "%s: %d packets, PTS == DTS on %d, DTS steps:", name, n, same + for (s in step) printf " %s x%d", s, step[s]; print "" }' +done + +if [[ $CHECK -eq 1 ]]; then + swift build --package-path "$REPO_ROOT" --product aetherctl >/dev/null 2>&1 + CTL="$(swift build --package-path "$REPO_ROOT" --product aetherctl --show-bin-path 2>/dev/null)/aetherctl" + for name in "${FIXTURES[@]}"; do + echo "$name:" + "$CTL" pktdump --count 12 "$OUT/$name" 2>&1 | grep -E '#409' || echo "no #409 verdict line" + "$CTL" swdecode --frames 300 "$OUT/$name" 2>&1 | grep -E 'Steps backwards' || true + done +fi diff --git a/Sources/AetherEngine/Demuxer/Demuxer.swift b/Sources/AetherEngine/Demuxer/Demuxer.swift index 1529f6749..58615b678 100644 --- a/Sources/AetherEngine/Demuxer/Demuxer.swift +++ b/Sources/AetherEngine/Demuxer/Demuxer.swift @@ -2184,6 +2184,15 @@ public final class Demuxer: @unchecked Sendable { return result } + /// Ticks (stream time base) by which a delivered keyframe may precede its `indexedKeyframeEntries` + /// timestamp: a #409 repair that synthesizes the decode ladder moves a late-written IDR back onto + /// its sequence. 0 when packets sit exactly on the index. + var indexedKeyframeAdvanceTicks: Int64 { + accessLock.lock() + defer { accessLock.unlock() } + return compositionRepair?.keyframeAdvanceTicks ?? 0 + } + func readPacket(isCurrent: @Sendable () -> Bool = { true }) throws -> UnsafeMutablePointer? { accessLock.lock() defer { accessLock.unlock() } diff --git a/Sources/AetherEngine/Video/H264CompositionOffsetRepair.swift b/Sources/AetherEngine/Video/H264CompositionOffsetRepair.swift index 1f0329007..4f3a8ab1a 100644 --- a/Sources/AetherEngine/Video/H264CompositionOffsetRepair.swift +++ b/Sources/AetherEngine/Video/H264CompositionOffsetRepair.swift @@ -129,6 +129,39 @@ enum H264CompositionOffsetRepair { /// reorder head, and the values between when it trims part of one. The ladder cannot say /// which, so this is read from the container the same way `shift` is. var ladderOrdinalOffset: Int64 = 0 + /// Set when the writer's decode ladder is not a ladder at all and `step` came from the stream's + /// average frame rate instead (`nominalStep`). DTS is then rebuilt from the decode ordinal as + /// well, because the container's DTS of a B picture can sit after the presentation time it + /// belongs to. + var synthesizesDecodeLadder = false + /// The exact picture length of a synthesized ladder, when the rate is known as a fraction (23 fps + /// is 90000/23 ticks at 90 kHz). Offsets inside a sequence and the length of a sequence are read + /// off it, so a rounded `step` never adds up across a long file. + var pictureCadence: Cadence? + /// The reorder delay in pictures, which `decodeLead` is the length of. Only a synthesized + /// ladder reads it; 0 on every other plan. + var decodeLeadPictures: Int64 = 0 + + /// The most a synthesized ladder places a keyframe before its written decode time: the reorder + /// delay plus two and a half pictures, which is how late the writers this ladder describes + /// were measured to push an IDR (four pictures on the 29 fps reporting asset). Continuing a + /// sequence and landing on a seek both stay inside it, so the container index, read on the + /// written axis, names every keyframe to within this many ticks. 0 for every other plan, + /// which keeps each keyframe exactly where the index says. + var keyframeAdvanceLimit: Int64 { + guard synthesizesDecodeLadder else { return 0 } + let (pictures, pictureOverflow) = step.multipliedReportingOverflow(by: 2) + let (limit, limitOverflow) = decodeLead.addingReportingOverflow(pictures) + let (total, totalOverflow) = limit.addingReportingOverflow(step / 2) + return pictureOverflow || limitOverflow || totalOverflow ? 0 : total + } + + /// Ticks from a sequence's anchor to its picture `ordinal`, on `pictureCadence` when there is one. + func pictureOffset(_ ordinal: Int64) -> Int64? { + if let pictureCadence { return pictureCadence.timestamp(at: ordinal) } + let (offset, overflow) = ordinal.multipliedReportingOverflow(by: step) + return overflow ? nil : offset + } /// The timestamp of a presentation ordinal, nil unless this plan carries a lattice. func presentationTimestamp(ordinal: Int64) -> Int64? { @@ -169,6 +202,15 @@ enum H264CompositionOffsetRepair { /// shows an offset; only the broken shape pays the full window, which has to span at least one /// mini-GOP for the picture order to prove reordering at all. static let sampleTarget = 12 + /// How far the sample may grow when the first `sampleTarget` pictures never reorder. A writer can + /// open every GOP on a run of P pictures (measured: twelve, then the first B picture), and without + /// a regression the head proves nothing either way. + static let extendedSampleTarget = 48 + /// Pictures a seek onto a synthesized ladder holds before it places the landing. The writer only + /// ever pushes a picture later, so the earliest of `written time - offset of its display slot` over + /// the first pictures is a better start than the keyframe alone: measured on the three reporting + /// files, 8 pictures cut the spread of the landing from 0.5 to 4.3 pictures to -0.9 to 1.3. + static let landingEnvelopePictures = 8 /// Below this the ladder and the reorder evidence are too thin to trust; sampling stops early /// only on a healthy verdict or EOF. static let minimumSamples = 9 @@ -206,7 +248,9 @@ enum H264CompositionOffsetRepair { samples: [Sample], videoDelay: Int, streamStartTime: Int64, - ladderStart: Int64 + ladderStart: Int64, + nominalStep: Int64? = nil, + rateCadence: Cadence? = nil ) -> Verdict { guard videoDelay > 0, videoDelay <= 16 else { return .inconclusive("reorder delay \(videoDelay) outside 1...16") @@ -247,14 +291,24 @@ enum H264CompositionOffsetRepair { } var cadence: Cadence? var step = shortestStep + var synthesizesDecodeLadder = false if shortestStep != longestStep { - guard longestStep - shortestStep == 1, - let quantized = quantizedCadence(deltas: deltas), - let roundedStep = quantized.timestamp(at: 1) else { + if longestStep - shortestStep == 1, + let quantized = quantizedCadence(deltas: deltas), + let roundedStep = quantized.timestamp(at: 1) { + cadence = quantized + step = roundedStep + } else if let nominalStep, + nominalLadderFits(samples: samples, step: nominalStep, videoDelay: videoDelay) { + // A writer that dropped `ctts` and then forced its decode-order presentation times to + // be monotonic (each B picture pushed one third of a frame past the anchor decoded + // before it) leaves no ladder to read a step from, but the pictures still come at the + // stream's constant rate. + step = nominalStep + synthesizesDecodeLadder = true + } else { return .inconclusive("decode ladder is not uniform") } - cadence = quantized - step = roundedStep } guard step > 0 else { return .inconclusive("no ladder step") } @@ -301,6 +355,24 @@ enum H264CompositionOffsetRepair { return .inconclusive("display indices do not fill the sampled window") } + // The average frame rate is only an estimate (short files, rounded durations: a 60-picture + // 29.97 clip reports 29.80). When the writer left its anchors on the presentation axis they give + // the exact step. The reporting file pushed its anchors as well (a P picture 1001 ticks after the + // P before it), so they agree on nothing; the step is then the stream's picture rate + // (`rateCadence`), and a head with neither is left alone. + var pictureCadence: Cadence? + if synthesizesDecodeLadder { + if let measured = anchorStep(samples: samples, pocStep: pocStep) { + step = measured + pictureCadence = Cadence(numerator: measured, denominator: 1) + } else if let rateCadence, let rounded = rateCadence.timestamp(at: 1), rounded >= 2 { + step = rounded + pictureCadence = rateCadence + } else { + return .inconclusive("anchors do not agree on a step") + } + } + // The reorder head is one decode lead long, and with a fractional cadence that lead is a // rounded distance on the lattice rather than a multiple of a whole step. let ladderOrigin = ladderStart == Int64.min ? (samples.first?.dts ?? Int64.min) : ladderStart @@ -310,14 +382,26 @@ enum H264CompositionOffsetRepair { return .inconclusive("cadence does not describe a reorder head") } decodeLead = -head + } else if let pictureCadence { + guard let lead = pictureCadence.timestamp(at: Int64(videoDelay)) else { + return .inconclusive("reorder head does not fit the timeline") + } + decodeLead = lead } else { let (lead, overflow) = Int64(videoDelay).multipliedReportingOverflow(by: step) guard !overflow else { return .inconclusive("reorder head does not fit the timeline") } decodeLead = lead } guard decodeLead > 0 else { return .inconclusive("no decode lead") } - let shift = presentationShift( - streamStartTime: streamStartTime, ladderStart: ladderOrigin, decodeLead: decodeLead) + // A synthesized ladder anchors every coded video sequence on its keyframe's DTS, so the shift + // is simply what puts the first keyframe on the presentation start. The writer that produces + // this shape can leave that keyframe more than one decode lead ahead (measured: 12012 ticks at + // a 3003 step and delay 2), so it is bounded by a generous number of pictures instead of the + // decode lead. + let shift = synthesizesDecodeLadder + ? nominalShift(streamStartTime: streamStartTime, ladderStart: ladderOrigin, step: step) + : presentationShift( + streamStartTime: streamStartTime, ladderStart: ladderOrigin, decodeLead: decodeLead) // A fractional cadence is read back off a lattice, so the lattice has to be located first, // and that takes two readings the ladder cannot both give. The phase and the origin come from @@ -354,11 +438,131 @@ enum H264CompositionOffsetRepair { cadence: cadence, latticeOrigin: latticeOrigin, ladderPhase: ladderPhase, - ladderOrdinalOffset: ladderOrdinalOffset + ladderOrdinalOffset: ladderOrdinalOffset, + synthesizesDecodeLadder: synthesizesDecodeLadder, + pictureCadence: pictureCadence, + decodeLeadPictures: synthesizesDecodeLadder ? Int64(videoDelay) : 0 ) ) } + /// Ticks per picture from the stream's average frame rate, rounded. nil when the rate is missing or + /// describes a picture shorter than two ticks. + static func nominalFrameStep(frameRate: AVRational, timeBase: AVRational) -> Int64? { + guard frameRate.num > 0, frameRate.den > 0, timeBase.num > 0, timeBase.den > 0 else { return nil } + let ticks = (Double(timeBase.den) * Double(frameRate.den)) / (Double(timeBase.num) * Double(frameRate.num)) + guard ticks.isFinite, ticks >= 2, ticks < Double(Int64.max / 4) else { return nil } + return Int64(ticks.rounded()) + } + + /// Frame rates a camera, encoder or broadcast chain actually produces: every whole rate up to 240, + /// and the NTSC ones (`n * 1000 / 1001`). + static let standardFrameRates: [(num: Int64, den: Int64)] = + (1...240).map { (Int64($0), 1) } + [24, 30, 48, 60, 120, 240].map { (Int64($0) * 1000, 1001) } + + /// The picture length, as an exact fraction of a tick, of the standard frame rate closest to + /// `estimate`, within 1%. 29.97 and 30 are both that close to either, so the nearer one wins and a + /// tie names neither. nil when no standard rate is that close. + static func standardFrameCadence(near estimate: Int64, timeBase: AVRational) -> Cadence? { + guard estimate >= 2, timeBase.num > 0, timeBase.den > 0 else { return nil } + let ranked = standardFrameRates.map { rate -> (rate: (num: Int64, den: Int64), distance: Double) in + let ticks = Double(timeBase.den) * Double(rate.den) / (Double(timeBase.num) * Double(rate.num)) + return (rate, abs(ticks - Double(estimate))) + }.sorted { $0.distance < $1.distance } + guard let nearest = ranked.first, nearest.distance * 100 <= Double(estimate) else { return nil } + if ranked.count > 1, ranked[1].distance == nearest.distance { return nil } + return pictureCadence(rate: nearest.rate, timeBase: timeBase) + } + + /// The picture rate a synthesized ladder is rebuilt at. The bitstream states it (the H.264 or HEVC + /// VUI timing the parser reads into the codec context): on all three reporting files it named the + /// exact rate (23, 29.97, 29.0002 fps), where the average rate of a short file with a pushed tail + /// cannot tell 24 from 23.976. It is trusted only within 1% of the average rate, and snapped to the + /// nearest standard rate within 0.01% (29.97 and 30 are 0.1% apart and must stay apart). Without + /// it, the standard rate nearest the average rate. + static func rateCadence(bitstreamRate: AVRational?, estimate: Int64, timeBase: AVRational) -> Cadence? { + guard timeBase.num > 0, timeBase.den > 0 else { return nil } + if let rate = bitstreamRate, rate.num > 0, rate.den > 0, + let stated = pictureCadence(rate: (Int64(rate.num), Int64(rate.den)), timeBase: timeBase) { + let ticks = Double(stated.numerator) / Double(stated.denominator) + if abs(ticks - Double(estimate)) * 100 <= Double(estimate) { + let distance = { (cadence: Cadence) in abs(Double(cadence.numerator) / Double(cadence.denominator) - ticks) } + let standard = standardFrameRates.compactMap { pictureCadence(rate: $0, timeBase: timeBase) } + .filter { distance($0) * 10_000 <= ticks } + .min { distance($0) < distance($1) } + return standard ?? stated + } + } + return standardFrameCadence(near: estimate, timeBase: timeBase) + } + + /// Ticks per picture at `rate` in `timeBase`, as a reduced fraction. nil when it overflows or is + /// shorter than two ticks, which the lattice arithmetic refuses. + static func pictureCadence(rate: (num: Int64, den: Int64), timeBase: AVRational) -> Cadence? { + let (numerator, overflow) = Int64(timeBase.den).multipliedReportingOverflow(by: rate.den) + let (denominator, denominatorOverflow) = Int64(timeBase.num).multipliedReportingOverflow(by: rate.num) + guard !overflow, !denominatorOverflow, let cadence = Cadence(numerator: numerator, denominator: denominator), + cadence.wholeTicks >= 2 else { return nil } + return cadence + } + + /// Whether a non-uniform sampled ladder still spans the time its pictures would take at `step`. The + /// writer's forced-monotonic ladder runs ahead of that by at most the reorder head plus the anchor + /// it pushed past (measured 3.5 pictures on the reporting file at delay 2), and falls behind only + /// by what the next anchor has not yet reclaimed; a genuinely variable-rate head misses it by far + /// more. `step` is rounded, so each bound also gives a tick per picture: a third reporting file at + /// 29 fps (3103.45 ticks) ran exactly that four pictures ahead and missed the upper bound by 3. + static func nominalLadderFits(samples: [Sample], step: Int64, videoDelay: Int) -> Bool { + guard step >= 2, let first = samples.first?.dts, let last = samples.last?.dts, last > first else { + return false + } + let pictures = Int64(samples.count - 1) + let slack = Int64(videoDelay + 2) + let (span, overflow) = last.subtractingReportingOverflow(first) + let (low, lowOverflow) = max(0, pictures - slack).multipliedReportingOverflow(by: step) + let (high, highOverflow) = (pictures + slack).multipliedReportingOverflow(by: step) + guard !overflow, !lowOverflow, !highOverflow else { return false } + return span >= low - pictures && span <= high + pictures + } + + /// Ticks per picture read off the sampled anchors, the pictures that display later than everything + /// decoded before them. A forced-monotonic writer leaves their timestamps on the presentation axis, + /// so each one sits `displayIndex * step` after the opening keyframe. nil unless every anchor + /// agrees to within a tick per picture. + static func anchorStep(samples: [Sample], pocStep: Int64) -> Int64? { + guard pocStep > 0, let first = samples.first, first.pictureOrderCount == 0 else { return nil } + var anchors: [(index: Int64, offset: Int64)] = [] + var latest: Int64 = 0 + for sample in samples.dropFirst() where sample.pictureOrderCount > latest { + latest = sample.pictureOrderCount + let (offset, overflow) = sample.dts.subtractingReportingOverflow(first.dts) + guard !overflow, offset > 0, sample.pictureOrderCount % pocStep == 0 else { return nil } + anchors.append((sample.pictureOrderCount / pocStep, offset)) + } + guard let farthest = anchors.last, farthest.index > 0 else { return nil } + let step = (farthest.offset + farthest.index / 2) / farthest.index + guard step >= 2 else { return nil } + for anchor in anchors { + let (expected, overflow) = anchor.index.multipliedReportingOverflow(by: step) + guard !overflow, abs(anchor.offset - expected) <= anchor.index + 1 else { return nil } + } + return step + } + + /// Pictures a synthesized ladder's presentation shift may reach (`nominalShift`). + static let maximumNominalShiftPictures: Int64 = 16 + + /// The presentation shift for a synthesized ladder, clamped to + /// `[0, maximumNominalShiftPictures * step]` so a malformed header cannot drag the video off its + /// audio. + static func nominalShift(streamStartTime: Int64, ladderStart: Int64, step: Int64) -> Int64 { + guard streamStartTime != Int64.min, ladderStart != Int64.min else { return 0 } + let (raw, overflow) = streamStartTime.subtractingReportingOverflow(ladderStart) + let (limit, limitOverflow) = maximumNominalShiftPictures.multipliedReportingOverflow(by: step) + guard !overflow, !limitOverflow else { return 0 } + return min(max(raw, 0), limit) + } + /// True when some decode-order prefix of at least `minimumSamples` display indices covers its /// own range without a gap, i.e. a whole number of mini-GOPs closed inside the window. static func hasContiguousPrefix(_ indices: [Int64]) -> Bool { @@ -448,6 +652,22 @@ enum H264CompositionOffsetRepair { /// order (an open GOP's leading pictures). The landing is displayed that many slots after /// the one it is decoded in, so the re-anchor has to know the count. private var landingLeadingPictures: Int64 = 0 + /// The decode slot of the next picture in the current sequence, counted from its anchor. Only a + /// synthesized ladder reads it. + private var decodeOrdinal: Int64 = 0 + /// The current sequence was anchored on a seek landing rather than an IDR. The parser was reset + /// for the seek, so an H.264 open-GOP landing reads its picture order relative to itself (a + /// non-IDR I picture can come out at -4), and only differences within the sequence mean anything. + private var anchoredOnLanding = false + /// The highest display index placed in the current sequence. A synthesized ladder's next IDR + /// is expected right after it. + private var sequenceLastDisplayIndex: Int64? + /// The earliest sequence origin the pictures held after a seek point to (`noteLandingEnvelope`). + private var landingEnvelopeAnchor: Int64? + /// The earliest origin, `written DTS - pictureOffset(display index)`, any picture of the + /// current sequence pointed to. The writer only ever pushes a picture later, so this is the + /// best reading of where the sequence really started (`continuedSequenceAnchor`). + private var sequenceEnvelopeOrigin: Int64? /// Pictures emitted untouched because no anchor was available or the arithmetic did not /// close. Surfaced so a stream this repair does not describe is visible as a number. private(set) var unrepairedPictures = 0 @@ -460,6 +680,18 @@ enum H264CompositionOffsetRepair { sequenceAnchorDTS = nil sequenceBaseOrdinal = nil landingLeadingPictures = 0 + decodeOrdinal = 0 + anchoredOnLanding = false + sequenceLastDisplayIndex = nil + landingEnvelopeAnchor = nil + sequenceEnvelopeOrigin = nil + } + + /// The earliest origin the held landing pictures imply, `written DTS - pictureOffset(display + /// index)`, which the landing takes when it is earlier than its keyframe's own. Synthesized + /// ladders only. + mutating func noteLandingEnvelope(anchor: Int64?) { + landingEnvelopeAnchor = plan.synthesizesDecodeLadder ? anchor : nil } mutating func noteLanding(leadingPictures: Int64) { @@ -482,9 +714,13 @@ enum H264CompositionOffsetRepair { // point can be placed at all; mid-sequence keyframes must not re-anchor, or every open // GOP would restart the display axis under a picture that has not moved. if isKeyframe, pictureOrderCount == 0 { - sequenceAnchorDTS = dts + sequenceAnchorDTS = awaitingReanchor ? dts : continuedSequenceAnchor(keyframeDTS: dts) sequenceBaseOrdinal = plan.presentationOrdinal(ladderTimestamp: dts) awaitingReanchor = false + decodeOrdinal = 0 + anchoredOnLanding = false + sequenceLastDisplayIndex = nil + sequenceEnvelopeOrigin = nil } else if awaitingReanchor, isKeyframe { guard pictureOrderCount % plan.pocStep == 0 else { unrepairedPictures += 1 @@ -494,28 +730,55 @@ enum H264CompositionOffsetRepair { // The landing is decoded in slot k and displayed in slot k + leading, so the // sequence origin sits `landingIndex - leading` slots before its decode time. let (anchorSlots, slotsOverflow) = landingIndex.subtractingReportingOverflow(landingLeadingPictures) + // A synthesized ladder's keyframe kept its own presentation time (the writer only + // pushed the B pictures), so its origin is `landingIndex` slots back, not its decode slot. + let originSlots = plan.synthesizesDecodeLadder ? landingIndex : anchorSlots // Audit BIT-101: a 64-bit `tfdt` step and a landing order near -2^31 leave Int64. - let (landingTicks, productOverflow) = slotsOverflow - ? (0, true) : anchorSlots.multipliedReportingOverflow(by: plan.step) + let (landingTicks, productOverflow): (Int64, Bool) = slotsOverflow + ? (0, true) + : plan.synthesizesDecodeLadder + ? plan.pictureOffset(originSlots).map { ($0, false) } ?? (0, true) + : originSlots.multipliedReportingOverflow(by: plan.step) let (anchor, anchorOverflow) = dts.subtractingReportingOverflow(landingTicks) guard !productOverflow, !anchorOverflow else { unrepairedPictures += 1 return nil } - sequenceAnchorDTS = anchor + // Never further back than a continued sequence could have put it, so the segment plan + // built from the index still finds this keyframe (`keyframeAdvanceLimit`). + sequenceAnchorDTS = landingEnvelopeAnchor.map { + max(min($0, anchor), anchor &- plan.keyframeAdvanceLimit) + } ?? anchor + landingEnvelopeAnchor = nil sequenceBaseOrdinal = plan.presentationOrdinal(ladderTimestamp: dts) .flatMap { ordinal -> Int64? in - let (base, overflow) = ordinal.subtractingReportingOverflow(anchorSlots) + let (base, overflow) = ordinal.subtractingReportingOverflow(originSlots) return overflow ? nil : base } awaitingReanchor = false + decodeOrdinal = anchorSlots + anchoredOnLanding = true + sequenceEnvelopeOrigin = nil + sequenceLastDisplayIndex = nil + } + guard let anchor = sequenceAnchorDTS, !awaitingReanchor else { + unrepairedPictures += 1 + return nil } - guard let anchor = sequenceAnchorDTS, !awaitingReanchor, - pictureOrderCount >= 0, pictureOrderCount % plan.pocStep == 0 else { + let slot = decodeOrdinal + decodeOrdinal &+= 1 + let acceptsRelativeOrder = plan.synthesizesDecodeLadder && anchoredOnLanding + guard pictureOrderCount >= 0 || acceptsRelativeOrder, pictureOrderCount % plan.pocStep == 0 else { unrepairedPictures += 1 return nil } let displayIndex = pictureOrderCount / plan.pocStep + sequenceLastDisplayIndex = max(sequenceLastDisplayIndex ?? displayIndex, displayIndex) + if plan.synthesizesDecodeLadder, !anchoredOnLanding, displayIndex >= 0, + let offset = plan.pictureOffset(displayIndex) { + let (origin, overflow) = dts.subtractingReportingOverflow(offset) + if !overflow { sequenceEnvelopeOrigin = min(sequenceEnvelopeOrigin ?? origin, origin) } + } // On a lattice the picture is placed by its ordinal, which reproduces the muxer exactly // even where the fraction rounds the other way. Without one, or when the sequence could // not be read back onto the lattice, the rounded step still describes the stream to @@ -525,8 +788,14 @@ enum H264CompositionOffsetRepair { let (ordinal, overflow) = base.addingReportingOverflow(displayIndex) return overflow ? nil : plan.presentationTimestamp(ordinal: ordinal) } + ?? plan.pictureOffset(displayIndex).map { anchor &+ plan.shift &+ $0 } ?? (anchor &+ plan.shift &+ displayIndex &* plan.step) - let newDTS = dts &+ plan.shift &- plan.decodeLead + // A synthesized DTS sits `decodeLeadPictures` pictures behind its decode slot on the same + // lattice as the presentation times, so rounding cannot put it a tick past its own picture. + let newDTS = plan.synthesizesDecodeLadder + ? plan.pictureOffset(slot &- plan.decodeLeadPictures).map { anchor &+ plan.shift &+ $0 } + ?? (anchor &+ plan.shift &+ slot &* plan.step &- plan.decodeLead) + : dts &+ plan.shift &- plan.decodeLead // The muxer invariant. A picture that lands before its own decode time means the // arithmetic no longer describes this stream, and passing it through unchanged is // better than handing the muxer something it will silently clamp. @@ -537,6 +806,40 @@ enum H264CompositionOffsetRepair { repairedPictures += 1 return (pts, newDTS) } + + /// Where the IDR that follows a placed sequence starts. A forced-monotonic writer pushes the + /// keyframe too, and only ever later: on both reporting files the keyframes sat 0 to 4 pictures + /// after the end of the sequence before them, which left a gap or put the next GOP in front of + /// the last pictures of this one. A later keyframe therefore continues the sequence right after + /// its last picture, measured on `pictureCadence`. One that comes earlier than that is the better + /// reading, because the chain itself started on a pushed keyframe (a seek lands on whatever the + /// writer left there) and moves back each time a keyframe was pushed less. Within half a tick per + /// picture of the sequence the keyframe keeps its own time too: the writer's clock does not run + /// at exactly the stated rate (the third reporting file states 29.0002 fps and writes 29.003), + /// and following an on-time keyframe is what keeps the chain on it; a chain that only counted + /// drifted 57 ms behind over 19 minutes and then overlapped where an earlier keyframe pulled it + /// back. One later than the reorder head plus two and a half pictures is a real + /// discontinuity: the same file kept its keyframes four pictures (plus up to 31 ticks) past the + /// sequence before them for a hundred seconds. + private func continuedSequenceAnchor(keyframeDTS dts: Int64) -> Int64 { + guard plan.synthesizesDecodeLadder, let previous = sequenceAnchorDTS, + let last = sequenceLastDisplayIndex, last >= 0 else { return dts } + let (pictures, countOverflow) = last.addingReportingOverflow(1) + guard !countOverflow, let length = plan.pictureOffset(pictures) else { return dts } + let (expected, expectedOverflow) = previous.addingReportingOverflow(length) + let (drift, driftOverflow) = dts.subtractingReportingOverflow(expected) + guard !expectedOverflow, !driftOverflow else { return dts } + guard drift > pictures / 2 + 1, drift <= plan.keyframeAdvanceLimit else { return dts } + // Counting alone drifts with the writer's clock, so the continued sequence also leans + // toward where the pictures it follows say it ends, by at most an eighth of a picture per + // sequence: too little to see as a gap, enough to follow a clock 0.01% off the stated rate. + guard let origin = sequenceEnvelopeOrigin else { return expected } + let (envelope, envelopeOverflow) = origin.addingReportingOverflow(length) + guard !envelopeOverflow else { return expected } + let slew = plan.step / 8 + let leaned = expected &+ min(max(envelope &- expected, -slew), slew) + return min(dts, max(dts &- plan.keyframeAdvanceLimit, leaned)) + } } } @@ -549,6 +852,11 @@ final class H264PictureOrderReader { private var context: UnsafeMutablePointer? private let codecID: AVCodecID var isFramePicture: Bool { parser?.pointee.picture_structure == AV_PICTURE_STRUCTURE_FRAME } + /// The picture rate the bitstream's VUI timing states, once the parser has read a parameter set. + var frameRate: AVRational? { + guard let rate = context?.pointee.framerate, rate.num > 0, rate.den > 0 else { return nil } + return rate + } init?(codecParameters: UnsafePointer, timeBase: AVRational) { codecID = codecParameters.pointee.codec_id @@ -617,6 +925,10 @@ final class H264CompositionOffsetRepairSession { private let streamStartTime: Int64 private let ladderStart: Int64 private let codecName: String + /// Ticks per picture at the stream's average frame rate, for a head whose decode ladder has no step + /// of its own. + private let nominalStep: Int64? + private let timeBase: AVRational private var reader: H264PictureOrderReader? private var rewriter: H264CompositionOffsetRepair.Rewriter? private let partialRepairCandidate: H264PartialCompositionRepairSession? @@ -630,6 +942,9 @@ final class H264CompositionOffsetRepairSession { /// order while the packets behind it are held to count its leading pictures. private var relanding = false private var landingPictureOrder: Int64? + /// Index in `held` of the first picture displayed after the landing; the leading pictures are the + /// ones before it. + private var landingTrailingIndex: Int? /// nil unless this stream is the exact shape the defect needs: ISO-BMFF, H.264 or HEVC, and a /// bitstream that declares reordered pictures. Everything else never sees a parser or a held @@ -653,6 +968,9 @@ final class H264CompositionOffsetRepairSession { self.videoDelay = Int(codecpar.pointee.video_delay) self.streamStartTime = stream.pointee.start_time self.ladderStart = ladderStart + self.nominalStep = H264CompositionOffsetRepair.nominalFrameStep( + frameRate: stream.pointee.avg_frame_rate, timeBase: stream.pointee.time_base) + self.timeBase = stream.pointee.time_base self.reader = reader let (lead, overflow) = stream.pointee.start_time.subtractingReportingOverflow(ladderStart) // The partial-region repair reads H.264 NAL types to find its IDRs, so HEVC gets the @@ -687,6 +1005,15 @@ final class H264CompositionOffsetRepairSession { return rewriter.plan.shift - rewriter.plan.decodeLead } + /// How far before its index entry (read through `decodeTimestampOffset`) a repaired keyframe can + /// be placed. 0 when every keyframe stays on the index. Measured on the 23 fps reporting asset: a + /// cutter that wanted the keyframe at or past its entry missed 25 of 40 segments, which then + /// opened on audio mid-GOP. + var keyframeAdvanceTicks: Int64 { + guard phase == .repairing, let rewriter else { return 0 } + return rewriter.plan.keyframeAdvanceLimit + } + /// Returns true when the packet was taken over by the session and must not be emitted yet. /// A packet the session keeps is owned by it until `dequeue()` hands it back. func ingest(_ packet: UnsafeMutablePointer) -> Bool { @@ -714,7 +1041,7 @@ final class H264CompositionOffsetRepairSession { } held.append((packet, pictureOrderCount)) heldBytes += Int(max(0, packet.pointee.size)) - if samples.count >= H264CompositionOffsetRepair.sampleTarget + if (samples.count >= H264CompositionOffsetRepair.sampleTarget && !awaitsReordering) || heldBytes >= H264CompositionOffsetRepair.sampleByteBudget || held.count >= H264CompositionOffsetRepair.heldPacketCeiling || earlyHealthyVerdict { @@ -742,17 +1069,21 @@ final class H264CompositionOffsetRepairSession { return false } landingPictureOrder = pictureOrderCount - } else if let landing = landingPictureOrder, let pictureOrderCount, pictureOrderCount > landing { - held.append((packet, pictureOrderCount)) - finishLanding() - return true + } else if landingTrailingIndex == nil, let landing = landingPictureOrder, let pictureOrderCount, + pictureOrderCount > landing { + landingTrailingIndex = held.count } } else if landingPictureOrder == nil { return false } held.append((packet, pictureOrderCount)) heldBytes += Int(max(0, packet.pointee.size)) - if heldBytes >= H264CompositionOffsetRepair.sampleByteBudget + // A synthesized ladder holds a few pictures more to read the landing's envelope off them. + let wantsEnvelope = rewriter?.plan.synthesizesDecodeLadder == true + && held.lazy.filter { $0.packet.pointee.stream_index == self.streamIndex }.count + < H264CompositionOffsetRepair.landingEnvelopePictures + if (landingTrailingIndex != nil && !wantsEnvelope) + || heldBytes >= H264CompositionOffsetRepair.sampleByteBudget || held.count >= H264CompositionOffsetRepair.heldPacketCeiling { finishLanding() } @@ -763,12 +1094,14 @@ final class H264CompositionOffsetRepairSession { relanding = false guard var rewriter else { return } if let landing = landingPictureOrder { - let leading = held.filter { + let leading = held.prefix(landingTrailingIndex ?? held.count).filter { $0.packet.pointee.stream_index == streamIndex && ($0.pictureOrderCount.map { $0 < landing } ?? false) }.count rewriter.noteLanding(leadingPictures: Int64(leading)) + rewriter.noteLandingEnvelope(anchor: landingEnvelope(plan: rewriter.plan)) } + landingTrailingIndex = nil for entry in held where entry.packet.pointee.stream_index == streamIndex { applyRepair(to: entry.packet, pictureOrderCount: entry.pictureOrderCount, using: &rewriter) } @@ -776,6 +1109,43 @@ final class H264CompositionOffsetRepairSession { landingPictureOrder = nil } + /// The earliest origin the held landing pictures imply: each was written no earlier than its own + /// time, so `DTS - pictureOffset(display index)` over the landing's sequence bounds where the + /// sequence really starts. Stops at the next IDR, whose picture order starts over. + private func landingEnvelope(plan: H264CompositionOffsetRepair.Plan) -> Int64? { + guard plan.synthesizesDecodeLadder, plan.pocStep > 0 else { return nil } + var earliest: Int64? + var first = true + for entry in held where entry.packet.pointee.stream_index == streamIndex { + let isKeyframe = (entry.packet.pointee.flags & AV_PKT_FLAG_KEY) != 0 + guard let pictureOrderCount = entry.pictureOrderCount else { continue } + if !first, isKeyframe, pictureOrderCount == 0 { break } + first = false + let dts = entry.packet.pointee.dts + guard dts != Int64.min, pictureOrderCount % plan.pocStep == 0, + let offset = plan.pictureOffset(pictureOrderCount / plan.pocStep) else { continue } + let (origin, overflow) = dts.subtractingReportingOverflow(offset) + guard !overflow else { continue } + earliest = min(earliest ?? origin, origin) + } + return earliest + } + + /// The sample is past `sampleTarget` without a picture-order regression, every picture so far reports + /// `PTS == DTS`, and it may still grow: keep sampling until the first regression plus the rest of + /// its mini-GOP (the reorder delay and two pictures, which is what the window checks need to see + /// it close). A head that already reordered inside `sampleTarget` decides there, as before. + private var awaitsReordering: Bool { + guard samples.count < H264CompositionOffsetRepair.extendedSampleTarget, + samples.allSatisfy({ $0.dts != Int64.min && $0.pts == $0.dts && $0.pictureOrderCount >= 0 }) + else { return false } + guard let regression = samples.indices.dropFirst().first(where: { + samples[$0].pictureOrderCount < samples[$0 - 1].pictureOrderCount + }) else { return true } + guard regression >= H264CompositionOffsetRepair.sampleTarget else { return false } + return samples.count - regression < videoDelay + 2 + } + /// A healthy file usually proves itself on its first packet, and paying twelve packets of hold /// for it would tax every well-formed MP4 with B-frames on the platform. private var earlyHealthyVerdict: Bool { @@ -809,6 +1179,7 @@ final class H264CompositionOffsetRepairSession { reader?.reset() relanding = true landingPictureOrder = nil + landingTrailingIndex = nil case .off: break } @@ -847,11 +1218,23 @@ final class H264CompositionOffsetRepairSession { } private func decide() { + decide(using: nil) + } + + /// `nominalStep` is nil on the first pass, so the missing-head partial repair below keeps first + /// claim on a non-uniform ladder; only when it declines does the nominal-rate rebuild get a turn. + private func decide(using nominalStep: Int64?) { + let rate = nominalStep.flatMap { + H264CompositionOffsetRepair.rateCadence(bitstreamRate: reader?.frameRate, estimate: $0, timeBase: timeBase) + } let verdict = H264CompositionOffsetRepair.classify( samples: samples, videoDelay: videoDelay, streamStartTime: streamStartTime, - ladderStart: ladderStart + ladderStart: ladderStart, + // The span check reads the rate too, and the stated one is the better reading of it. + nominalStep: rate?.timestamp(at: 1) ?? nominalStep, + rateCadence: rate ) switch verdict { case .repair(let plan): @@ -864,6 +1247,7 @@ final class H264CompositionOffsetRepairSession { } ?? "" verdictDescription = "repair step=\(plan.step) lead=\(plan.decodeLead) " + "shift=\(plan.shift) pocStep=\(plan.pocStep)" + cadenceDescription + + (plan.synthesizesDecodeLadder ? " ladder=nominal-rate" : "") phase = .repairing var rewriter = H264CompositionOffsetRepair.Rewriter(plan: plan) for entry in held where entry.packet.pointee.stream_index == streamIndex { @@ -885,7 +1269,7 @@ final class H264CompositionOffsetRepairSession { verdictDescription = "healthy" disarm() case .inconclusive(let reason): - if reason == "decode ladder is not uniform", sampledFramePictures, + if reason == "decode ladder is not uniform", nominalStep == nil, sampledFramePictures, let candidate = partialRepairCandidate, let forwardDepth = H264PartialCompositionRepair.missingHeadForwardDepth( dts: samples.map(\.dts), pts: samples.map(\.pts), @@ -904,6 +1288,10 @@ final class H264CompositionOffsetRepairSession { samples.removeAll(keepingCapacity: false) return } + if reason == "decode ladder is not uniform", nominalStep == nil, let step = self.nominalStep { + decide(using: step) + return + } verdictDescription = "inconclusive (\(reason))" // Only worth a line when the stream looked like a candidate: a file that simply carries // composition offsets is the normal case and says nothing. diff --git a/Sources/AetherEngine/Video/H264MatroskaSlotPermutation.swift b/Sources/AetherEngine/Video/H264MatroskaSlotPermutation.swift index e8252c856..27d6f0a48 100644 --- a/Sources/AetherEngine/Video/H264MatroskaSlotPermutation.swift +++ b/Sources/AetherEngine/Video/H264MatroskaSlotPermutation.swift @@ -12,6 +12,8 @@ protocol H264TimestampRepairSession: AnyObject { var isDecided: Bool { get } /// Ticks the container's own index has to move by to describe the same axis as the packets. var decodeTimestampOffset: Int64? { get } + /// Ticks a delivered keyframe may sit before its index entry once `decodeTimestampOffset` is applied. + var keyframeAdvanceTicks: Int64 { get } /// True when the packet was taken over and must not be emitted yet. func ingest(_ packet: UnsafeMutablePointer) -> Bool func dequeue() -> UnsafeMutablePointer? @@ -452,6 +454,7 @@ final class H264MatroskaSlotPermutationSession: H264TimestampRepairSession { /// keyframe is the first picture of its own sequence, and a permutation leaves that slot exactly /// where the container wrote it. The index and the packets already describe one axis. var decodeTimestampOffset: Int64? { nil } + var keyframeAdvanceTicks: Int64 { 0 } func ingest(_ packet: UnsafeMutablePointer) -> Bool { guard phase != .off else { return false } diff --git a/Sources/AetherEngine/Video/HLSSegmentProducer.swift b/Sources/AetherEngine/Video/HLSSegmentProducer.swift index f5d155d9a..a00a98528 100644 --- a/Sources/AetherEngine/Video/HLSSegmentProducer.swift +++ b/Sources/AetherEngine/Video/HLSSegmentProducer.swift @@ -540,6 +540,12 @@ final class HLSSegmentProducer: @unchecked Sendable { /// AE#561: the axis `segmentBoundaries` are stamped on, so the cutter gate compares like with /// like. Decode for a mov/mp4 index, presentation for Matroska Cues; see `PlanBoundaryAxis`. private let planBoundaryAxis: PlanBoundaryAxis + /// `VODSegmentCutter.reachTicks` of the restart boundary, which the gate honours too: the + /// keyframe that boundary names can arrive that far before it, and a gate that skipped it opened + /// a GOP late (or re-aimed). Only for the boundary itself, never for a re-aim. + private var restartGateReachTicks: Int64 { + effectiveGateTargetPts == restartTargetVideoPts ? vodCutter.reach(at: 0) : 0 + } /// AE#408: the gate target actually in force. Starts at `restartTargetVideoPts` and moves BACK when /// the boundary turns out not to be openable, so the segment covers its own advertised start @@ -1008,10 +1014,11 @@ final class HLSSegmentProducer: @unchecked Sendable { /// presentation time is the most permissive reading and cannot starve on a skew in either /// direction. What the axis buys here is knowing how much of the resulting overshoot is real, /// which is what `boundaryOpenToleranceTicks` spends it on. - static func videoGateTargetSatisfied(pts: Int64, dts: Int64, targetPts: Int64) -> Bool { + static func videoGateTargetSatisfied(pts: Int64, dts: Int64, targetPts: Int64, + reachTicks: Int64 = 0) -> Bool { if targetPts == Int64.min { return true } let ts = pts != Int64.min ? pts : dts - return ts != Int64.min && ts >= targetPts + return ts != Int64.min && SourceTimestampBounds.sum(ts, max(0, reachTicks)) >= targetPts } // MARK: - AE#408: a boundary that is not a random-access point @@ -1514,6 +1521,7 @@ final class HLSSegmentProducer: @unchecked Sendable { restartTargetVideoPts: Int64 = Int64.min, boundaryClaimsRandomAccess: Bool = false, planBoundaryAxis: PlanBoundaryAxis = .decode, + keyframeReachTicks: [Int64] = [], closedCaptionStreamIndex: Int32 = -1, subtitleTapStreamIndices: Set = [], subtitlePacketStreamIndices: Set = [], @@ -1589,7 +1597,8 @@ final class HLSSegmentProducer: @unchecked Sendable { self.vodCutter = VODSegmentCutter( sourceBoundaries: segmentBoundaries, planAnchorPts: planAnchorVideoPts, - baseIndex: baseIndex + baseIndex: baseIndex, + reachTicks: keyframeReachTicks ) self.isLive = isLive self.foldsSequentialTimeline = foldsSequentialTimeline @@ -4038,7 +4047,7 @@ final class HLSSegmentProducer: @unchecked Sendable { let isKey = (packet.pointee.flags & AV_PKT_FLAG_KEY) != 0 let targetSatisfied = Self.videoGateTargetSatisfied( pts: packet.pointee.pts, dts: packet.pointee.dts, - targetPts: effectiveGateTargetPts) + targetPts: effectiveGateTargetPts, reachTicks: isKey && !isLive ? restartGateReachTicks : 0) // #133: on a live H.264 Annex-B mid-stream join, opening on a bare keyframe flag is not // enough. A join packet must carry a decodable access unit (in-band SPS+PPS and an // IDR or immediate intra recovery point, AE#627); otherwise the decoder renders references diff --git a/Sources/AetherEngine/Video/HLSVideoEngine+SegmentPlanning.swift b/Sources/AetherEngine/Video/HLSVideoEngine+SegmentPlanning.swift index 2d86daced..ef0598cb0 100644 --- a/Sources/AetherEngine/Video/HLSVideoEngine+SegmentPlanning.swift +++ b/Sources/AetherEngine/Video/HLSVideoEngine+SegmentPlanning.swift @@ -69,6 +69,40 @@ extension HLSVideoEngine { /// producer can never finish and AVPlayer waits on forever. The 60 s default leaves room for a /// long final GOP and for a container duration padded by a trailing audio or subtitle track. /// + /// The cutter's and the restart gate's reach for each boundary of a keyframe-aligned plan: + /// `advance` (how far a repaired keyframe can precede its index entry), held below the distance + /// to the keyframe before that boundary so that one never reaches it. Per boundary, not one + /// figure for the plan: the 23 fps reporting asset keeps its keyframes 15 s apart but has one + /// pair four pictures apart, and a plan-wide bound left every other boundary without the reach + /// it needed. Empty when nothing moves. + static func keyframeReachTicks(advance: Int64, keyframes: [Int64], boundaries: [Int64]) -> [Int64] { + guard advance > 0 else { return [] } + let sorted = keyframes.sorted() + return boundaries.map { boundary in + var low = 0, high = sorted.count + while low < high { + let mid = (low + high) / 2 + if sorted[mid] < boundary { low = mid + 1 } else { high = mid } + } + guard low > 0 else { return advance } + let (gap, overflow) = boundary.subtractingReportingOverflow(sorted[low - 1]) + return overflow ? 0 : max(0, min(advance, gap - 1)) + } + } + + /// `collapseShortSegments`, then the reach for each boundary of the plan it leaves. The order is + /// the point: a pair of keyframes a few pictures apart is what makes a segment that gets folded, + /// and reaches read off the unfolded plan sat one boundary off from that fold on (three folds on + /// the 23 fps reporting asset). Off by one, a boundary takes the reach of the one before, which + /// can let the earlier keyframe of the pair open it, and hands its own to the next boundary, + /// whose keyframe can then miss it. + static func finalizedPlan(_ plan: [Segment], minDurationSeconds: Double, keyframeAdvance: Int64, + keyframes: [Int64]) -> (plan: [Segment], reach: [Int64]) { + let collapsed = collapseShortSegments(plan, minDurationSeconds: minDurationSeconds) + return (collapsed, keyframeReachTicks(advance: keyframeAdvance, keyframes: keyframes, + boundaries: collapsed.map(\.startPts))) + } + /// An index failing any witness is routed to the uniform-stride fallback. static func keyframeIndexIsTrustworthy( keyframes: [Int64], diff --git a/Sources/AetherEngine/Video/HLSVideoEngine.swift b/Sources/AetherEngine/Video/HLSVideoEngine.swift index 70227e17e..aa1ce5e87 100644 --- a/Sources/AetherEngine/Video/HLSVideoEngine.swift +++ b/Sources/AetherEngine/Video/HLSVideoEngine.swift @@ -861,6 +861,13 @@ public final class HLSVideoEngine: @unchecked Sendable { /// which is what the cutter compared before the axis existed. var planBoundaryAxis: PlanBoundaryAxis = .decode + /// How far before its boundary the keyframe each `segmentPlan` entry names may arrive + /// (`VODSegmentCutter.reachTicks`). Empty unless a #409 repair moves keyframes off the index. + /// Set with the final plan by `finalizedPlan`, never off the plan before it is collapsed. + var planKeyframeReachTicks: [Int64] = [] + /// `Demuxer.indexedKeyframeAdvanceTicks` of a keyframe-aligned plan, held until the plan is final. + var planKeyframeAdvanceTicks: Int64 = 0 + /// Guards subsystem refs + `sessionEpoch`. Never held across waits or network I/O so /// `stop()` on the main thread is never blocked behind a restart's 5 s waitForFinish. let restartLock = NSLock() @@ -1456,6 +1463,7 @@ public final class HLSVideoEngine: @unchecked Sendable { let durationSeconds = dem.duration var plan: [Segment] var keyframeBytePositions: [Int64: Int64] = [:] + planKeyframeAdvanceTicks = 0 if isLiveSession { sourceBitrate = dem.bitRate self.firstKeyframePts = 0 @@ -1559,6 +1567,7 @@ public final class HLSVideoEngine: @unchecked Sendable { // stamping. Read from the demuxer that produced them, never from the URL or the // host's metadata: on a remux session the delivered container is the one indexed. planBoundaryAxis = PlanBoundaryAxis.forContainer(formatName: dem.containerFormatName) + planKeyframeAdvanceTicks = dem.indexedKeyframeAdvanceTicks let firstKeyframePts = keyframes.sorted().first ?? 0 self.firstKeyframePts = firstKeyframePts let firstKeyframeSeconds = Double(firstKeyframePts) * Double(videoTimeBase.num) / Double(videoTimeBase.den) @@ -1883,7 +1892,9 @@ public final class HLSVideoEngine: @unchecked Sendable { // Fold degenerate sub-frame segments (keyframe clusters make buildKeyframeSegmentPlan emit // ~40 ms segments the producer cannot cut, wedging the near-EOF resume; the plan and producer // share these boundaries, so the merge fixes both at once). - plan = Self.collapseShortSegments(plan, minDurationSeconds: Self.minSegmentDurationSeconds) + (plan, planKeyframeReachTicks) = Self.finalizedPlan( + plan, minDurationSeconds: Self.minSegmentDurationSeconds, + keyframeAdvance: planKeyframeAdvanceTicks, keyframes: Array(keyframeBytePositions.keys)) self.segmentPlan = plan if !keyframeBytePositions.isEmpty { sourcePeakBitrate = Self.peakSegmentBitrate(plan: plan, keyframeBytePositions: keyframeBytePositions) @@ -3482,6 +3493,7 @@ public final class HLSVideoEngine: @unchecked Sendable { restartTargetVideoPts: videoTarget, boundaryClaimsRandomAccess: planBoundariesClaimRandomAccess, planBoundaryAxis: planBoundaryAxis, + keyframeReachTicks: Array(planKeyframeReachTicks.dropFirst(min(baseIndex, planKeyframeReachTicks.count))), closedCaptionStreamIndex: closedCaptionStreamIndexForSession, subtitleTapStreamIndices: Set(nativeSubtitleSourceStreamIndicesForSession.compactMap { $0 }), subtitlePacketStreamIndices: allEmbeddedSubtitleStreamIndices, // #112 rework diff --git a/Sources/AetherEngine/Video/VODSegmentCutter.swift b/Sources/AetherEngine/Video/VODSegmentCutter.swift index 0b02d4c5d..62b1fde5d 100644 --- a/Sources/AetherEngine/Video/VODSegmentCutter.swift +++ b/Sources/AetherEngine/Video/VODSegmentCutter.swift @@ -36,14 +36,27 @@ struct VODSegmentCutter { /// never advance). let boundaries: [Int64] let baseIndex: Int + /// How far before each boundary (same local indices as `boundaries`, missing entries 0) the + /// keyframe that boundary names may arrive. A #409 repair that synthesizes the decode ladder + /// places a late-written IDR back on its sequence, before the index entry the plan was built from + /// (`Demuxer.indexedKeyframeAdvanceTicks`); without the reach that keyframe stays in the previous + /// segment and the next one opens mid-GOP (25 of 40 segments on the 23 fps reporting asset). The + /// engine keeps each reach below the distance to the keyframe before, so that one never reaches. + let reachTicks: [Int64] private(set) var current: Int - init(sourceBoundaries: [Int64], planAnchorPts: Int64, baseIndex: Int) { + init(sourceBoundaries: [Int64], planAnchorPts: Int64, baseIndex: Int, reachTicks: [Int64] = []) { self.boundaries = sourceBoundaries.map { $0 &- planAnchorPts } self.baseIndex = baseIndex + self.reachTicks = reachTicks.map { max(0, $0) } self.current = baseIndex } + /// The reach of local boundary `index`. + func reach(at index: Int) -> Int64 { + index >= 0 && index < reachTicks.count ? reachTicks[index] : 0 + } + /// Segment index for a video packet, in decode order, from its ITEM-axis presentation time. /// Advances on a keyframe that has reached the next boundary; every other packet (and an /// intra-segment keyframe that has not yet reached the next boundary, e.g. when the GOP is shorter @@ -53,7 +66,8 @@ struct VODSegmentCutter { // boundaries[count-1] is the end of the final segment, not a segment start, so the last segment // a keyframe can open is local (count-2): advance only while the next entry is a real start. var nextLocal = (current - baseIndex) + 1 - while nextLocal < boundaries.count - 1, pts >= boundaries[nextLocal] { + while nextLocal < boundaries.count - 1, + SourceTimestampBounds.sum(pts, reach(at: nextLocal)) >= boundaries[nextLocal] { current += 1 nextLocal += 1 } diff --git a/Tests/AetherEngineTests/DocumentedConstantsTests.swift b/Tests/AetherEngineTests/DocumentedConstantsTests.swift index baeec4f1f..412fcb447 100644 --- a/Tests/AetherEngineTests/DocumentedConstantsTests.swift +++ b/Tests/AetherEngineTests/DocumentedConstantsTests.swift @@ -46,6 +46,21 @@ final class DocumentedConstantsTests: XCTestCase { assertDocumented("five entries per item", docs) } + func testNominalRateCompositionShiftMatchesDocumentation() throws { + XCTAssertEqual(H264CompositionOffsetRepair.maximumNominalShiftPictures, 16) + assertDocumented("clamped to **16 pictures**", try documentation()) + } + + func testCompositionSampleExtensionMatchesDocumentation() throws { + XCTAssertEqual(H264CompositionOffsetRepair.extendedSampleTarget, 48) + assertDocumented("up to\n**48 pictures**", try documentation()) + } + + func testLandingEnvelopeHoldMatchesDocumentation() throws { + XCTAssertEqual(H264CompositionOffsetRepair.landingEnvelopePictures, 8) + assertDocumented("first **8 pictures**", try documentation()) + } + func testPartialCompositionHoldBoundsMatchDocumentation() throws { let docs = try documentation() XCTAssertEqual(H264PartialCompositionRepair.maximumReorderDepth, 16) diff --git a/Tests/AetherEngineTests/H264ForcedMonotonicLadderRepairTests.swift b/Tests/AetherEngineTests/H264ForcedMonotonicLadderRepairTests.swift new file mode 100644 index 000000000..33314eaa3 --- /dev/null +++ b/Tests/AetherEngineTests/H264ForcedMonotonicLadderRepairTests.swift @@ -0,0 +1,2224 @@ +import Foundation +import AetherLibavcodec +import Testing +@testable import AetherEngine + +/// An MP4 writer that dropped `ctts` and then forced its decode-order presentation times to be +/// monotonic. Every sample reports `PTS == DTS`, anchors keep their own time, and each B picture is +/// pushed 1001 ticks past the picture decoded before it, so the decode ladder has no step for the #409 +/// classifier to read. The pictures still come at a constant rate, and the repair rebuilds both axes +/// from the picture order and that rate. +@Suite("H.264 missing composition offsets on a forced-monotonic ladder") +struct H264ForcedMonotonicLadderRepairTests { + + private struct Timestamps: Equatable, CustomStringConvertible { + var pts: Int64 + var dts: Int64 + var description: String { "pts=\(pts) dts=\(dts)" } + } + + private static func videoTimestamps(base64: String, seekingTo seconds: Double? = nil) throws -> [Timestamps] { + let data = try #require(Data(base64Encoded: base64, options: .ignoreUnknownCharacters)) + let demuxer = Demuxer() + try demuxer.open(reader: DataIOReader(data: data), formatHint: "mp4") + defer { demuxer.close() } + if let seconds { #expect(demuxer.seek(to: seconds)) } + let videoIndex = demuxer.videoStreamIndex + var result: [Timestamps] = [] + while let packet = try? demuxer.readPacket() { + if packet.pointee.stream_index == videoIndex { + result.append(Timestamps(pts: packet.pointee.pts, dts: packet.pointee.dts)) + } + var owned: UnsafeMutablePointer? = packet + trackedPacketFree(&owned) + } + return result + } + + @Test("the clamped twin is repaired to the healthy twin's timestamps") + func clampedLadderMatchesHealthyTwin() throws { + let healthy = try Self.videoTimestamps(base64: Self.healthyFixtureBase64) + let clamped = try Self.videoTimestamps(base64: Self.clampedFixtureBase64) + #expect(healthy.count == 60) + #expect(healthy.contains { $0.pts != $0.dts }) + #expect(clamped == healthy) + } + + @Test("a seek onto the second GOP lands on the healthy axis") + func seekLandsOnHealthyAxis() throws { + let healthy = try Self.videoTimestamps(base64: Self.healthyFixtureBase64, seekingTo: 1.2) + let clamped = try Self.videoTimestamps(base64: Self.clampedFixtureBase64, seekingTo: 1.2) + #expect(!healthy.isEmpty) + #expect(clamped == healthy) + } + + @Test("a seek onto an open-GOP keyframe lands on the healthy axis") + func openGOPSeekLandsOnHealthyAxis() throws { + let healthy = try Self.videoTimestamps(base64: Self.openGOPHealthyFixtureBase64, seekingTo: 1.2) + let clamped = try Self.videoTimestamps(base64: Self.openGOPClampedFixtureBase64, seekingTo: 1.2) + #expect(!healthy.isEmpty) + #expect(clamped == healthy) + } + + @Test("the open-GOP clamped twin plays through on the healthy axis") + func openGOPClampedLadderMatchesHealthyTwin() throws { + let healthy = try Self.videoTimestamps(base64: Self.openGOPHealthyFixtureBase64) + let clamped = try Self.videoTimestamps(base64: Self.openGOPClampedFixtureBase64) + #expect(healthy.count == 60) + #expect(clamped == healthy) + } + + @Test("a twin whose anchors were pushed too is repaired to the healthy twin's timestamps") + func pushedAnchorsMatchHealthyTwin() throws { + let healthy = try Self.videoTimestamps(base64: Self.healthyFixtureBase64) + let pushed = try Self.videoTimestamps(base64: Self.pushedAnchorsFixtureBase64) + #expect(healthy.count == 60) + #expect(pushed == healthy) + } + + @Test("a seek on the pushed-anchor twin lands on the healthy axis") + func pushedAnchorsSeekLandsOnHealthyAxis() throws { + let healthy = try Self.videoTimestamps(base64: Self.healthyFixtureBase64, seekingTo: 1.2) + let pushed = try Self.videoTimestamps(base64: Self.pushedAnchorsFixtureBase64, seekingTo: 1.2) + #expect(!healthy.isEmpty) + #expect(pushed == healthy) + } + + @Test("a keyframe the writer pushed late continues its sequence on the healthy axis") + func pushedKeyframeContinuesSequence() throws { + let healthy = try Self.videoTimestamps(base64: Self.healthyFixtureBase64) + let pushed = try Self.videoTimestamps(base64: Self.pushedKeyframeFixtureBase64) + #expect(healthy.count == 60) + #expect(pushed == healthy) + } + + @Test("a keyframe moved back onto its sequence stays within the reach its index entry is read with") + func movedKeyframeStaysWithinIndexReach() throws { + let data = try #require(Data(base64Encoded: Self.pushedKeyframeFixtureBase64, options: .ignoreUnknownCharacters)) + let demuxer = Demuxer() + try demuxer.open(reader: DataIOReader(data: data), formatHint: "mp4") + defer { demuxer.close() } + let videoIndex = demuxer.videoStreamIndex + var keyframes: [Int64] = [] + while let packet = try? demuxer.readPacket() { + if packet.pointee.stream_index == videoIndex, packet.pointee.flags & AV_PKT_FLAG_KEY != 0 { + keyframes.append(packet.pointee.dts) + } + var owned: UnsafeMutablePointer? = packet + trackedPacketFree(&owned) + } + let index = demuxer.indexedKeyframes(streamIndex: videoIndex).sorted() + let advance = demuxer.indexedKeyframeAdvanceTicks + // Reorder delay 2 plus two and a half pictures at 29.97 fps. + #expect(advance == 6006 + 2 * 3003 + 1501) + #expect(keyframes.count == index.count) + // The pushed keyframe is delivered before its entry, which is why a cutter needs the reach. + #expect(zip(keyframes, index).contains { $0 < $1 }) + #expect(zip(keyframes, index).allSatisfy { $0 <= $1 && $0 >= $1 - advance }) + let reach = HLSVideoEngine.keyframeReachTicks(advance: advance, keyframes: index, boundaries: index) + #expect(zip(zip(keyframes, index), reach).allSatisfy { pair, reach in pair.0 + reach >= pair.1 }) + } + + @Test("a continued sequence leans toward the earliest origin its pictures point to, an eighth of a picture at a time") + func continuedSequenceLeansTowardEnvelope() { + var plan = H264CompositionOffsetRepair.Plan(step: 3000, decodeLead: 6000, shift: 0, pocStep: 2) + plan.synthesizesDecodeLadder = true + plan.pictureCadence = H264CompositionOffsetRepair.Cadence(numerator: 3000, denominator: 1) + plan.decodeLeadPictures = 2 + var rewriter = H264CompositionOffsetRepair.Rewriter(plan: plan) + // The writer pushed the first IDR 600 ticks late and left the rest of its sequence on time, + // so counting from the IDR runs 600 ticks behind where the sequence really is. + _ = rewriter.rewrite(dts: 600, pictureOrderCount: 0, isKeyframe: true) + for picture in 1..<10 { + _ = rewriter.rewrite(dts: Int64(picture) * 3000, pictureOrderCount: Int64(picture) * 2, isKeyframe: false) + } + // The next IDR is pushed three pictures: it continues the sequence at 30 600 by count, and + // leans 3000 / 8 ticks toward the 30 000 the on-time pictures point to. + let idr = rewriter.rewrite(dts: 39_000, pictureOrderCount: 0, isKeyframe: true) + #expect(idr.map(\.pts) == Int64(30_600 - 375)) + } + + @Test("a head that opens on a run of P pictures is sampled until it reorders") + func leadingPRunIsSampledUntilReordering() throws { + let healthy = try Self.videoTimestamps(base64: Self.leadingPRunHealthyFixtureBase64) + let clamped = try Self.videoTimestamps(base64: Self.leadingPRunClampedFixtureBase64) + #expect(healthy.count == 60) + #expect(clamped.count == healthy.count) + #expect(clamped.contains { $0.pts != $0.dts }) + func ranks(_ values: [Int64]) -> [Int] { + let sorted = values.sorted() + return values.map { sorted.firstIndex(of: $0) ?? -1 } + } + #expect(ranks(clamped.map(\.pts)) == ranks(healthy.map(\.pts))) + let presentation = clamped.map(\.pts).sorted() + #expect(zip(presentation, presentation.dropFirst()).allSatisfy { $1 - $0 == 3003 }) + } + + @Test("classify takes the step from the anchors, not the rounded average rate") + func classifyMeasuresAnchorStep() { + // Decode order I0 P4 B2 b1 b3 P8 B6 b5 b7 P12 B10 b9: what the writer left on the ladder. + let pocs: [Int64] = [0, 8, 4, 2, 6, 16, 12, 10, 14, 24, 20, 18] + let ladder: [Int64] = [0, 12012, 13013, 14014, 15015, 24024, 25025, 26026, 27027, 36036, 37037, 38038] + let samples = zip(pocs, ladder).enumerated().map { index, pair in + H264CompositionOffsetRepair.Sample(dts: pair.1, pts: pair.1, pictureOrderCount: pair.0, isKeyframe: index == 0) + } + let withoutRate = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: 0) + #expect(withoutRate == .inconclusive("decode ladder is not uniform")) + guard case .repair(let plan) = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: 0, nominalStep: 3020) + else { + Issue.record("expected a repair") + return + } + #expect(plan.synthesizesDecodeLadder) + #expect(plan.step == 3003) + #expect(plan.decodeLead == 6006) + #expect(plan.shift == 0) + } + + @Test("a head whose anchors disagree on the step is left alone without a standard rate") + func disagreeingAnchorsAreInconclusive() { + let pocs: [Int64] = [0, 8, 4, 2, 6, 16, 12, 10, 14, 24, 20, 18] + // The span fits the nominal rate, but the second anchor sits 2000 ticks off the step the + // others measure, so the rounded average rate would be the only source of a step. + let ladder: [Int64] = [0, 12012, 13013, 14014, 15015, 26024, 27025, 28026, 29027, 36036, 37037, 38038] + let samples = zip(pocs, ladder).enumerated().map { index, pair in + H264CompositionOffsetRepair.Sample(dts: pair.1, pts: pair.1, pictureOrderCount: pair.0, isKeyframe: index == 0) + } + let verdict = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: 0, nominalStep: 3003) + #expect(verdict == .inconclusive("anchors do not agree on a step")) + } + + @Test("a head that pushed its anchors too takes the standard rate the estimate names") + func pushedAnchorsTakeStandardStep() { + // The reporting file's head: the keyframe four pictures ahead, and P pictures pushed 1001 + // ticks past the picture before them just like the B pictures, so no two anchors agree. + let pocs: [Int64] = [0, 2, 8, 4, 6, 10, 18, 14, 12, 16, 26, 22] + let ladder: [Int64] = [-12012, 0, 1001, 2002, 3003, 15015, 18018, 21021, 24024, 27027, 29279, 31531] + let samples = zip(pocs, ladder).enumerated().map { index, pair in + H264CompositionOffsetRepair.Sample(dts: pair.1, pts: pair.1, pictureOrderCount: pair.0, isKeyframe: index == 0) + } + let withoutStandard = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: -12012, nominalStep: 3003) + #expect(withoutStandard == .inconclusive("anchors do not agree on a step")) + guard case .repair(let plan) = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: -12012, + nominalStep: 3003, rateCadence: H264CompositionOffsetRepair.Cadence(numerator: 3003, denominator: 1)) + else { + Issue.record("expected a repair") + return + } + #expect(plan.synthesizesDecodeLadder) + #expect(plan.step == 3003) + #expect(plan.decodeLead == 6006) + #expect(plan.shift == 12012) + } + + @Test("a head four pictures ahead of a rounded 29 fps step still fits the nominal rate") + func roundedStepHeadFitsNominalRate() { + // The third reporting file's head: 29 fps is 3103.45 ticks, the step rounds to 3103, and the + // ladder runs exactly the reorder head plus two pictures ahead, 3 ticks past the bound without + // a tick per picture for the rounding. + let pocs: [Int64] = [0, 8, 4, 2, 6, 16, 12, 10, 14, 22, 18, 20] + let ladder: [Int64] = [0, 6207, 7758, 9309, 24826, 27153, 29480, 31807, 34134, 38272, 42410, 46548] + let samples = zip(pocs, ladder).enumerated().map { index, pair in + H264CompositionOffsetRepair.Sample(dts: pair.1, pts: pair.1, pictureOrderCount: pair.0, isKeyframe: index == 0) + } + #expect(H264CompositionOffsetRepair.nominalLadderFits(samples: samples, step: 3103, videoDelay: 2)) + guard case .repair(let plan) = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: 0, + nominalStep: 3103, rateCadence: H264CompositionOffsetRepair.Cadence(numerator: 90000, denominator: 29)) + else { + Issue.record("expected a repair") + return + } + #expect(plan.step == 3103) + // The offsets come off the exact 29 fps length, so a sequence of 1000 pictures ends where + // 29 fps puts it rather than 450 ticks early on the rounded step. + #expect(plan.pictureOffset(1000) == 3_103_448) + } + + @Test("the standard cadence is the nearest standard rate within 1%, as an exact fraction") + func standardFrameCadenceSnapsToNearestRate() { + let ninetyKilohertz = AVRational(num: 1, den: 90000) + func ticks(_ estimate: Int64, _ timeBase: AVRational = ninetyKilohertz) -> (Int64, Int64)? { + H264CompositionOffsetRepair.standardFrameCadence(near: estimate, timeBase: timeBase) + .map { ($0.numerator, $0.denominator) } + } + // 29.97 and 30 are both within 1% of each other; the nearer one wins. + #expect(ticks(3003)! == (3003, 1)) + #expect(ticks(3001)! == (3000, 1)) + // A 60-picture 29.97 clip reports 29.80. + #expect(ticks(3020)! == (3003, 1)) + #expect(ticks(3600)! == (3600, 1)) + // Whole rates count too, kept as the fraction they are: 23 fps is 3913.04 ticks (the second + // reporting file), 23.976 is 3753.75 rather than 24 fps's 3750. + #expect(ticks(3913)! == (90000, 23)) + #expect(ticks(3754)! == (15015, 4)) + // Half a frame per second is further than 1% from every standard rate. + #expect(ticks(180_000) == nil) + #expect(ticks(1001, AVRational(num: 1, den: 24000))! == (1001, 1)) + } + + @Test("the bitstream's stated rate wins over an average that cannot tell 24 from 23.976") + func bitstreamRateNamesTheCadence() { + let ninetyKilohertz = AVRational(num: 1, den: 90000) + func cadence(_ rate: AVRational?, _ estimate: Int64) -> (Int64, Int64)? { + H264CompositionOffsetRepair.rateCadence(bitstreamRate: rate, estimate: estimate, timeBase: ninetyKilohertz) + .map { ($0.numerator, $0.denominator) } + } + // A 24 fps clip with a pushed tail averages 23.98: the average alone picks 23.976. + #expect(cadence(nil, 3753)! == (15015, 4)) + #expect(cadence(AVRational(num: 24, den: 1), 3753)! == (3750, 1)) + // 30000/1001 stays 29.97 and does not snap to 30, which is 0.1% away. + #expect(cadence(AVRational(num: 30000, den: 1001), 3003)! == (3003, 1)) + // The third reporting file states 590325/20356 (29.0002 fps), which is 29 to within 0.01%. + #expect(cadence(AVRational(num: 590325, den: 20356), 3103)! == (90000, 29)) + // A stated rate that disagrees with the stream by more than 1% is not believed. + #expect(cadence(AVRational(num: 60, den: 1), 3003)! == (3003, 1)) + } + + @Test("a variable-rate head is still left alone") + func variableRateHeadIsInconclusive() { + let pocs: [Int64] = [0, 8, 4, 2, 6, 16, 12, 10, 14, 24, 20, 18] + // Same order, but the span is twice what twelve pictures take at the nominal rate. + let ladder: [Int64] = [0, 24024, 26026, 28028, 30030, 48048, 50050, 52052, 54054, 72072, 74074, 76076] + let samples = zip(pocs, ladder).enumerated().map { index, pair in + H264CompositionOffsetRepair.Sample(dts: pair.1, pts: pair.1, pictureOrderCount: pair.0, isKeyframe: index == 0) + } + let verdict = H264CompositionOffsetRepair.classify( + samples: samples, videoDelay: 2, streamStartTime: 0, ladderStart: 0, nominalStep: 3003) + #expect(verdict == .inconclusive("decode ladder is not uniform")) + } + + /// 64x64 H.264 High, 29.97 fps, 60 frames, three B pictures per pyramid mini-GOP, two closed GOPs. + /// + /// ffmpeg -f lavfi -i 'testsrc=s=64x64:r=30000/1001:d=3' -frames:v 60 -c:v libx264 \ + /// -preset ultrafast -pix_fmt yuv420p -bf 3 \ + /// -x264-params 'b-pyramid=normal:b-adapt=0:keyint=30:min-keyint=30:scenecut=0' \ + /// -video_track_timescale 90000 -movflags +faststart healthy.mp4 + /// ffmpeg -i healthy.mp4 -map 0:v:0 -c:v copy -bsf:v 'setts=ts=max(PTS\,PREV_OUTDTS+1001)' \ + /// -video_track_timescale 90000 -movflags +faststart clamped.mp4 + static let healthyFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAX8bW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAB1MAAA6pwAAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BSd0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAA6pwAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAOqcAAAXdgABAAAAAASfbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACv9RVxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAAESm1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAABApzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFExhdmM2My4xLjEwMiBsaWJ4 + MjY0AAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTAHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAD3gAAAAAAAAAAYc3R0cwAAAAAAAAABAAAAPAAAC7sAAAAYc3RzcwAAAAAAAAACAAAAAQAAAB8AAAHo + Y3R0cwAAAAAAAAA7AAAAAQAAF3YAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAAAAEAADqnAAAAAQAAF3YAAAABAAAA + AAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAA + AAEAADqnAAAAAQAAF3YAAAABAAAAAAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAA6pwAAAAEA + ABd2AAAAAQAAAAAAAAABAAALuwAAAAIAABd2AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAA6pwAAAAEAABd2 + AAAAAQAAAAAAAAABAAALuwAAAAEAADqnAAAAAQAAF3YAAAABAAAAAAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAA + AQAAC7sAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAAAAEAADqnAAAAAQAAF3YAAAABAAAAAAAAAAEAAAu7AAAAAQAA + OqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAAXdgAAABxzdHNjAAAAAAAAAAEAAAABAAAAPAAAAAEAAAEEc3RzegAAAAAA + AAAAAAAAPAAAB9AAAAP1AAAA0gAAAHUAAAAqAAACRgAAAFgAAAAxAAAAJQAAAssAAABaAAAAKgAAACkAAAJDAAAAfgAAADIAAAAe + AAACMAAAAGsAAAAvAAAALQAAAkYAAAB+AAAAMQAAADsAAAIHAAAAYQAAADAAAAA/AAABBwAACNcAAAG6AAAAQQAAACkAAAAwAAAB + 8gAAAE0AAAApAAAAKQAAAiMAAABPAAAANQAAACsAAAIiAAAAagAAACcAAAAzAAACMQAAAHIAAAA1AAAAIwAAAh4AAABuAAAAJAAA + ACAAAAIzAAAAdwAAACsAAAAhAAABAwAAABRzdGNvAAAAAAAAAAEAAAYsAAAAYXVkdGEAAABZbWV0YQAAAAAAAAAhaGRscgAAAAAA + AAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAsaWxzdAAAACSpdG9vAAAAHGRhdGEAAAABAAAAAExhdmY2My4xLjEwMgAAAAhmcmVlAAA9 + +G1kYXQAAAKeBgX//5rcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTY1IHIzMjIyIGIzNTYwNWEgLSBILjI2NC9NUEVHLTQg + QVZDIGNvZGVjIC0gQ29weWxlZnQgMjAwMy0yMDI1IC0gaHR0cDovL3d3dy52aWRlb2xhbi5vcmcveDI2NC5odG1sIC0gb3B0aW9u + czogY2FiYWM9MCByZWY9MSBkZWJsb2NrPTA6MDowIGFuYWx5c2U9MDowIG1lPWRpYSBzdWJtZT0wIHBzeT0xIHBzeV9yZD0xLjAw + OjAuMDAgbWl4ZWRfcmVmPTAgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0wIDh4OGRjdD0wIGNxbT0wIGRlYWR6b25l + PTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PTAgdGhyZWFkcz0yIGxvb2thaGVhZF90aHJlYWRzPTEgc2xpY2Vk + X3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0w + IGJmcmFtZXM9MyBiX3B5cmFtaWQ9MiBiX2FkYXB0PTAgYl9iaWFzPTAgZGlyZWN0PTEgd2VpZ2h0Yj0wIG9wZW5fZ29wPTAgd2Vp + Z2h0cD0wIGtleWludD0zMCBrZXlpbnRfbWluPTE2IHNjZW5lY3V0PTAgaW50cmFfcmVmcmVzaD0wIHJjPWNyZiBtYnRyZWU9MCBj + cmY9MjMuMCBxY29tcD0wLjYwIHFwbWluPTAgcXBtYXg9NjkgcXBzdGVwPTQgaXBfcmF0aW89MS40MCBwYl9yYXRpbz0xLjMwIGFx + PTAAgAAABSpliIQA6DGAAgAQNZwNmdWWOzHthiOY//wAeC/FtjLYNd/XXAB4AAgAFKAVI57JANlf66AB4P8S2Mto139dBhxAABAE + AFIgAAgngACBkYAQEzyBIUoQjpnmDUpQgAaGrsGK54Wi84oFxIzBiucFIPOIMYAFohHABFx0JBh4AUAP7AAgvJW9I1XpvrwAs7ca + SpSQHwMf8Wa5/cpAYGriAABAAAAwBUsP/+EAARIAAIAYAAgNBAACAeAAIVoAIAKb4IFQ0TKSQD1XW/AAfW9QAoPqOD/XxAFwJE2s + 8AFtuIkKWHhjAB5pMMAKm6tIJMgCgAAguBwABABAA4BcsOAAIAIAHALl+OEHMgGyyxo6fa//hAAIAAgHANAAELsIAAQCQEAB3yAA + DQAgIlgC+jAJBIxYgKYn+/DpnLAFuhgnFRjwgFtz/fhj8AAEA4DkhQxi4XNyHhR2Ew0l4OAB4bIqMCXk4HK79/+AB5O0WfIC4Yb+ + 3wFQRRHsQaYl4r6iodAAAgAkKAVA5/JENFf668IAAQBAAPYAcEAAIJ4AAhSqAAEAjH1Q9xvc3vgwKZcZaHkU/QiL+4IuFA9Y6YcG + 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 + """ + + static let clampedFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAT8bW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAFfkAACw70AAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BCd0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAACw70AAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAsO9AAAAAAABAAAAAAOfbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACw71VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAADSm1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAwpzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFExhdmM2My4xLjEwMiBsaWJ4 + MjY0AAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTAHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAD3gAAA9iIAAAEAc3R0cwAAAAAAAAAeAAAAAQAALuwAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAAB + AAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAMAAAPpAAAAAQAAIzEAAAAEAAAD + 6QAAAAEAAAfSAAAAAQAALuwAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAA + AAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAMAAAPpAAAAAQAAIzEAAAAEAAAD6QAAAAEAAAu7AAAAGHN0c3MAAAAAAAAAAgAAAAEA + AAAfAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAA8AAAAAQAAAQRzdHN6AAAAAAAAAAAAAAA8AAAH0AAAA/UAAADSAAAAdQAAACoAAAJG + AAAAWAAAADEAAAAlAAACywAAAFoAAAAqAAAAKQAAAkMAAAB+AAAAMgAAAB4AAAIwAAAAawAAAC8AAAAtAAACRgAAAH4AAAAxAAAA + OwAAAgcAAABhAAAAMAAAAD8AAAEHAAAI1wAAAboAAABBAAAAKQAAADAAAAHyAAAATQAAACkAAAApAAACIwAAAE8AAAA1AAAAKwAA + AiIAAABqAAAAJwAAADMAAAIxAAAAcgAAADUAAAAjAAACHgAAAG4AAAAkAAAAIAAAAjMAAAB3AAAAKwAAACEAAAEDAAAAFHN0Y28A + AAAAAAAAAQAABSwAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAA + JKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYzLjEuMTAyAAAACGZyZWUAAD34bWRhdAAAAp4GBf//mtxF6b3m2Ui3lizYINkj7u94 + MjY0IC0gY29yZSAxNjUgcjMyMjIgYjM1NjA1YSAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMjUg + LSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0wIHJlZj0xIGRlYmxvY2s9MDowOjAg + YW5hbHlzZT0wOjAgbWU9ZGlhIHN1Ym1lPTAgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MCBtZV9yYW5nZT0xNiBj + aHJvbWFfbWU9MSB0cmVsbGlzPTAgOHg4ZGN0PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9v + ZmZzZXQ9MCB0aHJlYWRzPTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRl + cmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9 + MCBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTAgb3Blbl9nb3A9MCB3ZWlnaHRwPTAga2V5aW50PTMwIGtleWludF9taW49MTYg + c2NlbmVjdXQ9MCBpbnRyYV9yZWZyZXNoPTAgcmM9Y3JmIG1idHJlZT0wIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1h + eD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIHBiX3JhdGlvPTEuMzAgYXE9MACAAAAFKmWIhADoMYACABA1nA2Z1ZY7Me2GI5j/ + /AB4L8W2Mtg139dcAHgACAAUoBUjnskA2V/roAHg/xLYy2jXf10GHEAAEAQAUiAACCeAAIGRgBATPIEhShCOmeYNSlCABoauwYrn + haLzigXEjMGK5wUg84gxgAWiEcAEXHQkGHgBQA/sACC8lb0jVem+vACztxpKlJAfAx/xZrn9ykBgauIAAEAAADAFSw//4QABEgAA + gBgACA0EAAIB4AAhWgAgApvggVDRMpJAPVdb8AB9b1ACg+o4P9fEAXAkTazwAW24iQpYeGMAHmkwwAqbq0gkyAKAACC4HAAEAEAD + gFyw4AAgAgAcAuX44QcyAbLLGjp9r/+EAAgACAcA0AAQuwgABAJAQAHfIAANACAiWAL6MAkEjFiApif78OmcsAW6GCcVGPCAW3P9 + +GPwAAQDgOSFDGLhc3IeFHYTDSXg4AHhsiowJeTgcrv3/4AHk7RZ8gLhhv7fAVBFEexBpiXivqKh0AACACQoBUDn8kQ0V/rrwgAB + AEAA9gBwQAAgngACFKoAAQCMfVD3G9ze+DAplxloeRT9CIv7gi4UD1jphwYEGQjkcI/FmLe4MdgAIAGDvAT8iRXGXjwj3IwIhCAA + dBlEcxJpqXCvKq8MMor2pNNGCHd+r7gGQFF+pnCinufVAXIAAEAYAAQBAAy4H/AIgIK9XOVFRG2/UQkAACAMAAIAgAZcIAAEAsAA + QBwA+4HAAEAYAAQBAAy5+np4WcABAABABAIeAWsRILhIHx/Q6yEfj//IALYAAgAChCSmMkTBoN77lAUEUEFfrjKqohbdoIfXhhFF + c1BpiXC/KKqGvB0OHwtgA4AQMeAhdZZcYbt3G7h69/8DgACBsAAIAwLuYBCDDsSOUAop+Ver3/EyzaDGl47LWyxTyoryipaqXlFS + 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 + qdsbK5c= + """ + /// The same 60 pictures with `open-gop=1`: the second keyframe is a non-IDR I picture whose + /// leading B pictures are decoded after it and displayed before it. + /// + /// ffmpeg -f lavfi -i 'testsrc=s=64x64:r=30000/1001:d=3' -frames:v 60 -c:v libx264 \ + /// -preset ultrafast -pix_fmt yuv420p -bf 3 \ + /// -x264-params 'b-pyramid=normal:b-adapt=0:keyint=30:min-keyint=30:scenecut=0:open-gop=1' \ + /// -video_track_timescale 90000 -movflags +faststart healthy.mp4 + /// (clamped.mp4 as above) + static let openGOPHealthyFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAYEbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAB1MAAA6pwAAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BS90cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAA6pwAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAOqcAAAXdgABAAAAAASnbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACv9RVxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAAEUm1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAABBJzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFExhdmM2My4xLjEwMiBsaWJ4 + MjY0AAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTAHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAD0AQAAAAAAAAAYc3R0cwAAAAAAAAABAAAAPAAAC7sAAAAYc3RzcwAAAAAAAAACAAAAAQAAAB4AAAHw + Y3R0cwAAAAAAAAA8AAAAAQAAF3YAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAAAAEAADqnAAAAAQAAF3YAAAABAAAA + AAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAA + AAEAADqnAAAAAQAAF3YAAAABAAAAAAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAA6pwAAAAEA + ABd2AAAAAQAAAAAAAAABAAALuwAAAAEAACMxAAAAAQAAC7sAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAAAAEAADqn + AAAAAQAAF3YAAAABAAAAAAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAAC7sAAAABAAA6pwAAAAEAABd2AAAA + AQAAAAAAAAABAAALuwAAAAEAADqnAAAAAQAAF3YAAAABAAAAAAAAAAEAAAu7AAAAAQAAOqcAAAABAAAXdgAAAAEAAAAAAAAAAQAA + C7sAAAABAAA6pwAAAAEAABd2AAAAAQAAAAAAAAABAAALuwAAAAEAABd2AAAAHHN0c2MAAAAAAAAAAQAAAAEAAAA8AAAAAQAAAQRz + dHN6AAAAAAAAAAAAAAA8AAAH0AAAA/UAAADSAAAAdQAAACoAAAJGAAAAWAAAADEAAAAlAAACywAAAFoAAAAqAAAAKQAAAkMAAAB+ + AAAAMgAAAB4AAAIwAAAAawAAAC8AAAAtAAACRgAAAH4AAAAxAAAAOwAAAgcAAABhAAAAMAAAAD8AAAjjAAAAFwAAAb4AAABBAAAA + KQAAADAAAAHyAAAATQAAACkAAAApAAACIwAAAE8AAAA1AAAAKwAAAiIAAABqAAAAJwAAADMAAAIxAAAAcgAAADUAAAAjAAACHgAA + AG4AAAAkAAAAIAAAAjMAAAB3AAAAKwAAACEAAAEDAAAAFHN0Y28AAAAAAAAAAQAABjQAAABhdWR0YQAAAFltZXRhAAAAAAAAACFo + ZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYzLjEuMTAyAAAA + CGZyZWUAAD0YbWRhdAAAAp4GBf//mtxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxNjUgcjMyMjIgYjM1NjA1YSAtIEguMjY0 + L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMjUgLSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwg + LSBvcHRpb25zOiBjYWJhYz0wIHJlZj0xIGRlYmxvY2s9MDowOjAgYW5hbHlzZT0wOjAgbWU9ZGlhIHN1Ym1lPTAgcHN5PTEgcHN5 + X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MCBtZV9yYW5nZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTAgOHg4ZGN0PTAgY3FtPTAg + ZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9MCB0aHJlYWRzPTIgbG9va2FoZWFkX3RocmVhZHM9 + MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVk + X2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MCBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTAgb3Blbl9n + b3A9MSB3ZWlnaHRwPTAga2V5aW50PTMwIGtleWludF9taW49MTYgc2NlbmVjdXQ9MCBpbnRyYV9yZWZyZXNoPTAgcmM9Y3JmIG1i + dHJlZT0wIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIHBiX3JhdGlv + PTEuMzAgYXE9MACAAAAFKmWIhADoMYACABA1nA2Z1ZY7Me2GI5j//AB4L8W2Mtg139dcAHgACAAUoBUjnskA2V/roAHg/xLYy2jX + f10GHEAAEAQAUiAACCeAAIGRgBATPIEhShCOmeYNSlCABoauwYrnhaLzigXEjMGK5wUg84gxgAWiEcAEXHQkGHgBQA/sACC8lb0j + Vem+vACztxpKlJAfAx/xZrn9ykBgauIAAEAAADAFSw//4QABEgAAgBgACA0EAAIB4AAhWgAgApvggVDRMpJAPVdb8AB9b1ACg+o4 + P9fEAXAkTazwAW24iQpYeGMAHmkwwAqbq0gkyAKAACC4HAAEAEADgFyw4AAgAgAcAuX44QcyAbLLGjp9r/+EAAgACAcA0AAQuwgA + BAJAQAHfIAANACAiWAL6MAkEjFiApif78OmcsAW6GCcVGPCAW3P9+GPwAAQDgOSFDGLhc3IeFHYTDSXg4AHhsiowJeTgcrv3/4AH + k7RZ8gLhhv7fAVBFEexBpiXivqKh0AACACQoBUDn8kQ0V/rrwgABAEAA9gBwQAAgngACFKoAAQCMfVD3G9ze+DAplxloeRT9CIv7 + gi4UD1jphwYEGQjkcI/FmLe4MdgAIAGDvAT8iRXGXjwj3IwIhCAAdBlEcxJpqXCvKq8MMor2pNNGCHd+r7gGQFF+pnCinufVAXIA + AEAYAAQBAAy4H/AIgIK9XOVFRG2/UQkAACAMAAIAgAZcIAAEAsAAQBwA+4HAAEAYAAQBAAy5+np4WcABAABABAIeAWsRILhIHx/Q + 6yEfj//IALYAAgAChCSmMkTBoN77lAUEUEFfrjKqohbdoIfXhhFFc1BpiXC/KKqGvB0OHwtgA4AQMeAhdZZcYbt3G7h69/8DgACB + sAAIAwLuYBCDDsSOUAop+Ver3/EyzaDGl47LWyxTyoryipaqXlFS1UsU8yKOVuWHU5eqlrZYWcABAAgc8BCyy64y7dONnLt1//A4 + AAgFgACAMAH3IBYAJQAmIHEU3wOvXM3Wy/x2WGPwGgjdSCsEzK8jYABAF5xFgALAkABYPAAWDwAFgSADAUM8AGBYYGgAeoECJzwq + AAY6gDAVOef5UAAp+AQGTngGgtxI5zbdgc337/CIAAgVgACAsAAIE4YAAgbAACCSAAINggAAKVgvFLNlAZCnk8Ax3oGlQp+V/DbA + BVhveABbgoB2EZE8AkzKiFo/EhHzpB/BfAAs2ocAC0ueUD4irJhZsKGSAobICty24kAyDERsICQksAhIvkJReV2Axem+sQWnBwAC + gDB0sOAAWAICJfDGxTFcZ/KNU9wYQAAgEAACDOcBYQAB8AAQI6AAEAkX4gPMH2QAB1mNwjvLQwFsL97o5wMqXwCk+LyEBMxipMZw + h3a0BAGlGe8YAwV9zDBIAFkG9L0DiTlfABsFyihAAFgQABYOAAsHAAWBAAYCgzgAwLBgYAOQKfU8KAAFcgDgmU8/ygABHYChcp4V + AAbQKExnhATjSxQCtyBsQ5BAAEAABCWAA90IAAQKyAACAqAwetAAHWC3NB5jsAApbQEBWw6AXJbMVW4MAqLoPMBAB6wlGyAAEA+a + HQH3j6aHXDFryq61gQCnz4XwAHAwrkEWAuSpMFHRyZgbf4UGeAoN8BfkvUCTZqvE28KGYXf8K6QCcWWHBOLLDgAhT5YcABBRMsOC + cWWHBOLLCgQAwGBAAEQABAYAB5qasCzDwACY1SkIiXlbiv+VVVYhioAByeZXJowZVFPpuKCGAsmf8QAQhUuAAAAD8UGaJAD68bFx + WKxXwCd9vd/grFYrAB826a8T9dABnf9ufroMOmMAJ6t7vf/8wl/3v9//zAD11/3qHNJXevCsAettwEWCFpPa9M5lKRgX1gYC79yd + Ulf/wNByIb0dLU96+6nwve5794f4Sql3eJxVKRv4P/CtJLJgfGABOBjyDCAD13LLfHG+NmQC1a+Umv+f/D8vFwA51zsPtWH/gApq + 7lqP/3Z8Pp6fgf7Hj8F6atu5+uj9EU3GuuP+HQYltcfqFqnvO/L4C77ej/Yf4JoAcXNpMP+D4cW98gP9qgyavJP6iKky5m36N/Ii + pMjDIPUGmDdnfO/wvHt6YA9kn4GogDt6YFvpyxp/UDt6YFqcnRh/wfAO3ph+iVc+Z/Z8z/haooRUmVFA9SZJNA9SZAfbdAYH1BEV + JkHeVYH/EnTONRJr7Hj3iBdAYNfA4EVUsGBsKS+fDQDB3XzDkbS494gCRCExwYADYEffOHABGbLwYeG184GA2ELZ54Xg7emFsHb0 + wtg7emHDwdvTAFqU+AjOQDROTPonWrXmZ+k2bbuIjbwABhQiAAOJhwABhQiAAOJgdAJkAccGgIQCZAHHBoAGIKiKh90AFginDPIE + sFhnvpAUOwGYAAICQAAgJgAcCaZIpClmyd7wBAAEEAAQPYotYCB63Mo5pBjTPZgAAgIAACAeABQIpgQABtRnhAACA8OAlMn6iZND + T6AIvBhM3T1GgvCFMFneOcLswlG5DmhxUhkuce0AuRmJYKgMGbZLCzgAAQEwAIEglxrFOEta21QO9/ExsPgABANQFQQ6ZD4AAQDU + BUEOmSYAoBgkCTCYAoFBIEmKTgyvM5OrgTLcACJWkpfujvaXPyakJiqBSb05Jr/7hhAAIAI0BQQABMByx4kSqAwSjqamO1zgu+lO + nivhje/rnZaAA7nYI61WbuH+92d5/WaP/sRGyYAAumCYAAumHQABdMEwABdMA5wGVMD4JAPOAyphGCQBld5nt++gAoMhg7miGS4x + 16hK22n8huQhHFeAsGsqLxutXF59mxm73+EUwQlzxDJMY+x1KSDwY2YktzP1CFpazgEDc04nzeEB23Q6a//goAAgGMBwgABAJYAg + oi/4SJPEhSAVhIUorkOKGr1UKcANkgzPHlbeW9LELWOMDgyhsp+FpzgFwW6CLOnrs0H7AABALADAUQXSGR7cfsX333+Nh3AAEAxA + UIAkyHcAAQDEBQgCTL4AD1SZph4ADxpM0y3Ty+AhuP/fM0ZTcE2ZMsyHGQ7K/IXHupn0i+kQgAIAOQAWEABAxgU3EfgQO8ytoamY + eGONDAz67Qx49gAKbw9/qdXC1mjItxn+6IAAAADOQZ5CQDqviLkYsYkJ42JePUPeuoDh6smNUDw9WTGqGkEmRBC2W2pvV1/7jCey + 2MYrEMjJEaGGPYMlCdFPwY2mNoI1aB6gFgcmPUCwOTFBKugEJsX7l8a3DL3TGBd5wEdGaGGXecUg5jUVjFHF6m3ODD3GaamgOJIa + Y9QPEkNMeoEzNj303J+cMBKh3UB3qqZl9thL7Yo4BY22OIK1iBqgFQ9MaoFQ9MAkfrD//3Nxxs3s3kOqml1Is/s/hSREX53fp33a + qHxV2Td8iSXo9OAAAABxAZ5hQGVfGE8MThgIrLYrNMYby2WxjSanoBGogUZBvBBdt24gwuQYBNsX7rLRRfwWo6Y0h6W0IcI8WVRz + 0w9Tp0xL0z3jYxHzzhgIxQLbR2tNuQoyGXtW2cbditidN014uBTLUVVU1H/L/FpXVUcwx/AAAAAmAZ5jQGUNghis7ArewcHEbduN + UqqYJ73npx24i6G6GMphbRSmFtIAAAJCQZpoNEB9Ce5GLGJIiNigAFoWAALIBQAC0mAALIBSaYTgAoFk0x2ACgAPyEljACwRxyHu + EsFBlDwgKmfmAACAkAAICYAHB9MTDhSgBAAEEAAYPYsxAHD9uaQxSC+z6yAAICAAAgHgAYH5vAgADvIUIAAQIBwEsmHQDp2NMAMv + TBhq2xZaFYQ5ge/wR53jAD3lfYA4yzie9c8BcjMToKgMG7ZpC7gAAQEQAIFgpxnFOE9a62Qu+PxsHSMAAQA0w4ACoHpGAAIAaYcA + BUCQAKgDQEAY1MPAAqBoCAMamKhhncuMoSpb8Fa1cA3TCtN3It9AD4QgACAsUAAQBAgACY55pSADcAxyWRP8TVWGgaxh8jU4AB14 + HBELuRkds2m8bW7P/9fiI2WAAXigAF5YABeKAAXgukmHAHi6SYcAecYxqAowAkcwxbiRTJUYQcoStlReQ1IQjjtASDWVV43WhpEA + DiOYQshIhcqMfX5lbzwxMpZbHbqkL96uAQNxJhfm8IDd+h41//BEAAQBCAcIAAQCSACCKwHrJYArCQriqUQUNXukW4A2bBFLGl6e + W9rRoNeUCIuZG4v1AjAXAu4Oo6eurgdwAAEAkAYDCTKQyP4Fx8xfd//xsB5kAAIASYSAA0B5kAAIASY4ABoPANQBgEAcxMHgGoGA + QBzEx5PBEIhTl48sNUdQFhO7gEUaD0OjFa+6uoIgACAqQAAQBQgACQxxQKvABTSCpjzGLobyeEAlLLODbAAXcGhFkjNRVJCTXeU9 + +kAAAABUQZ6GRREodQ2CqK4rk5eTt4xG/Ftxsdo9hbR7EBybSZBybSZQ204kaz/I/iuNUVi9yMeP4rxWDi+VSOL7VI+Mn8fGqQaq + kG4DiUaY4OJRpjuDZ6PpAAAALQGepUBtDY/e48mNcmPy/CONpmrTMtO2nhqB80xYfNMWNQVHEx/xG93HUw61MQAAACEBnqdAbQ2I + veMU1FPcdpraeSdidjgqysSsH9/vPQ9BSMAAAALHQZqsNEAhXl8YHeuesOJ0f/DGNL+NTmv8OJLvoX3rRqZ75sJcX7v56VfgkGr4 + RaI94GOYULlwDANgqr5wMPBtZ5hwAIKVLDkMkuPeBwBhgjnZAABEGLlg4AMCPueB/GXsAwBsEbM88OeHFyTC6XOxRVb/4iNigAGo + DkMwRMFAANQchmCJh4ADxAADzwAHnAAPAPhEatgBYEccp5Qtg0FffER0Q+YAAIDAAAgQgAGA6mPl2bN/MAoAAgmAAgNcw5ADwv+s + UKI/3//vRAACAwAAID4ABgNgDgQAAgD8zhAACBUHALdGjKsARBRpgYNm6NNCsEa4+fUKSI+YRnybuoHOlw8n9/9gLiVTFQVQMC9s + kx1wAAIC4ADB4NdaxTh3YxvEJB04mNgPIABgqYTgAUwDyAAYKmOwAKYOAAJKAHAIQcGUw4AAkoBwCEHBlMpbHIVVdU5SSlgSweRF + LAMCg00xA5uFsMDgACBBwABASCAALqAXxaDlDpkWYLE7D7yNw8QDDEudEQHAc8IA0AuJHb/M8Hg4zo5j7YiNlgAG4DiEcKmFgAG4 + OIRwqYcAB4XAApg4ADycACmDEbrLACRzCFuJEMnQp96jPSD8huQRHkvALA1pZiE7Uy2ST/MAJDsYeQsU2ZCSDPc3KPDCdSyyiP1S + H+SrgEBOLFK3bw4Dd/Aq///ggIARwaEAAIB5AAwVL/ebY2ApictL3MMMCV7pHOADI0DMaFm6IJexoxM6iuOmTZGfhy84AuAuoDLE + xavmonwAAEA8AGAUabSCIYwMRK1Ie//42DpgAIETDgACbA9MABAiYcAATYEgAE1AOAIUeGEw8AAmoBwBCjwwmZ+hWmzMm+NrcsHj + UbywBcWwnW8oQzyH0G6BQABAhwAAgKhAAGGAbgZBXBs5ZB1EQDpilzrEHDPBnHAjSd2xgBANGViqSKT4Zetau/1AAAAAVkGeykUV + KHUNgpn4DpDRMfwdIaJimnhp4DM3yxP+f8apw7CwpjJhTGVQTlUM785wRmr0B/iZeBiQaY3jEg0yAwN3937v4KpFB2CKHsQdPwdP + 2R6PoKRgAAAAJgGe6UBtDbl7t94KZWJWMeTNcmSsdGqyXKodqjHxCvOV8/n756HpAAAAJQGe60BtDbn41R/VUxE6idRjFKimI47T + W0ysZWMdnYnYiuK56cAAAAI/QZrwNEAhQnnrgf0v9D3gcAGCBFmsBwACMBjacOACEfLwBzxrnhQAbBnzPPPX/saGZrxsUAAQQIDg + JQABAACkwUAAQQIOAlAAEAAKTDgABAR4kAAQD2DgABAR54AAgHsAHAAgQZBagBuiQS5t3KZ14JmAnHXLBohnOGW8IAA6oB9CgACB + WgAAgOg4CEg1ZQgivazPEd+oX5gY0sYIR4jLKAKDwYo42/GwHBFJhMOAAJMA4IpMJhwABJgBwikQmFgACCDBwikQmFgACCDG6/bQ + 7tHyEktxYZBk5pf+/04AAwHHrzjfgYAAQIkAAEBYIAAvgFwBVNmywi8FuhnA20hkVPejQiRPwCiY6/AaG6gCh5vfen7xrMyEveYi + NigAG4HIBugFAANw5AN0B4AHgsgSYeAB4sgSYPRussAJHEIPY84WZCnm/56RfkN2MYo7wCwMaWYhS2GpKWAFAxHGuWKbMhJBnybk + 3hhOpppBH6pTUks4BATjRSuWbOA3b0KkH/8EBACMCwgABARMAGHIk9Hl2VrAVgmOcVjiDg1nKh7gAyNgRjQs3TyyWtHGuGcpiLMV + Vxc4EYAuAu8GWIi1PdRPYAAIB4AMAow6oPhjgJGhD99/jYOEQiUw8AAmwPCIRKYeAATYAcEQmUwsAAQQIOCITKYWAAIIGcUqSf+7 + R6N3lg8IqtrRs//f6PAAgw5aW/CQABAn4AAgLhAAFlATQZKhZjgHxiOQ5zf0yoN5Xs8VkJPBYM8AETLjKU4dspGdPIAAAAB6QZ8O + RRUofQmorpI22A/5MbB/yZG3G2cOBE3V19+Px8EB47Q1sn1kBX4ehdQQROoIIBxIcTMHEhxMzbEOYAH8duQgvCjgo4zcYPx0vA4o + 0wXhxRphrCbWI9Mw78bIpYmKaIByYnTC2DkxOmFtBHpgQ77J3/XxqwuucOAAAAAuAZ8tQG0NgpwHvSZB70mPYOw9h7GKdhGq3P7/ + xNwOo0w4dRpi+ngmysSsT0xqmAAAABoBny9AdQ2EorOwK3sc7GTFcV7n9/7plYTaxAAAAixBmzQ0QCFCeelX41LJcuPeJaChcuAO + BsK1nhzxlfPhwAIKVLBh4bXzrnlThnU2mv8bFAAEECA4AECDmUwUAAQQIOABAg5lMLAAEBOA4hHACphYAAgJwcQjgBUwBwAIMOWl + gISSYmyE7t3gmYCKS2WCo3MGLadfhAAF0AegMAAIEsAAEBsBDFoAcN68m8dO3tz8UpBVi0pxLR5g8GFPc0QTnpQeNnAAEmBAABBQ + cAASYOAAIKAHAVAAgSmFgACCDBwFQAIEphYAAgg0pJU41lq4i8GcDxJl2pZvfCwAGA41etvwgIAAgDKACggADYwWllnUJYVUVCYQ + TswBoIkNeeFQQ4u7w3e4COJP2JOv2IjYoABuKABigAG4oAGJADwMRETDwA8YiImA6Y1lgAdAYhBpDRjpkKJM9zcqi8huxjOFJALA + 1pZiFLYcFSwAoDEceUsY6ZCSTPk1JvDCdTTSCP9iG+SzgCAnFilcs2eBu/oVf//wRAAEAYwAUIAAQESAAgQDtheStIBWCQpx2MKM + DV8qHOADI0BGNCztvKe1oFrs8oA4xOMj3r9c0AXA+4GWJjFPdRPYAAIB4AMAo06oEQxxEzYh++/xs8AAmwJAAIqDwACbB4ABFQA4 + FAAKEJhYAAggQcCgAFCEwsAAQQJizcsCyc7gPCgOFh+Ibk6HgAQYctLfhAgACAKsALCAANChSZ9hg0HiIBOeWLBtKXN8gI4g0Cq9 + qxnYYFURjxfQAAAAZ0GfUkUVKH0NjMB8KJkHwomP4UUD+SKCEGVO9+P1/vhXBHYgnsBumRo6ZG/0NDHxdeNisDUapgrGo1TG8SqW + 89UtCd/Nn/LrAPHjI1nMOmHG7GbJYXiRUH4kVD+P+/+QyUxbrpmIOSkAAAArAZ9xQHUNhGfuf3zsNNfJGkxqkx4mKzqBW9QelF4+ + VhysWOphvWphvXiCfgAAACkBn3NAdQ2JwZSZlKTNBDbL2MkvFZeW8Tgam0yNTaYeid2nwRSqZVMapgAAAkJBm3g0QCFCeeUSjow8 + 3PA/paqXHvEtBQuXAw8NrPB/aueHAAIA5UsDPGXPI94HAGGCJmWHAAIgxssHAGjNp+D7nhwAGwMTfP567tKa7CM2HjYDgBBRQeTC + wABATjwAgooPJhYAAgJxAAAgIqAYAAdHQHAACAioKAAHR0ABKaAkedgwiiSgMgr208HGPBOOuWC3HC9X48/CAALhBagSAAIEqgAC + AyA5OAAEAdcRBEkvMEpn7sBCV3kKt+QBobABGiDUGyINOeBMGRN3MMtzxs4AAgoEgAElBwABBQeAASUAOAQQ4AJTDgABBF4OAQQ4 + AJTDgABBF4+iUsDyIpaLAWqQGmkYOJomMAAQKPZlg8IAgACAI4AKCAAEAUEBrRC+Ss4WqQBMaWPDGJT682R19Yn3dbF/ETa/GwHA + BBBYfTCwABASg4AIILD6YWAAICUUAAQEoNAAODIBQABASlQADgyABN5MG6XFBRN00YTDKesCWDgTDIlhERNmziAXQQQABQcUmEQA + BAkYAAQFhauAAEAZOHhPBlkSSL/0NozOWIPjzoRZkzMdDzGsgGEDLIzQ72IjZYxA8sZw8QAAe4DgCMeBCYcAAe4OAIx4EJgAgACC + KAAYBW3toB8vy+ReJEwj8MRpJxKhrhoKo9ODYnDMAAEBgAAQIQADAdTEOIjWMA3OEuuUQjWXhg4ABkAqCAAJOENIjUtEIOEzAJkC + Mw2OjxFWRF/+O1nrG9qmbtHArYGzuAzgHD2T7J+qwX4pfBlOhu3d5AAAAHpBn5ZFFSh9DY2A6Q0TMekNEzPysH7WIvtZ16AP08rH + 0dBEasKGg/w9OwxPYogMkkzGSSZkDQ02/41UcTBAwdmG0QjAXjYDzEqYWweYlTC2XnXb3rkR+539A2xMAB1ORJ0A/8PUI7EE9gDR + Zpj+NFmmP6/Sl3+5GE1kxAAAAC0Bn7VAdQ2Fo1TVU3AAFFnY41RDqTVf+SVgvKw34MIrluWlAVfd2fBDFcVxrYAAAAA3AZ+3QHUN + jLl7t8bQVaCWE7F9PH8rGVgKUy/klMv6kZP4/P5+Xg7KW8dlJaQF4iVg7DWHsS+XwAAAAgNBm7w0QCFDeNgOCgAAgBOTDgABAP48 + FAABACcmHAACAfwagACAqgGgnZMKoAAgKoFQTsmA8DBokqsdQPEgJx4aA8B8Y8E465YnW00LX+EAATAwkgEACAAIALQBYFAEOfd4 + D1AA1wxvttz3QPU5iahwB9KoBsqAAEAEPIiGeFAOrmN6HnjZ4ABBQBwAIcQmTwACCgOABDiEyWAAIIMSAAIIvLAAEEGeAAIIvF8i + VjBCJojjEh/+/2Lx4ACBR7MsDgAg49mW8IAgACAEwAYEAAIAYCAskaisw2bWEECNLH4TWMFkoajSMRO6zDtc6G0hlLfjYDhUAAQA + 3ph4AAgHcHCoAAgBvTDwABAO4gAAQDuBgTOmHAACAdxgTOmB4Dxy719vwWGGEVU3FjyBDMlgkql9qs4ADuGIQABIDnBYQBAAEABg + AwSogxw4Pw4vgAWeGDKSru8cmuG8Dwat1ANF+AAIAGcPDTGuYfJaE2DmIjZYwOIRUyWMOIRUyWAAagOAjGgxMLAANQcBGNBiYAIA + AghgAGAVtzaQIl+XybxImEPhjNJOLcPeMBNPx8aEoZgAAgMAACA+AAcD6YZIJJYBmWJUucg2oeGGAAZgCwgACjhD1pUA5HDLAJkC + E52OjzvEib/86b5sPeAxT1I/7ZAIsEHnAYoWZZPrn6rBfikgDR6FQ1ifkAAAAF1Bn9pFFSh9DYUgdOTOHTkzn4Gu0w/Gu0xz+m0R + 3xXLjVOI0QdplElphblvjYGdpkYztMjYgxTGJBTFry/QX6cAK+PFk17y3h29gB+mmPYB+mmZfl/dNfHQaDQeUVgAAAAsAZ/5QHUN + hHP5+lnYPT+qNViIUaPYJGj2D+/8O47StoWz8tv+52H+JVDKg4AAAAA7AZ/7QHUNjIHFyYw4uTHP5+BPvoOx2fhnYPT+CGVi1iMQ + cTAcraY9gHK2mPYEH8f23/x07E7GK4r52IAAAAAFBgYBxIAAAAjWQYj/gEaDGAAgAAgxAACCMeAAIBQAB3cBaQiENtcEohACiAAI + moAAi6/wAeAAIEgKaAaqECDUkDks/rrgA8AAQjAAFA0AOtQQGDqbAeQef10ADwABAlBTQDFQgQakocln9dBhxAABCkAAEIkiAACR + OAAJBRgAX4CEU0JI9cHPmbFQJsBiraEkeujv7NioAFiYAFCypYCR8oNwcr5aKygSNgAQKKFgJHyg2BwvForIMYACAACAFADAqA6A + w+yNUjqOA8AASAABIfAAdhEOOtBBCHnvII4CCJ15xJKUOW8ABCHCCIYZSCCEOOcQQHwAAICoAAgKglgJitQUdGhC6CQDooEAACEI + AAIaAAAhpSw8P+EAAIHYAAgAAAMAhrgACQYEAAIa4AAo/gACK0AAI8HBjqgUGLRNMkzea+DO288YVugACAmi31cmAAqAruhh8DCs + oIBq8IJ0uj/eQEUl3gAAtNMgBAyQ4PwxgA4AQOcceBIADbHfbdiB2LYAEwAAX7g4AAhCgACGiAAIacsOAAIQoAAhogACGnL8cAAQ + BAABA1MgEIMwUkVkq4Va3a//hAACBiAAIqQAAhmgAC96EAAIXIAAg0AACLwC5AAAgzAACEUAAIK0sAPgAzEAOACmg1R49CwFhbf9 + +QAAVAcJlgB8AAxFA0BBEfVJC8GAHZ1f9+GPwAAQ2AABBTABIANAC5ZDcPAZQWyILhPAD8AAIfoAC8HAA8AAQFwAwom0aA5qitRA + RNu/f/gAeAAIBAAcFcJ8VUJAIohY2/t8BQABAAADAEA0KG5I0FbV1VyUrtTWHQAAIRoACAaAHWgIDD1NoPAPP668IAAQpAABDXNA + AEUgIAASJwABRlAIAAEarQDHVnMC0JpggpLCVwYCsE5wxLBrGVCqjTYfAAAgFAAwXFNMqBpzU8AZvQyiYHwBAFFFBtonNC3x6LEN + RUgx2AAgAAgxgACCOAHgACCCbgtNpDDiC8C8C6QwXQABAsAAECwABwABAAADAFA0KGxI1FbX1ViUrNbXwAAIAIACgaFTclaitrLw + QiWrtYIHAAAgSgAIAAsA2bIGrQVNSsiK7WAJ5AAAhaAACI8AAImlwP+AABAkAAQABQBo2QtWoqawioqu1gMZAAAhaAACI8AAImlw + gAAQtwABEhAAETe4HAAELQAARHgABE0ufp6eFnAAQAAQjQABBwAAEAsAAQbQiNpsIYLgAAgWAACBaC6QgXAABAuAAEC/9oAAsAAQ + AQABBkANADgmaunU2igKwPO+40AFAAECQABQAFAGjdC0aitrCIiqzUAx+aHAAAgAgAIBoVNiVoK2rqrEtWams0A0kPjb53wtgA4A + AgxAACCMAAIBYAAggBGQiEQw4BxDiHAhhwDiHH/wOAAIxwAAiQAACC/cwCAABAAAAwFAA8KNQQNQBMFEwCt6vf4gXRRwOE5pYMDY + c18+BI9Ty43MU8qCISlwOeMvYYir54cAAgBTJYM81fOKeBwAYIERq2HAAIgQ+WBwBlGbGzRMCAwABoIXfMLOAAgAAgxAACCOAAIB + YAAggBEQiGQw4hxDgHAhBxDiHD/+BwABC3AAESAAARN7kAsAAQJQAFAAEFggKAgNrOYQCy9cwTLAPkZu+cDhlKSbPANbI75ww/gA + AgEgACAACAHHvQDa3MzEoAbB3AAEVorgAyiEAAQCmAcgACAUwOQABAKYHIAAgFMAyAAIA1wmAAbhSAAIA1wmAAbgBoQABCWJBAAB + CuAAEHqj7QaEAAQfiQQAAQhgABBIo+0H4eQABAmJDAACBoAAICWA8A0AAQJAQ0A6Mhx/qQejIu9+/wgABCjAAETUAAQPgABI/CAA + EagAAR9wABI5AAEUoDAArMCA+aEAGOE5wAG2whAAEB1XAAEBsAF1ngChBeX6h+WukAnvo7EMAFFwAAgmTcDaIfEFU7oUQIAAE4aI + MeT2u4AAEAcAAw14sOktJKoOw+NYfTAFsAHADAUQC3i+JcGIqGPXDAJnYABgLAAYOAAXACQLAAY4ABcAJAHAIAAgKMA4BAAEBRgc + AgACAowOAQABAUYKgACAp1AAEBgABU54C8yAAEBYjqIAHAaoT4EMABBBm84QKOhFJ7twEETrziSUocsGArgEkBAACAcVAgBoOKW7 + QSQEAhUCDwxlu1wADACggVImqkwMVBEK0JRUAwgABBmAAEV0HAAOBAACHiAAIooAGgACHGFxQf6hQAAgHgY5A8gAAgR3VoYC8FBO + qGGm00B2LhBdwIZ7D1SC2OpoCcKNAAA4UNgKzYmQB5gABA9urgYB2QDgq90grvCYF8vE/D2gGBV3B48vnfgwSAAgAAgAiBAP2hOx + kbWitoA3sQAAghlgSgcQABALYBxAAEAtgcQABALYHEAAQC2AagACAPcJQANwqgACAPcJQANwNxAAEH8tFAAEIcAAQSaftBuIAAgy + looAAgvgACAzT9r/DxAAEA8tOAAIBoAJyHhQAAQC9gABALAD5TwgAAQBAABABAFSweASKAKLyg4D3TdIIAARGgABNSAAEKUHwgAB + FrAAEXAAARLwABMKFgAHaoA/4DoABHB7s0DAxyAAIMCFYAAQfgABBSEw60ADAQ6yXqF1YAYAMGklv5AAAQAcwAAha1IQAAZAUTCr + 4A/SPgAId5qAACUdS4AAMAwjK5dR0n0gA0ACR5GEM4AOAAIIIABwAIE3ReFqcK19eSBMADSAwgLoeIAEoMAALMdYHiABKKAAFmOs + DgsAAtCAWAAWhALAALQOCwAC0AUAAQCQAwK6T3j6ciQCzAxK7JGAAIF5WyHAAEEMAAQHUEIiAShEQCUm0DAAECcsSAEwIYzd4G6B + gAcsSC4jjN3hEQCUIiASgQAAgcAACJ6AAIUgADYQAEAARDgABDraEMBIXgACCZJ4qARLggAHTXAQYpRwOmSzBU2VARc1gAgA7IYA + eG6N2gjhMZA6kL++URhLWAAIAF1cAAYAAgAgSyFRPq43oKAfjABAFasABAI6CAKErN2oAAAAEwGeHUB1J8bEZ/PzL3L8dvd73T4A + AAG6QZoCOSippQ+hvGxQABALiAABAJ4oAAgFzgABAJ4HIAGFocABUByABhaHAAVADiGKuWBxDOWW/BzzofVePlJKW/CABAISDAgA + CYAKaAmBqMLXgjoCc/iJS7CGQHQcBVwOIZZZV/3P38bEgAVADgQgQKph4AFQDgQgQKphYAAgYwHEI4AAgGphYAAgYwcQjgACAamD + KUsAtSIZEmIcdC0EGYAIUWnLAzIO6UKbJJ/fhBABkAMCAAJgMJLJwNQquCzCaYESKWVAICxlCahu3/ogiaX42WAAIBUSAAIBHLAA + EAqeAAIBHA4gAQSh4ACoDiABBKHgAKgeEgiJYHIQiJbwdovJESHyqblvwgAgHIB4QABAAEMARRgoEJXhOyyg1GHPe2B4IEuBwkJL + AaaSg5iI2KwHTGETC2DpjCJhYABaBswAEmFgAFo2YACTAAcAAQOQAEAzLGfBcnu6QcIEBn4ZDjkPcLWKDaPT4obggZgAAgJAACAm + ABwfTDJCSWDn8RKnV/g4ABEFQQASkBWpQKMWYBMgjMJjI8rzQW//FbLAZTNcm+6EEMS5wGcBwtUyqXqkO7UtgifTTf/ngAAAAD1B + niBAOobES+3wsph7BMph7GPxxBWIG8DEhpjeMSGmX54jl+XmYuY4ydgKCmDsEgpisDZkmVjZkmPfRc/gAAAAJQGeX0BtDajVO8B3 + pMg70mYi95VJVBVLVGOwH3pkH3pje7fbvzgAAAAsAZ5BQG0NklYPysP+CrJ5PBXpkV6ZfmL6o27u7gOpyY4OpyY780Ynd+xq5/gA + AAHuQZpGNEB9Ch70KTPBD6+fwHADQpFzSOKPeBwYwU2WBwAQxMsDQZSFk8xHvnhAAIQmX56+KW/P+NiAABAJ4gAAQCVBwAAgE84A + AQCVANAQqZJwApgqAhUyTgBTAOCU5ZYHBKKuW/NxZm8bUslY3hAAEwBjQICAAKgBRoi2AVE9hzYD0pZU/hEnvIZAwRljwOIZZcdf + mWPGwcjExMPAAEDNQPIxMTDwABAzUCgACBjAcggAOEphYAAgYwcggAOEpgyOqo2HgCCi1Zb8HABBRKcsBQj4tUSlbff78IAgDMAo + IABACFgU7ANAQCLMlJhZoRNnFO4lfYgXAACAHVi56hqEyJ94QRNL8bPAAEAjiQABAIUHgACARzwABAIUAwCETJwACoKAQiZOAAVB + gSGJLA5CERLegYig2qpuW8IAAoAOcBIQABYAIOEti6JJCqjK5YkGQpZoLcyDhSkWBwkJLGDUqumxEbAdEWJhYABaDoixMLAALRQA + C0Dgg0UFAALQ4INFABAAEDsABAKy5tAMkN3n3SL4v8YI4pRJAlgoM/8ICxqGYAAICQAAgJgAcH0wyQkliY+Rfdv+EIOjhAARUC8p + WBxI7gEyCMglMjSvLBr/8NmQJB6hfXGwRYdXOAzgOGqmVS9Uh3ilkEV9u9NpvoAAAABJQZ5kRREodQ2Iz+fvvjIHuph7qbgOCkdM + cHBSOmKm6+egSt+B/3l/CvT052J2OxrT/hSB0nSw6TpcBy9JkHL0mGX2l3qBJcapgAAAACUBnoNAbQ2SIVEQqOKx1MVqYtY8Rb7f + Hkxrkx467u7jaZdWmXd8AAAAJQGehUBtDYJbnYd7B2L2KNVjo1QtqqFsVxXw+Mt8mdidjE33e+AAAAIfQZqKNEAhQnnmRVwbcvA9 + qXAo95UESM8g+vnjwDAANgci+dEwIj3hIeIERq2BwAEELlgaDKQsnmBjyfPCAAEELl+elX/Q7mql+NiAABARUAOJAwA6YcAAICKg + HEgYAdMPAAIKBAABJg8AAgoOAAJMA4Jx2ywTfWDFlKnf6WVUyhJS3hAAFwAGHhAIAA+AFDxcOqzcywVLDQNMaCMK08xYDcQCcWWn + Gx+NgOhgAw1MLAAEEGDoYAMNTCwABBBigACCDAcABBw/yYKAAIIMHAAQcP8mArlgXom+z74eAAgUe3LceDgAgUezLAD7ZnBv1PsX + NPhhAAFABjgICABAAYkA43i5LsTgNAJmCzBEmwCukXZSSGwIWRe6TFntBVaPMifeEBMaX42eAAICCgByYIAMmHgACAgoByYIAMmH + AAEVAkAAmwcAARUHgAE2DCBDKlgihrgQ0K838gTYETD0xrLeEAAWAAUYEwgADwAYMFh/kso6DasM2r5kgMhzmXNapZmhyAiMlsM4 + iNgcgGZAWAAahyAZkBYABqKAAagHD4MacFAANQQPgxpwAQABBDAAIAzL3VAVL8vvkSpEf+GI15hJA1wwFUfyQ4fhrQAAgMAACBCA + AYDrDwyQTSwNJrkZo2+/gMIEX2hAAR0BZV7gdCC3AJoCE5nMjzkiRN/+jLXsvrgnp6Cma34eBHghYAoM4Bw9s+2dqsF+q3gcT1Xz + Hdb4AAAAS0GeqEUVKHUNgmn4MtA/lLQW+mi9YyA9uTMHtyZisHT4rHT5kwPn3x/ydqX46mNUjVUexex2X4yA6pplg6pplwcrwcrr + xAr8INAn4AAAADEBnsdAbQ25+K38t42NJjVJjFYVaCslaMDBfH/f6rCVl+CLKxKwMbgpu9vMzMy741TAAAAAJwGeyUBtDYidg7D2 + HsHUXqOFcVxXA1bTI1bTOqaX6giwZSZlKTI1TAAAAh5Bms40QCFCeeu2Oy/j3oRIz1Tz8AcADYHImeUvY0e8JDxAofJgOAAwELuY + GjYnk8wMeI+eEAAEQYuX56/92AY1R42DUAAQFUAPgAGHTCqAAICqAfgAGHTDgACTAgAAgoOAAJMHAAEFAOCcdssHFmGqqpfn+oSK + WB5IpbwgACYADHgGBAAGwBRp8DQTBa8eFSDH13YmP/5gaY1bAMARoo3NBBhBdYb1Ps5K8bAcAhhQbTCwABBBg4BDCg2mFgACCDAc + BGNAHph4AAgHcHARjQB6YeAAIB3AIFEDRJmCn4SYAAgo9mWBPAKimD6XKCiZqmBMMqWDwgACgDCigQgACA8UAAQEiXgHBlAEXDho + RLzi6KViOI5Th52Mg6fxEu8HnCNT+NgyAAICiAHcABBkwpAAEBRAPcABBkw8AAmwJAAIqDwACbB4ABFQPCYZUsF0KkRGQefj1m5Y + HWblvCAAJAAOICoQABYAQccOEDsEduMYn4znuoQCc8s2CPZWs8ZQrQRNdY93ERsSAAaYFAANTwADTBYABqA4SmDJgPGIemA4SmDJ + g+MQ9MABwABBDAAMArb3WARL8vEiZcmOfDEa84koa4YC6PxMcPwzAABAYAAECEAAwHUzeCp3EUnqca9GPKPg+AwgXOUIAIfgsMrl + lCAdwDZAjMNjvO80C/9x/YEADNPQpl99OBFmDM4BQzgHD2TrZ2qwZ4tdBpdc70bl+gAAAGZBnuxFFSh9DY6fhxTH9IpkEdiCexak + JnxsGFUbyhVG8B5hHTIweYR0yNBSd8BB9bmt7/vh+sQ8fuQqAuE6YMdMplHTLTXjYLrTH8XWmP4DpDRMwdIaJmvnO4RPLD3/6BnS + VEM3P8AAAAAjAZ8LQG0NghlYtYo1WPy+Xis7Bbewen4J5WHKxZ6PpjG7vfAAAAAvAZ8NQHUNvKolUY6DJJl/KSTL/OzOyYZ2/v4T + zrzrnoenjoGp6ZjU9M5fL06TZ+AAAAItQZsSNEAhQnnmRRx2W3cK5fHvQiRnjpo8AeAbApE7BHXzyPeEjzBQ+TANABlDE3zgaNiW + TzHuJbmHAAIgxcvzyp2FSzkt/xsUAAQE4DiMXkwUAAQE4OIxeTDgACCgBwjlQmHAAEFAOEcqEwBwTjtlgz37prvf/rDSwDKck5lK + //YYQAIAAgBEgDggACoHFkwUNgAyHC33YhxJshd/hATGlgoaAAOnEcMOLBSPGMvHxt/3zeNlgACCDFAAEEeWAAIIMUAAQR4DkgAB + AhAdMOAAEBRQDkgABAhAdMOAAEBRQoAAyA49mth4ACBx7MtHgUIfCxBe+/3/GBMMqW/CAANAgY4EgACBNwABAcrPuY0CGFR4Pwmr + Y0ypIfaS35XtdeoNw0xi84kNpjXZ42KAAICUByIT0wsAAQEoORCemHgAEVADgjFymHgAEVAOCMXKYDhMMqWFGL11T8esflghna09 + /+9/hABAAEAEgAeEAAUAQQWQeAAsZxjRUK0MsHhAIrZao4AAgALhExXA3lcJpPlnfWIjZwAB5QKAAIAE4AA8oLAAEACByAKUBI6Y + CnQcgClBcdMBToAQABBDAAEAABW3NrAiX5fJkjBMc+GI15hJg1wwFUvyg2ehmAACAwAAID4ABwOpkVKiiDO6wjCvIb577QvwGEAL + nUIAIFwFwMaMAOuAA8gQiHY6PEXZE3/4/sCAEbPVO/ZUCKVHnAMULLsn2z9Vgvxe6DSfCtGPT5AAAABuQZ8wRRUofQ2NisHfisd+ + dgBxI0mPYBxI0mOvLTxBL+p/39GihoR1w8vM8YBEUBeNluW4NJpiMqTTEesGeQHzU3+LqpBj/BViLMRZQgMEjTIIYJGmQh/ayPjG + 4UzmDGTD+UZMP/Zg1Ow5t6Bo1UAAAAAxAZ9PQHUNuXu33j97gO0NMwdoaZmrgmL+Pn8/uXu3z54UvuB+kxYfpMXZzHWBwN8apgAA + AB8Bn1FAdQ28LqYk6mMVGUxalMWrFQ/UEuK4rOvevGqYAAACGkGbVjRAIUJ55k4ebmrX496EUb1poduAPANg7p2AY8nzxc9KvxiE + kuX42A4ghQ9MOAAIKB4ghQ9MOAAIKBIABBQBxAAEBIAVAeAAQUBxAAEBIAVABTJ9zDPSvL0kQCTB4+RFLBOSiS71s9dr8IEAAQAE + gDAgAC441U4zwANRjcodJvgk+E12CDfBGu8AtdAAGU5AAQkC4sFa9mbHwY/19/3Txs4AAQQ+KAAIIM4AAQQ+WAAIIMBwBBRrSYeA + AIBygHAEFGtJh4AAgHKBIADAKPgvQPAAQKPVlo8Aus0Y7y5HQwghFSweEAAIAgwKQCEAAQKHAAEBjoQMlwg+HkTSgweuc/k5Qor3 + PQggiSyAkNL8bAcgxA5MPAAIqAcgxA5MPAAIqBAABFQByAAICYAiA4AAioDkAAQEwBEAMIZ2gjQhzQzbNCb/VFnLBoDhX8hvXefh + EAAQHyAACAiEAAWGFJBioAFmBiLFzCgqG8ngscM92AKq8AAQAHMQBCirhgKIhIX+NO1X9piI2TAAHIAWAAakwAByAFgAGoPA+A8M + gKmDwPg8MgKmACAAIIYABgGbc2wCJfl8kTMExz4YjXnElDXDAVS/JDZ+GYAAIDAAAgQgAGA6mL0bsBGG+McW+/gMIAVHsIAg9Ak1 + azBjBx4AsgQnOx0ed9HgW3WueAGZSPpP/2yICLBWZwDOAcPZEeV8uxT1W6DbeM7tGcQAAABqQZ90RRUofQ2HYNLo9gqXR7DEZmic + ytCh+jVehkBsbB8x8/v8BxMw0zBxMw0zCcf90/+HogWUIQLMEdgrD2GsHogQmHYeNkzbg3HjcVxWA8hImWDyEiZe87AEGqkt/q9A + xqkgNH7hQQND+AAAACABn5NAdQ2IzsTsRpMVSYyZ/P8RlYlYhRpJGmJluW7vwAAAABwBn5VAdQ2S7E7FknYnY4ifz/GUxUpjHbe3 + vd34AAACL0GbmjRAIUKHvMIRj8Z+7cAwDYKy+cDvEfsj3hJogofJgDoAyhHxPDrad8+HETyw4ACCFy/PSrVS3O1UtRO+NgOcABnT + DwACCgecABnTDwACCgQAASYAcIxQAWmHAAEmAcIxQAWmBkHGW9/+8SyVOD1CRSwDAoHtMwKJkqwxAABAgAAEBRBAAF1AWwHEHx1g + ugpwNz+wiUHjoZCGF5wOCObcChEAceOAGd3+2/3vaU5MZHjZwAAgisAOAAgQeF0w4AAQRWAcABAg8Lph4AAgIKBQABASngACAgoL + AAEBKYAAg49mWAdyYN2qOFN8tGGYERypYHCI5Ut4QAAgAEADKCEAAQKFAAEBpGOkzNBhNcJlgeZb3zzyU3KbSAkJLICYSX42A4wA + CMmHAAEVAOMAAjJhwABFQJAAJsAOCOWAFJh4ABNgHBHLACkwO4RNm1eShHxjb8es3LAn8C/qhhE0j+/EAAECEAAQFUEAAWwDUTEF + YNt0jA7sDkcmKXF0QBlOfTohCWVVEU8AAQBBDhgNSQ/cV8iei/zERs4AA0EAQHIMRyYcAAaCAIOQYjkw8AFgA5HTAc6PABYFkdMB + zrwAAQQwACAK29tYES/L5IkYJjnxgjilPIEsHAv/0iOiEMwAAQGAABAfAAOB1MNpuWCMK8jDz338BhAAPHsIAg+Ams0GwhQ7gG0C + E53O87zQL/3Je+ArU+VJgrCLMFU7gGcA4eyfbO1WDPFroNJu18Zz74AAAABzQZ+4RRUofQyev4fsfxsvYm+iQgPISZQg8hJl+fkD + RXz9Gqjh+w+GYau4EcO6wMSaZWMSaYxSfsUv++Nhr+TsQLw9YjsUT2GIDRPTKIaJ6Y0Lueb2Pxu4lSwpwG445huOOP5fv29sJra/ + 2S/djqZHQMapgAAAACcBn9dAdQ2IuVh2sFYtY4mXgr0wvFemWw8E+9z08Yg4i4N6Zcr0ze8AAAAdAZ/ZQHUNghjKYLalMFsapUVK + wflYfz0/E3e3d+AAAAD/QZvbNEAhQnnrlw5n3z1/Hxjq8bA7tMnVAd2mTqgT4oABWf5YABW8YUGW+IA7GNbm4MABDIQTGhGqv+AT + MsbYY0sNteEoDVvzPTGvGxQACsDwGqAUAArDwGqA4A8UAzgDywDB0xvLCUz3yKP1KpYZVLeIRCBwANBBZKvIFgml8vCZ8xIRO3Bl + +NgfJMnqgPkmT1QIeKAAUnPLAAKeEv907LBYiSdvEAAQ6EJCBmiItERSFlI871S64DuFVYhVZozxsSAAWYA6ASIDwACzAdAJEBYA + xQBlgDFAGPibSxNVEhH/Wy/hq+W4DEEMQYABBUHSdzQoiqS7KIIm3anbGyuX + """ + + static let openGOPClampedFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAUEbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAFfkAACw70AAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BC90cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAACw70AAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAsO9AAAAAAABAAAAAAOnbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACw71VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAADUm1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAxJzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFExhdmM2My4xLjEwMiBsaWJ4 + MjY0AAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTAHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAD0AQAA8qgAAAEIc3R0cwAAAAAAAAAfAAAAAQAALuwAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAAB + AAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD + 6QAAAAEAAAu7AAAAAQAAA+kAAAABAAArAwAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAA + AAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAQAAAPpAAAAAQAAC7sAAAAYc3RzcwAAAAAA + AAACAAAAAQAAAB4AAAAcc3RzYwAAAAAAAAABAAAAAQAAADwAAAABAAABBHN0c3oAAAAAAAAAAAAAADwAAAfQAAAD9QAAANIAAAB1 + AAAAKgAAAkYAAABYAAAAMQAAACUAAALLAAAAWgAAACoAAAApAAACQwAAAH4AAAAyAAAAHgAAAjAAAABrAAAALwAAAC0AAAJGAAAA + fgAAADEAAAA7AAACBwAAAGEAAAAwAAAAPwAACOMAAAAXAAABvgAAAEEAAAApAAAAMAAAAfIAAABNAAAAKQAAACkAAAIjAAAATwAA + ADUAAAArAAACIgAAAGoAAAAnAAAAMwAAAjEAAAByAAAANQAAACMAAAIeAAAAbgAAACQAAAAgAAACMwAAAHcAAAArAAAAIQAAAQMA + AAAUc3RjbwAAAAAAAAABAAAFNAAAAGF1ZHRhAAAAWW1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAA + LGlsc3QAAAAkqXRvbwAAABxkYXRhAAAAAQAAAABMYXZmNjMuMS4xMDIAAAAIZnJlZQAAPRhtZGF0AAACngYF//+a3EXpvebZSLeW + LNgg2SPu73gyNjQgLSBjb3JlIDE2NSByMzIyMiBiMzU2MDVhIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIw + MDMtMjAyNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTAgcmVmPTEgZGVibG9j + az0wOjA6MCBhbmFseXNlPTA6MCBtZT1kaWEgc3VibWU9MCBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3Jh + bmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hy + b21hX3FwX29mZnNldD0wIHRocmVhZHM9MiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0 + ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIg + Yl9hZGFwdD0wIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MCBvcGVuX2dvcD0xIHdlaWdodHA9MCBrZXlpbnQ9MzAga2V5aW50 + X21pbj0xNiBzY2VuZWN1dD0wIGludHJhX3JlZnJlc2g9MCByYz1jcmYgbWJ0cmVlPTAgY3JmPTIzLjAgcWNvbXA9MC42MCBxcG1p + bj0wIHFwbWF4PTY5IHFwc3RlcD00IGlwX3JhdGlvPTEuNDAgcGJfcmF0aW89MS4zMCBhcT0wAIAAAAUqZYiEAOgxgAIAEDWcDZnV + ljsx7YYjmP/8AHgvxbYy2DXf11wAeAAIABSgFSOeyQDZX+ugAeD/EtjLaNd/XQYcQAAQBABSIAAIJ4AAgZGAEBM8gSFKEI6Z5g1K + UIAGhq7BiueFovOKBcSMwYrnBSDziDGABaIRwARcdCQYeAFAD+wAILyVvSNV6b68ALO3GkqUkB8DH/Fmuf3KQGBq4gAAQAAAMAVL + D//hAAESAACAGAAIDQQAAgHgACFaACACm+CBUNEykkA9V1vwAH1vUAKD6jg/18QBcCRNrPABbbiJClh4YwAeaTDACpurSCTIAoAA + ILgcAAQAQAOAXLDgACACABwC5fjhBzIBsssaOn2v/4QACAAIBwDQABC7CAAEAkBAAd8gAA0AICJYAvowCQSMWICmJ/vw6ZywBboY + JxUY8IBbc/34Y/AABAOA5IUMYuFzch4UdhMNJeDgAeGyKjAl5OByu/f/gAeTtFnyAuGG/t8BUEUR7EGmJeK+oqHQAAIAJCgFQOfy + RDRX+uvCAAEAQAD2AHBAACCeAAIUqgABAIx9UPcb3N74MCmXGWh5FP0Ii/uCLhQPWOmHBgQZCORwj8WYt7gx2AAgAYO8BPyJFcZe + PCPcjAiEIAB0GURzEmmpcK8qrwwyivak00YId36vuAZAUX6mcKKe59UBcgAAQBgABAEADLgf8AiAgr1c5UVEbb9RCQAAIAwAAgCA + BlwgAAQCwABAHAD7gcAAQBgABAEADLn6enhZwAEAAEAEAh4BaxEguEgfH9DrIR+P/8gAtgACAAKEJKYyRMGg3vuUBQRQQV+uMqqi + Ft2gh9eGEUVzUGmJcL8oqoa8HQ4fC2ADgBAx4CF1llxhu3cbuHr3/wOAAIGwAAgDAu5gEIMOxI5QCin5V6vf8TLNoMaXjstbLFPK + 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 + NCiKpLsogibdqdsbK5c= + """ + + /// The healthy 60 pictures with every other picture, anchors included, pushed 1001 ticks before the + /// clamp, except the last ten of each GOP so the push does not reach the next keyframe: + /// + /// ffmpeg -i healthy.mp4 -map 0:v:0 -c:v copy \ + /// -bsf:v 'setts=ts=max(PTS+1001*mod(N\,2)*lt(mod(N\,30)\,20)\,PREV_OUTDTS+1001)' \ + /// -video_track_timescale 90000 -movflags +faststart pushed.mp4 + static let pushedAnchorsFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAT8bW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAFfkAACw70AAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BCd0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAACw70AAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAsO9AAAAAAABAAAAAAOfbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACw71VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAADSm1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAwpzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFExhdmM2My4xLjEwMiBsaWJ4 + MjY0AAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTAHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAD3gAAA9iIAAAEAc3R0cwAAAAAAAAAeAAAAAQAAMtUAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAAB + AAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAfSAAAAAMAAAPpAAAAAQAAIzEAAAAEAAAD + 6QAAAAEAAAfSAAAAAQAAMtUAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAA + AAEAACMxAAAAAwAAA+kAAAABAAAfSAAAAAMAAAPpAAAAAQAAIzEAAAAEAAAD6QAAAAEAAAu7AAAAGHN0c3MAAAAAAAAAAgAAAAEA + AAAfAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAA8AAAAAQAAAQRzdHN6AAAAAAAAAAAAAAA8AAAH0AAAA/UAAADSAAAAdQAAACoAAAJG + AAAAWAAAADEAAAAlAAACywAAAFoAAAAqAAAAKQAAAkMAAAB+AAAAMgAAAB4AAAIwAAAAawAAAC8AAAAtAAACRgAAAH4AAAAxAAAA + OwAAAgcAAABhAAAAMAAAAD8AAAEHAAAI1wAAAboAAABBAAAAKQAAADAAAAHyAAAATQAAACkAAAApAAACIwAAAE8AAAA1AAAAKwAA + AiIAAABqAAAAJwAAADMAAAIxAAAAcgAAADUAAAAjAAACHgAAAG4AAAAkAAAAIAAAAjMAAAB3AAAAKwAAACEAAAEDAAAAFHN0Y28A + AAAAAAAAAQAABSwAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAA + JKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYzLjEuMTAyAAAACGZyZWUAAD34bWRhdAAAAp4GBf//mtxF6b3m2Ui3lizYINkj7u94 + MjY0IC0gY29yZSAxNjUgcjMyMjIgYjM1NjA1YSAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMjUg + LSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0wIHJlZj0xIGRlYmxvY2s9MDowOjAg + YW5hbHlzZT0wOjAgbWU9ZGlhIHN1Ym1lPTAgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MCBtZV9yYW5nZT0xNiBj + aHJvbWFfbWU9MSB0cmVsbGlzPTAgOHg4ZGN0PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9v + ZmZzZXQ9MCB0aHJlYWRzPTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRl + cmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9 + MCBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTAgb3Blbl9nb3A9MCB3ZWlnaHRwPTAga2V5aW50PTMwIGtleWludF9taW49MTYg + c2NlbmVjdXQ9MCBpbnRyYV9yZWZyZXNoPTAgcmM9Y3JmIG1idHJlZT0wIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1h + eD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIHBiX3JhdGlvPTEuMzAgYXE9MACAAAAFKmWIhADoMYACABA1nA2Z1ZY7Me2GI5j/ + /AB4L8W2Mtg139dcAHgACAAUoBUjnskA2V/roAHg/xLYy2jXf10GHEAAEAQAUiAACCeAAIGRgBATPIEhShCOmeYNSlCABoauwYrn + haLzigXEjMGK5wUg84gxgAWiEcAEXHQkGHgBQA/sACC8lb0jVem+vACztxpKlJAfAx/xZrn9ykBgauIAAEAAADAFSw//4QABEgAA + gBgACA0EAAIB4AAhWgAgApvggVDRMpJAPVdb8AB9b1ACg+o4P9fEAXAkTazwAW24iQpYeGMAHmkwwAqbq0gkyAKAACC4HAAEAEAD + gFyw4AAgAgAcAuX44QcyAbLLGjp9r/+EAAgACAcA0AAQuwgABAJAQAHfIAANACAiWAL6MAkEjFiApif78OmcsAW6GCcVGPCAW3P9 + +GPwAAQDgOSFDGLhc3IeFHYTDSXg4AHhsiowJeTgcrv3/4AHk7RZ8gLhhv7fAVBFEexBpiXivqKh0AACACQoBUDn8kQ0V/rrwgAB + AEAA9gBwQAAgngACFKoAAQCMfVD3G9ze+DAplxloeRT9CIv7gi4UD1jphwYEGQjkcI/FmLe4MdgAIAGDvAT8iRXGXjwj3IwIhCAA + dBlEcxJpqXCvKq8MMor2pNNGCHd+r7gGQFF+pnCinufVAXIAAEAYAAQBAAy4H/AIgIK9XOVFRG2/UQkAACAMAAIAgAZcIAAEAsAA + QBwA+4HAAEAYAAQBAAy5+np4WcABAABABAIeAWsRILhIHx/Q6yEfj//IALYAAgAChCSmMkTBoN77lAUEUEFfrjKqohbdoIfXhhFF + c1BpiXC/KKqGvB0OHwtgA4AQMeAhdZZcYbt3G7h69/8DgACBsAAIAwLuYBCDDsSOUAop+Ver3/EyzaDGl47LWyxTyoryipaqXlFS + 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 + qdsbK5c= + """ + + /// The healthy 60 pictures pushed so that the second keyframe lands 2002 ticks late as well: + /// + /// ffmpeg -i healthy.mp4 -map 0:v:0 -c:v copy \ + /// -bsf:v 'setts=ts=max(PTS+1001*mod(N\,2)*lt(N\,40)+3003*eq(N\,29)\,PREV_OUTDTS+1001)' \ + /// -video_track_timescale 90000 -movflags +faststart pushed-keyframe.mp4 + static let pushedKeyframeFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAUEbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAFfkAACw70AAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BC90cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAACw70AAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAsO9AAAAAAABAAAAAAOnbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACw71VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAADUm1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAxJzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFExhdmM2My4xLjEwMiBsaWJ4 + MjY0AAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTAHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAD3gAAA9iIAAAEIc3R0cwAAAAAAAAAfAAAAAQAAMtUAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAAB + AAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAMAAAPpAAAAAQAAIzEAAAADAAAD + 6QAAAAEAAAu7AAAAAQAAA+kAAAABAAArAwAAAAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAfSAAA + AAMAAAPpAAAAAQAAIzEAAAADAAAD6QAAAAEAACMxAAAAAwAAA+kAAAABAAAjMQAAAAQAAAPpAAAAAQAAC7sAAAAYc3RzcwAAAAAA + AAACAAAAAQAAAB8AAAAcc3RzYwAAAAAAAAABAAAAAQAAADwAAAABAAABBHN0c3oAAAAAAAAAAAAAADwAAAfQAAAD9QAAANIAAAB1 + AAAAKgAAAkYAAABYAAAAMQAAACUAAALLAAAAWgAAACoAAAApAAACQwAAAH4AAAAyAAAAHgAAAjAAAABrAAAALwAAAC0AAAJGAAAA + fgAAADEAAAA7AAACBwAAAGEAAAAwAAAAPwAAAQcAAAjXAAABugAAAEEAAAApAAAAMAAAAfIAAABNAAAAKQAAACkAAAIjAAAATwAA + ADUAAAArAAACIgAAAGoAAAAnAAAAMwAAAjEAAAByAAAANQAAACMAAAIeAAAAbgAAACQAAAAgAAACMwAAAHcAAAArAAAAIQAAAQMA + AAAUc3RjbwAAAAAAAAABAAAFNAAAAGF1ZHRhAAAAWW1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAA + LGlsc3QAAAAkqXRvbwAAABxkYXRhAAAAAQAAAABMYXZmNjMuMS4xMDIAAAAIZnJlZQAAPfhtZGF0AAACngYF//+a3EXpvebZSLeW + LNgg2SPu73gyNjQgLSBjb3JlIDE2NSByMzIyMiBiMzU2MDVhIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIw + MDMtMjAyNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTAgcmVmPTEgZGVibG9j + az0wOjA6MCBhbmFseXNlPTA6MCBtZT1kaWEgc3VibWU9MCBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3Jh + bmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hy + b21hX3FwX29mZnNldD0wIHRocmVhZHM9MiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0 + ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIg + Yl9hZGFwdD0wIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MCBvcGVuX2dvcD0wIHdlaWdodHA9MCBrZXlpbnQ9MzAga2V5aW50 + X21pbj0xNiBzY2VuZWN1dD0wIGludHJhX3JlZnJlc2g9MCByYz1jcmYgbWJ0cmVlPTAgY3JmPTIzLjAgcWNvbXA9MC42MCBxcG1p + bj0wIHFwbWF4PTY5IHFwc3RlcD00IGlwX3JhdGlvPTEuNDAgcGJfcmF0aW89MS4zMCBhcT0wAIAAAAUqZYiEAOgxgAIAEDWcDZnV + ljsx7YYjmP/8AHgvxbYy2DXf11wAeAAIABSgFSOeyQDZX+ugAeD/EtjLaNd/XQYcQAAQBABSIAAIJ4AAgZGAEBM8gSFKEI6Z5g1K + UIAGhq7BiueFovOKBcSMwYrnBSDziDGABaIRwARcdCQYeAFAD+wAILyVvSNV6b68ALO3GkqUkB8DH/Fmuf3KQGBq4gAAQAAAMAVL + D//hAAESAACAGAAIDQQAAgHgACFaACACm+CBUNEykkA9V1vwAH1vUAKD6jg/18QBcCRNrPABbbiJClh4YwAeaTDACpurSCTIAoAA + ILgcAAQAQAOAXLDgACACABwC5fjhBzIBsssaOn2v/4QACAAIBwDQABC7CAAEAkBAAd8gAA0AICJYAvowCQSMWICmJ/vw6ZywBboY + JxUY8IBbc/34Y/AABAOA5IUMYuFzch4UdhMNJeDgAeGyKjAl5OByu/f/gAeTtFnyAuGG/t8BUEUR7EGmJeK+oqHQAAIAJCgFQOfy + RDRX+uvCAAEAQAD2AHBAACCeAAIUqgABAIx9UPcb3N74MCmXGWh5FP0Ii/uCLhQPWOmHBgQZCORwj8WYt7gx2AAgAYO8BPyJFcZe + PCPcjAiEIAB0GURzEmmpcK8qrwwyivak00YId36vuAZAUX6mcKKe59UBcgAAQBgABAEADLgf8AiAgr1c5UVEbb9RCQAAIAwAAgCA + BlwgAAQCwABAHAD7gcAAQBgABAEADLn6enhZwAEAAEAEAh4BaxEguEgfH9DrIR+P/8gAtgACAAKEJKYyRMGg3vuUBQRQQV+uMqqi + Ft2gh9eGEUVzUGmJcL8oqoa8HQ4fC2ADgBAx4CF1llxhu3cbuHr3/wOAAIGwAAgDAu5gEIMOxI5QCin5V6vf8TLNoMaXjstbLFPK + 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 + KIqkuyiCJt2p2xsrlw== + """ + + /// 64x64 H.264 High, 29.97 fps, 60 frames, whose first GOP opens on twelve P pictures before its + /// first B picture (x264 frame types from a qpfile: `0 I -1`, then `1 P -1` to `12 P -1`). Written + /// through Matroska, so its timestamps sit on whole milliseconds rather than the 3003 lattice; the + /// test compares display order instead of values. + /// + /// ffmpeg -f lavfi -i 'testsrc=s=64x64:r=30000/1001:d=3' -frames:v 60 -pix_fmt yuv420p src.y4m + /// x264 --preset ultrafast --bframes 3 --b-pyramid normal --b-adapt 0 --keyint 30 \ + /// --min-keyint 30 --scenecut 0 --qpfile qp.txt -o p-run.mkv src.y4m + /// ffmpeg -i p-run.mkv -c:v copy -video_track_timescale 90000 -movflags +faststart healthy.mp4 + /// (clamped.mp4 as above) + static let leadingPRunHealthyFixtureBase64 = """ + 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 + AAkUAAABtwAAAEMAAAApAAAAMgAAAdoAAABPAAAAIwAAAC0AAAIpAAAAUAAAAC8AAAAvAAACKQAAAHIAAAAhAAAAOgAAAikAAABp + AAAANQAAACEAAAIXAAAAcAAAAC0AAAAjAAACMgAAAHQAAAAsAAAAJgAAAQEAAAAUc3RjbwAAAAAAAAABAAAIBAAAAGF1ZHRhAAAA + WW1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALGlsc3QAAAAkqXRvbwAAABxkYXRhAAAAAQAAAABM + YXZmNjMuMS4xMDIAAAAIZnJlZQAAQO1tZGF0AAACngYF//+a3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2NSByMzIyMiBi + MzU2MDVhIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4u + b3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTAgcmVmPTEgZGVibG9jaz0wOjA6MCBhbmFseXNlPTA6MCBtZT1kaWEgc3Vi + bWU9MCBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4 + eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9MiBsb29r + YWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0 + PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0wIGJfYmlhcz0wIGRpcmVjdD0xIHdl + aWdodGI9MCBvcGVuX2dvcD0wIHdlaWdodHA9MCBrZXlpbnQ9MzAga2V5aW50X21pbj0xNiBzY2VuZWN1dD0wIGludHJhX3JlZnJl + c2g9MCByYz1jcmYgbWJ0cmVlPTAgY3JmPTIzLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IGlwX3JhdGlv + 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 + """ + + static let leadingPRunClampedFixtureBase64 = """ + AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAUsbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAFfkAACw7sAAQAAAQAA + AAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + BFd0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAACw7sAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAA + AAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAsO7AAAAAAABAAAAAAPPbWRpYQAAACBtZGhk + AAAAAAAAAAAAAAAAAAFfkAACw7sVxwAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAADem1p + bmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAzpzdGJsAAAAtnN0c2QA + AAAAAAAAAQAAAKZhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAGP//AAAALGF2Y0MBTUAK/+EAFWdNQArsohNgIgAAB9IAAdTBHiRLLAEABGjOD8gAAAAQcGFzcAAAAAEA + AAABAAAAFGJ0cnQAAAAAAAEDUQABAeMAAAEwc3R0cwAAAAAAAAAkAAAAAQAAC7oAAAABAAALuQAAAAEAAAu+AAAAAQAAC7oAAAAB + AAALuQAAAAEAAAu+AAAAAQAAC7oAAAABAAALuQAAAAEAAAu+AAAAAQAAC7oAAAABAAALuQAAAAEAAAu+AAAAAQAALusAAAADAAAD + 6QAAAAEAACMvAAAAAwAAA+kAAAABAAAjNAAAAAMAAAPpAAAAAQAAIzAAAAAEAAAD6QAAAAEAAAfTAAAAAQAALusAAAADAAAD6QAA + AAEAACMvAAAAAwAAA+kAAAABAAAjNAAAAAMAAAPpAAAAAQAAIzAAAAADAAAD6QAAAAEAACMvAAAAAwAAA+kAAAABAAAjNAAAAAMA + AAPpAAAAAQAAIzAAAAAEAAAD6QAAAAEAAAu6AAAAGHN0c3MAAAAAAAAAAgAAAAEAAAAfAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAA8 + AAAAAQAAAQRzdHN6AAAAAAAAAAAAAAA8AAAH0AAAAwUAAADxAAABJgAAAQ4AAAEYAAAA/gAAASoAAAEVAAAA+gAAAQYAAAEAAAAB + UgAAAjEAAABqAAAALgAAACwAAAI2AAAAYwAAADMAAAAqAAACQQAAAHYAAAAmAAAAOgAAAg0AAABfAAAAKQAAADIAAAEUAAAJFAAA + AbcAAABDAAAAKQAAADIAAAHaAAAATwAAACMAAAAtAAACKQAAAFAAAAAvAAAALwAAAikAAAByAAAAIQAAADoAAAIpAAAAaQAAADUA + AAAhAAACFwAAAHAAAAAtAAAAIwAAAjIAAAB0AAAALAAAACYAAAEBAAAAFHN0Y28AAAAAAAAAAQAABVwAAABhdWR0YQAAAFltZXRh + AAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYz + LjEuMTAyAAAACGZyZWUAAEDtbWRhdAAAAp4GBf//mtxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxNjUgcjMyMjIgYjM1NjA1 + YSAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMjUgLSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94 + MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0wIHJlZj0xIGRlYmxvY2s9MDowOjAgYW5hbHlzZT0wOjAgbWU9ZGlhIHN1Ym1lPTAg + cHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MCBtZV9yYW5nZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTAgOHg4ZGN0 + PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9MCB0aHJlYWRzPTIgbG9va2FoZWFk + X3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNv + bnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MCBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRi + PTAgb3Blbl9nb3A9MCB3ZWlnaHRwPTAga2V5aW50PTMwIGtleWludF9taW49MTYgc2NlbmVjdXQ9MCBpbnRyYV9yZWZyZXNoPTAg + cmM9Y3JmIG1idHJlZT0wIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQw + IHBiX3JhdGlvPTEuMzAgYXE9MACAAAAFKmWIhADoMYACABA1nA2Z1ZY7Me2GI5j//AB4L8W2Mtg139dcAHgACAAUoBUjnskA2V/r + oAHg/xLYy2jXf10GHEAAEAQAUiAACCeAAIGRgBATPIEhShCOmeYNSlCABoauwYrnhaLzigXEjMGK5wUg84gxgAWiEcAEXHQkGHgB + QA/sACC8lb0jVem+vACztxpKlJAfAx/xZrn9ykBgauIAAEAAADAFSw//4QABEgAAgBgACA0EAAIB4AAhWgAgApvggVDRMpJAPVdb + 8AB9b1ACg+o4P9fEAXAkTazwAW24iQpYeGMAHmkwwAqbq0gkyAKAACC4HAAEAEADgFyw4AAgAgAcAuX44QcyAbLLGjp9r/+EAAgA + CAcA0AAQuwgABAJAQAHfIAANACAiWAL6MAkEjFiApif78OmcsAW6GCcVGPCAW3P9+GPwAAQDgOSFDGLhc3IeFHYTDSXg4AHhsiow + JeTgcrv3/4AHk7RZ8gLhhv7fAVBFEexBpiXivqKh0AACACQoBUDn8kQ0V/rrwgABAEAA9gBwQAAgngACFKoAAQCMfVD3G9ze+DAp + lxloeRT9CIv7gi4UD1jphwYEGQjkcI/FmLe4MdgAIAGDvAT8iRXGXjwj3IwIhCAAdBlEcxJpqXCvKq8MMor2pNNGCHd+r7gGQFF+ + pnCinufVAXIAAEAYAAQBAAy4H/AIgIK9XOVFRG2/UQkAACAMAAIAgAZcIAAEAsAAQBwA+4HAAEAYAAQBAAy5+np4WcABAABABAIe + AWsRILhIHx/Q6yEfj//IALYAAgAChCSmMkTBoN77lAUEUEFfrjKqohbdoIfXhhFFc1BpiXC/KKqGvB0OHwtgA4AQMeAhdZZcYbt3 + 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 + """ +} diff --git a/Tests/AetherEngineTests/RepairedKeyframeSegmentReachTests.swift b/Tests/AetherEngineTests/RepairedKeyframeSegmentReachTests.swift new file mode 100644 index 000000000..4da204f92 --- /dev/null +++ b/Tests/AetherEngineTests/RepairedKeyframeSegmentReachTests.swift @@ -0,0 +1,88 @@ +import Testing +@testable import AetherEngine + +/// A #409 repair that synthesizes the decode ladder moves a late-written IDR back onto its sequence, +/// so the keyframe arrives before the index entry the segment plan was built from. The cutter and the +/// restart gate reach back for it; without that the keyframe stayed in the previous segment and the +/// next one opened on audio mid-GOP (25 of 40 segments on the 23 fps reporting asset). +@Suite("Segment boundaries reach a repaired keyframe placed before its index entry") +struct RepairedKeyframeSegmentReachTests { + + // 3913 ticks per picture (23 fps at 90 kHz); boundaries are the index entries. + private let boundaries: [Int64] = [0, 352_170, 716_087, 1_056_522] + + @Test("a keyframe three pictures early opens its segment once the boundary reaches for it") + func earlyKeyframeOpensSegmentWithinReach() { + var without = VODSegmentCutter(sourceBoundaries: boundaries, planAnchorPts: 0, baseIndex: 0) + _ = without.index(pts: 0, isKeyframe: true) + #expect(without.index(pts: 352_170, isKeyframe: true) == 1) + #expect(without.index(pts: 716_087 - 3 * 3913, isKeyframe: true) == 1) + + var with = VODSegmentCutter(sourceBoundaries: boundaries, planAnchorPts: 0, baseIndex: 0, + reachTicks: [0, 17_608, 17_608, 17_608]) + _ = with.index(pts: 0, isKeyframe: true) + #expect(with.index(pts: 352_170, isKeyframe: true) == 1) + #expect(with.index(pts: 716_087 - 3 * 3913, isKeyframe: true) == 2) + // Pictures after it stay with it, and non-keyframes never cut, reach or not. + #expect(with.index(pts: 1_056_522 - 3913, isKeyframe: false) == 2) + } + + @Test("each boundary's reach stops short of the keyframe before it") + func reachStopsShortOfPreviousKeyframe() { + // Two keyframes four pictures apart at the third boundary: the advance (17 608) would let the + // first one open that boundary's segment. + let keyframes: [Int64] = [0, 352_170, 716_087 - 4 * 3913, 716_087, 1_056_522] + let reach = HLSVideoEngine.keyframeReachTicks(advance: 17_608, keyframes: keyframes, + boundaries: [0, 352_170, 716_087, 1_056_522]) + #expect(reach == [17_608, 17_608, 4 * 3913 - 1, 17_608]) + var cutter = VODSegmentCutter(sourceBoundaries: [0, 352_170, 716_087, 1_056_522, 1_400_000], + planAnchorPts: 0, baseIndex: 0, reachTicks: reach) + _ = cutter.index(pts: 0, isKeyframe: true) + _ = cutter.index(pts: 352_170, isKeyframe: true) + #expect(cutter.index(pts: 716_087 - 4 * 3913, isKeyframe: true) == 1) + #expect(cutter.index(pts: 716_087, isKeyframe: true) == 2) + } + + @Test("the reach is read off the plan after its short segments are folded") + func reachFollowsCollapsedPlan() { + // 90 kHz. Keyframes at 8.0 s and 8.11 s make a 0.11 s segment, which folds into the one before. + let starts: [Int64] = [0, 360_000, 720_000, 730_000, 1_080_000] + let plan = zip(starts, starts.dropFirst() + [1_440_000]).map { start, end in + HLSVideoEngine.Segment(startPts: start, endPts: end, startSeconds: Double(start) / 90_000, + durationSeconds: Double(end - start) / 90_000) + } + let final = HLSVideoEngine.finalizedPlan(plan, minDurationSeconds: 1, keyframeAdvance: 17_608, + keyframes: starts) + // The fold keeps the later keyframe as the boundary, so its reach stops short of the one at 8.0 s. + #expect(final.plan.map(\.startPts) == [0, 360_000, 730_000, 1_080_000]) + #expect(final.reach == [17_608, 17_608, 9_999, 17_608]) + // Read off the unfolded plan instead, 730 000 took 720 000's reach (so the keyframe before it + // could open its segment) and 1 080 000 took 730 000's (so its own keyframe could miss it). + let unfolded = HLSVideoEngine.keyframeReachTicks(advance: 17_608, keyframes: starts, boundaries: starts) + #expect(Array(unfolded[2...3]) == [17_608, 9_999]) + } + + @Test("no repair, no reach") + func noAdvanceNoReach() { + #expect(HLSVideoEngine.keyframeReachTicks(advance: 0, keyframes: [0, 1000], boundaries: [0, 1000]).isEmpty) + let cutter = VODSegmentCutter(sourceBoundaries: boundaries, planAnchorPts: 0, baseIndex: 0) + #expect(cutter.reach(at: 1) == 0) + } + + @Test("the restart gate opens on the early keyframe instead of waiting a GOP") + func restartGateTakesEarlyKeyframe() { + let target: Int64 = 716_087 + #expect(!HLSSegmentProducer.videoGateTargetSatisfied(pts: target - 2 * 3913, dts: target - 4 * 3913, + targetPts: target)) + #expect(HLSSegmentProducer.videoGateTargetSatisfied(pts: target - 2 * 3913, dts: target - 4 * 3913, + targetPts: target, reachTicks: 17_608)) + } + + @Test("only a synthesized ladder moves keyframes, by the reorder delay plus two and a half pictures") + func keyframeAdvanceLimit() { + var plan = H264CompositionOffsetRepair.Plan(step: 3913, decodeLead: 7826, shift: 7826, pocStep: 2) + #expect(plan.keyframeAdvanceLimit == 0) + plan.synthesizesDecodeLadder = true + #expect(plan.keyframeAdvanceLimit == 7826 + 2 * 3913 + 1956) + } +} diff --git a/docs/architecture.md b/docs/architecture.md index d207e70b5..2b429c22a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -605,7 +605,7 @@ Sources/AetherEngine/ │ ├── PlanBoundaryAxis.swift Which axis a container stamps its index entries on, and therefore which packet timestamp the cutter gate may compare against a plan boundary: decode for a mov/mp4 sample table, presentation for a Matroska Cue (#358, AE#561) │ ├── VideoConfigRecord.swift The `hvcC` / `avcC` config record and the framing question hanging off it (#365): movenc decides whether to Annex-B-convert every sample from the EXTRADATA rather than the packet, so a source carrying Annex-B parameter sets while muxing length-prefixed NALs has each sample rewritten by a converter that finds no start codes. Mirrors movenc's own two tests instead of an equivalent-looking predicate, since predicting that decision wrong is the whole defect │ ├── H264SPS.swift Hand-rolled H.264 SPS parser (SSAI ad-creative coded dimensions / codec config) -│ ├── H264CompositionOffsetRepair.swift Rebuilds the presentation axis of an MP4 whose writer dropped `ctts` while the bitstream still reorders pictures: libavcodec's H.264 or HEVC parser supplies each access unit's picture order count without decoding, a fail-closed head sample settles the ladder and the shift, and packets are rewritten to the timeline a correct muxer would have written, so the native path and hardware decode are kept (#409, HEVC #699) +│ ├── H264CompositionOffsetRepair.swift Rebuilds the presentation axis of an MP4 whose writer dropped `ctts` while the bitstream still reorders pictures: libavcodec's H.264 or HEVC parser supplies each access unit's picture order count without decoding, a fail-closed head sample settles the ladder and the shift, and packets are rewritten to the timeline a correct muxer would have written, so the native path and hardware decode are kept (#409, HEVC #699); a writer that forced the decode ladder monotonic is rebuilt at the stream's frame rate, measured on its anchors │ ├── OutputTimestampSanitizer.swift Final-stage DTS/PTS monotonicity guard before the fMP4 mux (SSAI splices, program restarts) │ ├── RestartCoalescer.swift Coalesces a burst of producer-restart requests into one in-flight + one settled target (rapid-seek, AetherEngine#35) │ ├── LiveWindow.swift Live path: session-relative DVR timeline (seconds since first frame), shared by the native and SW live paths diff --git a/docs/formats.md b/docs/formats.md index 5d42734f4..184539eab 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -99,6 +99,61 @@ exactly matches the observed reorder head, and a complete, distinct picture-orde timestamps pass the slot policy. It assigns existing slots plus that lead rather than fitting a frame rate; DTS, audio and the keyframe index remain unchanged. An ambiguous head stays untouched. +Some writers go one step further: after dropping `ctts` they force the decode-order presentation +times to be monotonic, so each B picture (and on some files each anchor too) decoded after another +picture is pushed 1001 ticks (at 90 kHz, a third of a 29.97 fps frame) past the picture before +it. There is then no ladder to read a step from: the reporting file (H.264 High, 720p, GOP 90) +stepped 3003 on 87% of its samples and 1001, 2252, 9009 or 12012 on the rest, so the probe skipped it +as non-uniform and QuickTime, the Files app and both engine routes showed the B pictures out of +order. When nothing above takes such a head, it is repaired with the step taken from the stream's +average frame rate, provided the sampled span is the time its pictures take at that rate within the +reorder delay plus two pictures, and every other check above still passes. The step is then +remeasured on the anchors when the writer left them on the presentation axis (the average rate is +only an estimate: a 60-picture 29.97 fps clip reports 29.80). The reporting file pushed its P pictures +as well (each anchor 1001 ticks past the picture before it), so no two anchors agree; the rate is +then the one the bitstream's VUI timing states, if that is within 1% of the average rate, snapped to +a standard frame rate (any whole rate up to 240, or an NTSC one such as 23.976 or 29.97) within +0.01%. Without a VUI rate it is the standard rate nearest the average rate, if that is within 1%. The +VUI is what tells 24 from 23.976 on a short clip whose pushed tail drags the average, and on all three +reporting files it named the exact rate (23, 29.97 and 29.0002 fps). Pictures are placed on that +rate as an exact fraction of a tick, so a 23 fps file (3913.04 ticks at 90 kHz) does not lose a tick +every 25 pictures. A head with neither is left untouched. DTS is rebuilt from each picture's +decode slot rather than shifted, since a pushed B picture's DTS can sit after its own presentation +time. The writer pushes keyframes as well, and only ever later: on both reporting files they sat 0 to +4 pictures after the end of the GOP before them, which left a gap or put the next GOP in front of the +last pictures of the previous one. A later IDR therefore continues the sequence before it, unless it +is later than the reorder delay plus two and a half pictures (a real discontinuity; a third reporting +file at 29 fps kept its keyframes four pictures late for a hundred seconds) or within half a tick per +picture (all a rounded step can be off by); an earlier one keeps its own time, which is how a chain +started on a pushed keyframe after a seek moves back into place. Counting alone follows the stated +rate, and the writer's clock is not quite that (the third file states 29.0002 fps and writes 29.003): +the chain drifted 61 ms behind the writer over 19 minutes and overlapped 3 times where an earlier +keyframe pulled it back. A continued sequence therefore also leans toward the earliest origin the +pictures before it point to (the writer only ever pushes a picture later, so the earliest is the +nearest the truth), by at most an eighth of a picture per GOP, which removed both. A seek landing +holds its first **8 pictures** and takes the earliest origin they point to in the same way. A keyframe +placed back like this sits before its index entry, by at most the reorder delay plus two and a half +pictures, while the HLS segment plan is built from the index. The VOD cutter and the restart gate +therefore reach that far back for the keyframe each boundary names, never as far as the keyframe +before it: without the reach the keyframe stayed in the previous segment and the next one opened on +audio, mid-GOP, which `aetherctl segverify` counted on 25 of 40 segments of the 23 fps file and 22 of +the 29.97 fps one. All 40 are independent now, and so are segments a restart produced. The second +reporting file also opens +every GOP on twelve P pictures before its first B, so a head that has not reordered after twelve +pictures, with `PTS == DTS` throughout, is sampled on to its first regression plus a mini-GOP, up to +**48 pictures**. The sampled span is checked against the rounded step with a tick per picture to +spare, which is what the third file's head needed. After a seek onto an open-GOP +keyframe, which kept its own presentation time, the sequence origin is taken from its display rank +rather than its decode slot. The shift is what puts the first +keyframe on the stream's start time, clamped to **16 pictures** because this writer left the keyframe +four pictures ahead. Measured on a generated twin (`Scripts/make-clamped-ladder-fixture.sh`, 900 +pictures at 29.97 fps), the AVPlayer route stepped backwards 169 times before and 0 after, and the +software route 44 times before and 0 after. Its `pushed-anchors.mp4`, which pushes the anchors as +well, was left untouched before ("anchors do not agree on a step", 44 backward steps on the software +route) and is repaired with 0 after; both now demux to the healthy twin's timestamps, including +the GOP whose keyframe the clamp pushed. Its `p-run-clamped.mp4` (x264 CLI only) opens every GOP on +twelve P pictures: 99 of 239 backward steps before, 0 after. + ### Matroska with presentation slots in coding order Matroska block timestamps are presentation timestamps by specification, and the format has no