Tester une chronologie iOS : précision et bornes de CMTime

Tester une chronologie iOS : précision et bornes de CMTime

Tester une chronologie iOS : précision et bornes de CMTime

Un sous-titre clignote à la jonction de deux séquences, ou un découpage laisse passer une image de trop ? Ne modifiez pas tout de suite le lecteur. Reprenezz les instants problématiques sur un Mac cloud, vérifiez les calculs dans un script Swift autonome, puis revenez dans l’application pour contrôler le résultat du décodage. Vous pourrez ainsi distinguer une erreur de calcul temporel d’un positionnement imprécis à la lecture, sans téléverser à répétition l’intégralité du média.

Définir le contrat temporel avant de convertir en secondes

CMTime représente le temps par un numérateur entier, value, et une échelle de temps, timescale. Il porte aussi des informations de validité, un indicateur d’arrondi et un epoch. Une durée de 1/30 de seconde peut être représentée exactement, mais perd cette exactitude une fois convertie en millisecondes entières. Afficher seconds avec trois décimales relève uniquement de la présentation : cela ne permet pas de déterminer si deux temps sont égaux.

Commencez par consigner quatre règles dans le dépôt : les entrées doivent être des temps numériques ; les temps utilisés dans les calculs d’une même chronologie doivent partager le même epoch ; un segment est défini par un début et une durée ; sa borne de fin n’appartient pas au segment. Une durée ne doit pas être négative. Selon les règles métier, un segment de durée nulle sera refusé ou considéré comme vide.

Une échelle de temps n’est pas une fréquence d’images. La fréquence vidéo, la fréquence d’échantillonnage audio et l’unité temporelle d’une interface métier ne sont pas interchangeables sous prétexte qu’elles s’expriment toutes par des entiers.

Cet exemple se limite à une chronologie ordinaire dont l’epoch vaut zéro. Pour des données couvrant plusieurs cycles de lecture ou portant d’autres numéros d’époque, définissez d’abord une correspondance. Les reconstruire à partir du seul numérateur et du seul dénominateur ne suffit pas.

Exécuter une vérification minimale sur un Mac cloud

Préparez un environnement de ligne de commande Xcode fonctionnel, puis lancez xcrun --find swift et xcrun --sdk macosx --show-sdk-path pour vérifier que le compilateur et le SDK sont accessibles. Enregistrez le code ci-dessous dans timeline-check.swift. Il n’utilise que des frameworks système et ne nécessite aucun téléchargement de média.

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

Si toutes les vérifications réussissent, le script se termine sans rien afficher ; si l’une d’elles échoue, il renvoie un code de sortie non nul. Le pipeline doit exploiter directement ce code, plutôt que rechercher un mot dans les journaux pour décider de la réussite. Le code emploie des vérifications explicites, et non des appels à assert, qui peuvent être supprimés dans une compilation optimisée.

Limiter les pertes de précision aux frontières des interfaces

Une même valeur numérique peut avoir plusieurs représentations. Pour comparer deux temps, utilisez donc CMTimeCompare, et non leurs deux champs value. Pour les additionner, utilisez CMTimeAdd au lieu d’additionner directement les numérateurs. Un calcul entier ne peut être équivalent que si les échelles sont identiques, les époques concordantes et la politique de dépassement de capacité clairement définie.

Lors d’un changement d’échelle, précisez impérativement le mode d’arrondi. Déplacer le début d’un découpage vers un instant antérieur ou ultérieur peut déterminer si l’image située à la borne est conservée. Ne laissez pas le comportement par défaut trancher un choix produit. La conversion d’un tiers de seconde dans le script n’est qu’un cas de validation, pas une recommandation visant à exprimer tous les médias en millisecondes.

hasBeenRounded signale qu’un résultat porte la trace d’un arrondi, sans en donner l’erreur. Cet indicateur peut aussi être hérité d’un calcul en amont. Si vous devez respecter un budget d’erreur, conservez le temps d’origine, calculez l’écart avant et après conversion, puis comparez-le à la tolérance métier. Ne rejetez pas systématiquement un résultat parce que l’indicateur est présent, et ne sautez pas les contrôles d’entrée parce qu’il est absent.

Vérifier les intervalles avec des cas aux bornes

Construisez deux segments adjacents correspondant à [0, 1) et [1, 2). Un temps exactement égal à une seconde ne doit appartenir qu’au second. Le relais entre sous-titres, la sélection des segments et l’accrochage lors du glisser peuvent ainsi suivre une règle commune, sans activer les deux segments à la fois.

Pour chaque intervalle métier, vérifiez au minimum le début, le dernier point d’échantillonnage valide avant la fin, la fin et le premier point d’échantillonnage après la fin. L’espacement entre ces points doit provenir de la piste réelle ou de la précision métier. Soustraire systématiquement une milliseconde ne permet pas de désigner « l’image précédente ». Avec des médias à fréquence d’images variable en particulier, ne déduisez pas toutes les bornes en divisant la durée totale par le nombre d’images.

Un temps négatif n’est pas nécessairement invalide : il peut représenter un décalage. C’est à l’interface de décider s’il est autorisé, plutôt que de le ramener systématiquement à zéro. De même, une durée inconnue ne doit pas être convertie en zéro seconde pour poursuivre les calculs : un média pas encore chargé serait alors pris à tort pour un segment vide.

Intégrer les tests de non-régression sans remplacer la validation de lecture

Placez le script dans le répertoire d’outils du dépôt et consignez la version du système à l’exécution ainsi que la sortie de xcodebuild -version, afin de pouvoir rattacher un échec à son environnement. Reprenez ensuite les mêmes cas dans les tests du projet, en appelant directement les fonctions de conversion temporelle et d’appartenance aux intervalles du code de production. Tester uniquement les fonctions du framework ne révélera pas une reconversion en virgule flottante dans la couche métier.

Avant de valider les modifications, vérifiez les points suivants : les valeurs numériques et les époques sont contrôlées ; aucune durée n’est négative ; les changements d’échelle sont regroupés à des points d’entrée explicites ; le sens de l’arrondi est testé ; les intervalles adjacents n’ont aucune appartenance commune ; les temps invalides, infinis et indéfinis empruntent le chemin d’erreur.

Enfin, utilisez un média court pour comparer la position demandée au lecteur, la position effectivement renvoyée et l’image finalement affichée. La tolérance de recherche, les images clés et l’ordonnancement du décodage influencent tous le point atteint : des calculs corrects ne garantissent pas une image exacte. Stabilisez d’abord le contrat temporel, puis examinez le comportement de lecture. Le périmètre de diagnostic sera bien plus restreint.

Questions fréquentes

Tout convertir en millisecondes supprime-t-il les erreurs ?

Non. Une image à 30 images par seconde dure 1/30 de seconde, ce qui ne se représente pas exactement en millisecondes entières. Gardez l’échelle source et contrôlez chaque conversion.

Faut-il lancer un simulateur iOS ?

Les calculs CMTime se testent avec xcrun swift et un SDK macOS disponible. La recherche dans le média, le décodage et la synchronisation audio-vidéo nécessitent des tests applicatifs distincts.

Ajoutez un Mac dédié à votre workflow

Utilisez macOS natif pour compiler, tester et expérimenter. Comparez les configurations et les durées de facturation, puis choisissez le nœud physique qui vous convient.

Louer un Mac dans le cloud Voir les configurations

Confiez votre prochaine compilation à un Mac dans le cloud

Un ordinateur physique dédié, pas une machine virtuelle. Choisissez la configuration et la durée de location adaptées à votre projet.