iOS-Zeitachsen prüfen: CMTime-Präzision und Intervallgrenzen
Wenn Untertitel am Übergang zwischen zwei Segmenten kurz aufblitzen oder ein zugeschnittener Clip ein Bild zu viel enthält, sollte nicht gleich der Player geändert werden. Übertragen Sie zunächst die betroffenen Zeitpunkte auf einen Cloud-Mac und prüfen Sie die Berechnungen mit einem eigenständigen Swift-Skript. Anschließend kontrollieren Sie das Decodierungsergebnis in der App. So lassen sich Rechenfehler von ungenauen Wiedergabepositionen trennen, ohne immer wieder das gesamte Ausgangsmaterial hochladen zu müssen.
Zuerst die Zeitregeln festlegen, nicht in Sekunden umrechnen
CMTime stellt Zeit mit einem ganzzahligen Zähler value und einer Zeitskala timescale dar. Zusätzlich enthält der Wert Angaben zu Gültigkeit, Rundung und epoch. So lässt sich 1/30 Sekunde exakt darstellen; bei der Umrechnung in ganzzahlige Millisekunden geht diese Genauigkeit verloren. Die Formatierung von seconds mit drei Nachkommastellen ist lediglich eine Darstellungsentscheidung und keine Grundlage dafür, zwei Zeitwerte als gleich zu beurteilen.
Halten Sie zunächst vier Regeln im Repository fest: Eingaben müssen numerische Zeitwerte sein; Zeitwerte, die auf derselben Zeitachse miteinander verrechnet werden, müssen dieselbe epoch verwenden; Segmente werden durch Startzeit und Dauer beschrieben; der Endzeitpunkt gehört nicht mehr zum aktuellen Segment. Die Dauer darf nicht negativ sein. Segmente mit einer Dauer von null werden je nach Fachlogik abgelehnt oder als leere Segmente behandelt.
Die Zeitskala ist keine Bildrate. Videobildrate, Audioabtastrate und die Zeiteinheiten einer Anwendungsschnittstelle sind nicht austauschbar, nur weil sie alle ganzzahlig angegeben werden.
Dieses Beispiel behandelt ausschließlich gewöhnliche Zeitachsen mit epoch null. Für Zeitwerte aus wiederholten Wiedergabedurchläufen oder mit anderen Epochennummern muss zunächst eine Abbildung definiert werden. Es reicht nicht, nur Zähler und Nenner zu übernehmen und daraus einen neuen Zeitwert zu konstruieren.
Eine minimale Prüfung auf dem Cloud-Mac ausführen
Richten Sie eine funktionierende Xcode-Kommandozeilenumgebung ein. Prüfen Sie mit xcrun --find swift und xcrun --sdk macosx --show-sdk-path, ob Compiler und SDK gefunden werden. Speichern Sie den folgenden Code als timeline-check.swift. Er verwendet ausschließlich System-Frameworks; Mediendateien müssen nicht heruntergeladen werden.
import CoreMedia
import Foundation
func check(_ condition: Bool, _ name: String) {
if !condition {
fatalError("Check failed: \(name)")
}
}
let frame = CMTime(value: 1, timescale: 30)
let sameFrame = CMTime(value: 20, timescale: 600)
check(frame.isNumeric, "numeric-input")
check(frame.epoch == 0, "expected-epoch")
check(CMTimeCompare(frame, sameFrame) == 0, "rational-equality")
let sum = CMTimeAdd(frame, frame)
check(
CMTimeCompare(sum, CMTime(value: 1, timescale: 15)) == 0,
"exact-addition"
)
let third = CMTime(value: 1, timescale: 3)
let exact = CMTimeConvertScale(
third,
timescale: 600,
method: .roundHalfAwayFromZero
)
check(exact.value == 200, "exact-conversion")
check(!exact.hasBeenRounded, "exact-rounding-flag")
let rounded = CMTimeConvertScale(
third,
timescale: 1000,
method: .roundHalfAwayFromZero
)
check(rounded.value == 333, "rounded-value")
check(rounded.hasBeenRounded, "rounded-flag")
let segment = CMTimeRange(
start: CMTime(value: 0, timescale: 30),
duration: CMTime(value: 30, timescale: 30)
)
check(CMTimeRangeContainsTime(segment, time: .zero), "start-included")
check(
CMTimeRangeContainsTime(
segment,
time: CMTime(value: 29, timescale: 30)
),
"last-frame-start-included"
)
check(
!CMTimeRangeContainsTime(segment, time: CMTimeRangeGetEnd(segment)),
"end-excluded"
)
check(!CMTime.invalid.isNumeric, "invalid-rejected")
check(!CMTime.indefinite.isNumeric, "indefinite-rejected")
check(!CMTime.positiveInfinity.isNumeric, "infinity-rejected")
xcrun swift timeline-check.swift
Wenn alle Prüfungen erfolgreich sind, endet das Skript ohne Ausgabe. Schlägt eine Prüfung fehl, liefert es einen von null verschiedenen Exit-Status. Die Pipeline sollte direkt diesen Status auswerten, statt den Erfolg vom Auftreten eines bestimmten Wortes im Log abhängig zu machen. Der Code verwendet explizite Prüfungen anstelle von assert, das in optimierten Builds entfernt werden kann.
Genauigkeitsverluste auf Schnittstellengrenzen beschränken
Derselbe Zahlenwert kann unterschiedlich dargestellt werden. Vergleichen Sie Zeitwerte deshalb mit CMTimeCompare, nicht anhand ihrer beiden value-Werte. Verwenden Sie zum Addieren CMTimeAdd, statt die Zähler direkt zu addieren. Ganzzahlarithmetik kann nur dann gleichwertig sein, wenn Zeitskala und Epoche übereinstimmen und der Umgang mit Überläufen eindeutig festgelegt ist.
Bei der Umrechnung der Zeitskala muss die Rundungsmethode ausdrücklich festgelegt werden. Ob der Start eines Zuschnitts zu einem früheren oder späteren Zeitpunkt verschoben wird, beeinflusst, ob das Bild an der Grenze erhalten bleibt. Diese Produktentscheidung darf nicht einem Standardverhalten überlassen werden. Die Umrechnung einer Drittelsekunde im Skript ist lediglich ein Prüffall, keine Empfehlung, für sämtliche Medien einheitlich Millisekunden zu verwenden.
hasBeenRounded zeigt an, dass das Ergebnis eine Rundung aufweist, sagt aber nichts über die Größe des Fehlers aus. Das Flag kann auch aus vorgelagerten Berechnungen übernommen worden sein. Wenn eine Fehlertoleranz eingehalten werden muss, bewahren Sie den ursprünglichen Zeitwert auf, berechnen die Differenz zwischen dem Wert vor und nach der Umrechnung und vergleichen sie mit der fachlich zulässigen Abweichung. Ein gesetztes Flag sollte nicht pauschal zum Fehlschlag führen; ein nicht gesetztes Flag ist umgekehrt kein Grund, auf die Eingabeprüfung zu verzichten.
Intervalle mit Testfällen an den Übergängen prüfen
Legen Sie für zwei benachbarte Segmente die Intervalle [0, 1) und [1, 2) an. Ein Zeitpunkt von exakt einer Sekunde darf nur dem zweiten Segment zugeordnet werden. Damit können Untertitelwechsel, Segmentauswahl und das Einrasten beim Ziehen dieselben Regeln verwenden, ohne dass beide Segmente gleichzeitig aktiv werden.
Prüfen Sie für jedes fachliche Intervall mindestens den Startzeitpunkt, den letzten gültigen Abtastzeitpunkt vor dem Ende, den Endzeitpunkt und den ersten Abtastzeitpunkt danach. Der Abstand zwischen diesen Zeitpunkten muss aus der tatsächlichen Spur oder der fachlich erforderlichen Genauigkeit abgeleitet werden. Pauschal eine Millisekunde abzuziehen, ist kein Ersatz für das „vorherige Bild“. Gerade bei Material mit variabler Bildrate lassen sich nicht alle Bildgrenzen aus Gesamtdauer geteilt durch Bildanzahl ableiten.
Negative Zeitwerte sind nicht grundsätzlich ungültig: Sie können einen Versatz darstellen. Ob sie zulässig sind, muss die Schnittstelle festlegen, statt sie pauschal auf null zu begrenzen. Eine unbekannte Dauer darf dagegen nicht in null Sekunden umgewandelt und weiterverrechnet werden. Sonst werden noch nicht geladene Medien fälschlich als leere Segmente eingestuft.
In Regressionstests einbinden, ohne die Wiedergabeprüfung zu ersetzen
Legen Sie das Skript im Werkzeugverzeichnis des Repositorys ab und protokollieren Sie bei der Ausführung die Systemversion sowie xcodebuild -version, damit sich Fehler der jeweiligen Umgebung zuordnen lassen. Übernehmen Sie dieselben Testfälle anschließend in die Projekttests und rufen Sie dort direkt die Funktionen des Produktionscodes für Zeitumrechnung und Intervallzuordnung auf. Wer nur Framework-Funktionen prüft, entdeckt nicht, wenn die Anwendungslogik Zeitwerte erneut in Gleitkommazahlen umwandelt.
Prüfen Sie vor dem Commit: Numerische Gültigkeit und Epoche sind validiert; keine Dauer ist negativ; Umrechnungen der Zeitskala sind an klar definierten Eingangsstellen gebündelt; die Rundungsrichtung ist getestet; benachbarte Intervalle haben keine doppelt zugeordneten Zeitpunkte; ungültige, unendliche und unbestimmte Zeitwerte werden über den Fehlerpfad behandelt.
Kontrollieren Sie abschließend mit kurzem Testmaterial die vom Player angeforderte Position, die tatsächlich zurückgemeldete Position und das letztlich angezeigte Bild. Seek-Toleranzen, Keyframes und die zeitliche Planung der Decodierung beeinflussen die erreichte Position. Korrekte Arithmetik garantiert noch keine bildgenaue Ausgabe. Stabilisieren Sie zuerst die Zeitregeln und untersuchen Sie danach das Wiedergabeverhalten – so wird der Suchbereich deutlich kleiner.
Häufig gestellte Fragen
Verhindert eine Umstellung auf Millisekunden alle Zeitfehler?
Nein. Ein Bild bei 30 Bildern pro Sekunde dauert 1/30 Sekunde und lässt sich nicht exakt in ganzen Millisekunden darstellen. Behalte die Quellskala bei und prüfe Rundungsrichtung sowie Fehlertoleranz an jeder Umwandlung.
Muss dafür ein iOS-Simulator laufen?
CMTime-Berechnungen lassen sich mit xcrun swift und einem verfügbaren macOS SDK prüfen. Suchpositionen im Player, tatsächlich decodierte Bilder und die Synchronität von Ton und Bild brauchen separate Anwendungstests.
Ein eigener Mac für deinen Workflow
Builds, Tests und Experimente mit nativem macOS durchführen. Vergleiche zuerst Ausstattung und Abrechnungszeiträume und wähle dann den passenden physischen Mac.