Comment concevoir une échelle de transcodage qui ne rend pas fous les algorithmes ABR
Si vous avez déjà observé la lecture HLS en conditions réelles, vous avez vu le phénomène : le flux ne cesse de rebondir entre deux renditions. Haut, bas, haut, bas. Le spectateur voit un changement constant de qualité, parfois toutes les quelques secondes. C'est pire que de rester sur une rendition inférieure tout le temps. Au moins, un flux stable en 720p semble intentionnel. Un flux qui oscille entre 720p et 1080p toutes les 10 secondes semble cassé.
La cause racine est presque toujours la même : l'échelle d'encodage a des renditions trop proches en débit, ou l'écart de qualité entre les échelons ne justifie pas le saut en bande passante. L'algorithme ABR côté client n'arrive pas à se décider, parce que la différence entre « j'ai assez de bande passante pour ça » et « je n'ai pas assez » est infime.
Voyons comment résoudre ce problème correctement.
Le test de cohérence bits-par-pixel
Avant toute chose, vous devez comprendre ce que le bits-par-pixel (BPP) vous dit sur votre échelle. C'est la métrique la plus simple pour évaluer si un débit donné est réellement pertinent pour une résolution donnée.
La formule est simple :
BPP = bitrate / (width × height × framerate)
Par exemple, un flux 1920×1080 à 4500 kbps et 30 fps :
BPP = 4,500,000 / (1920 × 1080 × 30) = 0.072
Pourquoi est-ce important ? Parce que le BPP vous indique la densité de compression à chaque échelon. Si deux échelons adjacents de votre échelle ont des valeurs de BPP très similaires, le spectateur ne verra pas de différence de qualité significative — mais l'algorithme ABR essaiera quand même de basculer entre eux. C'est ainsi que vous vous retrouvez avec un comportement de ping-pong.
Une échelle bien conçue doit avoir une courbe de BPP descendante à mesure que la résolution augmente. Cela reflète une propriété réelle des codecs vidéo : ils sont plus efficaces à des résolutions supérieures. Il faut moins de bits par pixel en 1080p pour atteindre la même qualité perçue qu'en 480p. Si votre BPP est plat ou incohérent d'un échelon à l'autre, quelque chose ne va pas.
La « Règle du 0.70 » est une référence pratique ici. L'idée est que lorsque vous doublez le nombre de pixels (par exemple en passant de 720p à 1080p), vous devriez appliquer environ 0.70× le BPP de la résolution inférieure. C'est une heuristique, pas une loi, mais cela vous donne un moyen rapide de repérer les anomalies. Si vous tracez les valeurs de BPP de votre échelle et qu'un échelon se démarque nettement — trop haut ou trop bas par rapport à ses voisins — cet échelon posera des problèmes.
À retenir : ne choisissez pas simplement des débits qui semblent être de beaux chiffres ronds. Calculez le BPP pour chaque échelon et assurez-vous que la courbe est cohérente. Si deux échelons adjacents sont à 15-20 % l'un de l'autre en BPP, le spectateur ne les distinguera pas, mais l'heuristique ABR perdra du temps à basculer entre eux.
Espacement des débits : la règle du facteur 1.5×
Il n'existe pas de standard universel, mais une recommandation courante en ingénierie est de maintenir un ratio d'au moins 1.5× entre les échelons de débit adjacents. Certaines implémentations poussent ce ratio à 2×.
Pourquoi ? Parce que les algorithmes ABR utilisent l'estimation de bande passante pour décider quelle rendition choisir. L'estimation a un intervalle de confiance — elle n'est jamais exacte. Si deux renditions sont à 2,5 Mbps et 3,0 Mbps, le BWE peut facilement osciller au-dessus et en dessous de 3,0 Mbps sur une connexion légèrement variable, provoquant des basculements constants. Si au contraire le saut va de 2,0 Mbps à 4,0 Mbps, l'algorithme a besoin d'un changement de bande passante beaucoup plus significatif pour déclencher un basculement. Le résultat : une lecture plus stable.
Voici un exemple concret. Supposons que vous avez une échelle propre à quatre échelons :
| Résolution | Débit | BPP (30fps) | Ratio avec le précédent |
|---|---|---|---|
| 480×270 | 400 kbps | 0.103 | — |
| 960×540 | 2000 kbps | 0.129 | 5.0× |
| 1280×720 | 2800 kbps | 0.101 | 1.4× |
| 1920×1080 | 4500 kbps | 0.072 | 1.6× |
À première vue, ça semble raisonnable — quatre résolutions, des débits croissants. Mais regardez le saut 540p→720p : de 2000 kbps à 2800 kbps. C'est seulement un ratio de 1.4×. Sur toute connexion qui oscille autour de 2,5–3 Mbps (ce qui correspond à la plupart des connexions mobiles), le BWE franchira constamment ce seuil. Haut, bas, haut, bas. Le spectateur voit un changement de résolution tous les quelques segments.
Et voici l'autre problème : le BPP baisse en fait de 0,129 à 540p à 0,101 à 720p. Donc le spectateur obtient plus de pixels mais moins de données par pixel. Selon le contenu, la rendition 720p pourrait ne pas être significativement meilleure que la 540p — vous avez ajouté de la résolution mais perdu de la marge de compression. L'algorithme ABR bascule pour rien.
Une meilleure version de cette échelle augmenterait le débit du 720p et ajusterait le 540p à la baisse :
| Résolution | Débit | BPP (30fps) | Ratio avec le précédent |
|---|---|---|---|
| 480×270 | 400 kbps | 0.103 | — |
| 960×540 | 1500 kbps | 0.096 | 3.75× |
| 1280×720 | 3000 kbps | 0.109 | 2.0× |
| 1920×1080 | 5800 kbps | 0.093 | 1.93× |
Maintenant, le saut 540p→720p est un ratio net de 2×. Le BWE doit doubler avant que le lecteur n'envisage même de monter en qualité. Et le BPP augmente en fait de 0,096 à 0,109, ce qui signifie que l'échelon 720p offre à la fois plus de pixels et une meilleure qualité de compression — le spectateur voit une vraie amélioration. Le saut 720p→1080p à 1,93× est tout aussi solide, et le BPP ne baisse que légèrement à 0,093, ce qui correspond au gain d'efficacité attendu aux résolutions supérieures.
Vérification échelon par échelon : test de lecture mono-rendition
Voici quelque chose que je vois rarement les équipes faire, mais c'est essentiel : lire chaque rendition individuellement et la regarder en entier.
Cela semble évident, mais la plupart des gens ne testent l'ABR qu'en tant que flux multi-variant complet. Ils n'isolent jamais une seule rendition pour la lire de bout en bout. Quand vous le faites, vous détectez des problèmes que le comportement ABR masque :
- Une rendition qui buffeurise même à son propre débit déclaré (parce que la BANDWIDTH déclarée dans la playlist est trop basse par rapport au débit de crête réel)
- Une rendition où l'encodeur a eu du mal et a produit des artefacts visibles sur certaines scènes
- Une rendition où le framerate chute ou saccade parce que la combinaison résolution/débit est trop exigeante pour le décodeur de l'appareil cible
Pour tester cela, vous pouvez forcer la lecture mono-rendition de plusieurs façons :
Avec la page de démo hls.js : Chargez votre flux multi-variant, puis dans le menu déroulant de sélection de qualité, épinglez manuellement chaque niveau un par un. La démo hls.js sur hlsjs.video-dev.org/demo/ expose tous les niveaux de qualité et vous permet de contourner l'ABR. Lisez chacun pendant au moins quelques minutes de contenu représentatif. Surveillez les images perdues dans l'onglet « Buffer & Statistics ».
Avec AVPlayer sur les plateformes Apple : Utilisez preferredPeakBitRate et preferredMaximumResolution sur AVPlayerItem pour contraindre la lecture à une seule rendition. Ou encore plus simple : créez une playlist de test qui n'inclut qu'un seul variant.
Avec ffprobe ou mediainfo : Avant même de lire quoi que ce soit, vérifiez les statistiques de débit réel de chaque rendition. La valeur BANDWIDTH dans votre playlist master doit tenir compte du pic, pas seulement de la moyenne. Si votre encodage VBR présente des pointes de 40 % au-dessus de la moyenne, votre BANDWIDTH déclarée doit le refléter.
Si une seule rendition ne peut pas être lue sans à-coups à son propre débit déclaré, elle causera absolument des problèmes en mode ABR. L'algorithme ABR sélectionne cette rendition en pensant avoir assez de bande passante, puis rencontre un pic VBR, se bloque, redescend, récupère, sélectionne à nouveau cette rendition — ping-pong.
Durée des segments : l'horloge de commutation de l'ABR
Il y a un autre facteur que les gens ont tendance à négliger : la durée des segments contrôle directement la fréquence à laquelle l'algorithme ABR peut prendre une décision. Chaque frontière de segment est un point de commutation potentiel. Donc si vous utilisez des segments de 2 secondes, le lecteur peut réévaluer et basculer jusqu'à 30 fois par minute. Avec des segments de 6 secondes, ce nombre tombe à 10 fois par minute. Avec des segments de 10 secondes, seulement 6.
Cela compte beaucoup quand votre échelle a déjà un espacement de débits serré. Des segments courts combinés à des échelons de débit proches, c'est la pire combinaison — vous donnez à l'algorithme ABR un maximum d'opportunités pour effectuer des basculements marginaux que le spectateur n'a pas besoin de voir.
À l'inverse, des segments plus longs agissent comme un amortisseur naturel contre l'oscillation. Même si l'estimation de bande passante fluctue, le lecteur doit s'engager sur sa rendition actuelle pour toute la durée du segment qu'il vient de télécharger. Au moment où le prochain point de décision arrive, le BWE s'est peut-être stabilisé.
La spécification HLS n'impose pas de durée précise, mais les directives d'Apple recommandent une cible de 6 secondes. En pratique :
- Segments de 2 secondes : pertinents pour le live basse latence où le démarrage rapide et l'adaptation rapide sont essentiels. Mais il faut une échelle bien espacée pour éviter les basculements constants.
- Segments de 6 secondes : un bon choix par défaut pour la VOD et le live standard. Ils donnent à l'algorithme ABR suffisamment de temps pour construire une estimation de bande passante fiable entre les décisions.
- Segments de 10 secondes : très stables mais lents à s'adapter. Si la bande passante chute brutalement, le lecteur est bloqué à télécharger un segment à haut débit qu'il pourrait ne pas être en mesure de terminer à temps.
Il y a aussi une subtilité avec l'encodage VBR : la variance de débit au sein d'un segment augmente avec la durée du segment. Un segment de 10 secondes sur une transition de scène — passant d'un plan statique à une séquence d'action — peut avoir une fluctuation de débit massive en interne. Si la BANDWIDTH déclarée dans la playlist correspond à la moyenne globale mais pas aux pics par segment, l'algorithme ABR est pris au dépourvu. Les segments plus courts tendent à avoir des débits par segment plus cohérents, ce qui rend le BWE plus précis.
En résumé : si vous constatez de l'oscillation et que l'espacement de votre échelle semble correct, vérifiez la durée de vos segments. Passer de 4 secondes à 6 secondes pourrait suffire à calmer les choses.
Utiliser Network Link Conditioner pour simuler des conditions réelles
Tester l'ABR sur une connexion fibre stable à 100 Mbps ne sert à rien. Vous devez simuler des conditions réelles, et Network Link Conditioner d'Apple est le meilleur outil pour cela sur macOS.
Vous l'obtenez à partir du package Additional Tools for Xcode d'Apple : dans Xcode, allez dans Xcode > Open Developer Tool > More Developer Tools, ce qui vous amène à la page de téléchargements développeur Apple. Cherchez « Additional Tools for Xcode », téléchargez le DMG pour votre version de Xcode, ouvrez-le, et dans le dossier Hardware vous trouverez Network Link Conditioner.prefPane. Double-cliquez pour l'installer. Il apparaît ensuite comme un panneau dans Réglages Système (ou Préférences Système sur les anciennes versions de macOS). Il vous permet de définir des profils de bande passante avec un débit, une latence, un taux de perte de paquets et un délai DNS spécifiques.
Créez des profils personnalisés qui comptent :
- 4G médiocre : 8 Mbps descendant / 2 Mbps montant, 80ms RTT, 1 % de perte de paquets
- WiFi de mauvaise qualité : 3 Mbps descendant / 1 Mbps montant, 150ms RTT, 3 % de perte de paquets
- Transitoire : Commencez à 15 Mbps, basculez manuellement à 2 Mbps en cours de lecture
Ce dernier test est le plus révélateur. Si votre échelle est bien conçue, le lecteur devrait descendre en douceur vers une rendition inférieure en quelques secondes et y rester. S'il commence à osciller entre deux renditions, vos échelons sont trop proches à la frontière de bande passante.
Sur les appareils iOS, Network Link Conditioner est disponible via les réglages Développeur (activez-le dans Réglages > Développeur après avoir connecté l'appareil à Xcode).
L'objectif de ces tests n'est pas simplement « est-ce que ça buffeurise ? » — c'est « est-ce que le flux se stabilise sur une rendition et y reste ? ». Une bonne expérience ABR est celle où les basculements sont rares et décisifs. Le spectateur voit le changement de qualité une fois, puis ça se stabilise.
Utiliser AVMetrics d'Apple pour surveiller les basculements en production
À partir d'iOS 18, Apple a introduit l'API AVMetrics dans AVFoundation. C'est une avancée majeure pour surveiller le comportement ABR en conditions réelles.
Le type d'événement clé pour notre besoin est l'événement de changement de variant. Chaque fois qu'AVPlayer bascule entre des variants HLS, vous obtenez un événement métrique qui vous indique d'où il a basculé, vers quoi il a basculé, et les détails de la rendition média. Vous obtenez aussi des événements de blocage quand le lecteur rebuffeurise, et un événement de synthèse à la fin de la session avec les KPI globaux.
Voici le patron Swift :
let playerItem: AVPlayerItem = // your configured item
let switchMetrics = playerItem.metrics(
forType: AVMetricPlayerItemVariantSwitchEvent.self
)
let stallMetrics = playerItem.metrics(
forType: AVMetricPlayerItemStallEvent.self
)
for await (event, _) in switchMetrics.chronologicalMerge(with: stallMetrics) {
switch event {
case let switchEvent as AVMetricPlayerItemVariantSwitchEvent:
// Log: from variant, to variant, timestamp
await analytics.logSwitch(switchEvent)
case let stallEvent as AVMetricPlayerItemStallEvent:
// Log: stall duration, variant at time of stall
await analytics.logStall(stallEvent)
default:
break
}
}
Ce que vous devez rechercher dans vos analyses :
- Fréquence de basculement : Si la session moyenne compte plus de 3-4 basculements par minute, votre échelle a des problèmes. Les flux sains basculent peut-être une ou deux fois par session, généralement au démarrage.
- Schémas d'oscillation : Deux renditions qui alternent sans cesse — signe classique d'échelons trop proches.
- Blocages corrélés aux montées en qualité : Si les blocages surviennent juste après un basculement vers une rendition supérieure, vos déclarations BANDWIDTH dans la playlist sont trop basses.
Pour la WWDC 2025, Apple a étendu AVMetrics pour inclure les informations de rendition média dans les événements de changement de variant, facilitant ainsi l'identification exacte des pistes audio/vidéo/sous-titres actives lors du basculement.
Si vous n'êtes pas sur les plateformes Apple, des données similaires peuvent être collectées depuis hls.js via les événements LEVEL_SWITCHED et FRAG_BUFFERED. Les mêmes principes s'appliquent.
Expérimenter avec hls.js : ajuster le comportement ABR
La page de démo hls.js (hlsjs.video-dev.org/demo/) est le meilleur outil gratuit pour expérimenter le comportement de commutation ABR sur le web. Chargez votre flux HLS et utilisez les panneaux « Real-time metrics » et « Buffer & Statistics » pour observer ce qui se passe.
Paramètres clés de hls.js à expérimenter :
abrEwmaFastVoDetabrEwmaSlowVoD: Ces paramètres contrôlent la moyenne mobile pondérée exponentiellement (EWMA) pour l'estimation de bande passante. Des valeurs plus basses font réagir le lecteur plus vite aux changements de bande passante (basculement plus agressif). Des valeurs plus élevées le rendent plus conservateur (basculement plus lent, plus stable). Si vous constatez trop de basculements, essayez d'augmenter ces valeurs.abrBandWidthFactor(défaut : 0.95) etabrBandWidthUpFactor(défaut : 0.7) : Ce sont des marges de sécurité. Le lecteur sélectionne une rendition dont le débit est inférieur àestimatedBandwidth × factor. Le facteur de montée en qualité est délibérément plus bas — le lecteur est plus conservateur pour monter que pour descendre en qualité. Si votre échelle a un espacement serré, vous pourriez vouloir baisserabrBandWidthUpFactorà 0.6 pour réduire l'oscillation.abrMaxWithRealBitrate(défaut : false) : Quand activé, le contrôleur ABR utilise le débit réel mesuré des segments téléchargés plutôt que la BANDWIDTH déclarée dans la playlist. C'est particulièrement utile si vos débits déclarés sont inexacts.
Mais voici la clé : si vous devez fortement ajuster ces paramètres pour obtenir une lecture stable, votre échelle est probablement mal conçue. Ces réglages sont de l'ajustement fin. L'échelle d'encodage elle-même est le fondement. Une échelle bien espacée fonctionne bien avec les paramètres ABR par défaut sur n'importe quel lecteur.
Recommandations pratiques
Si je devais résumer tout cela en une checklist :
- Calculez le BPP pour chaque échelon de votre échelle. Assurez-vous qu'il diminue à mesure que la résolution augmente. Supprimez ou ajustez tout échelon dont le BPP est à moins de 15 % de son voisin.
- Maintenez un ratio de débit d'au moins 1.5× entre les échelons adjacents. Préférez 2× quand c'est possible, surtout dans la moitié basse de l'échelle où les fluctuations de bande passante ont le plus d'impact.
- Testez chaque rendition isolément. Forcez la lecture mono-variant et regardez du contenu représentatif de bout en bout. Si ça ne peut pas être lu sans à-coups seul, ça ne le sera pas non plus en ABR.
- Utilisez Network Link Conditioner pour simuler des baisses de bande passante. Le flux devrait se stabiliser rapidement sur une rendition et y rester.
- Vérifiez la durée de vos segments. Si vous êtes à 2–4 secondes et que vous constatez de l'oscillation, essayez 6 secondes. Des segments plus longs réduisent naturellement la fréquence de basculement en donnant au BWE plus de temps pour se stabiliser entre les décisions.
- Instrumentez votre lecteur. Utilisez AVMetrics sur Apple, les événements hls.js sur le web. Suivez la fréquence de basculement par session. Si les sessions comptent en moyenne plus qu'une poignée de basculements en dehors de la phase de démarrage, investiguez.
- Ne vous fiez pas uniquement à la BANDWIDTH déclarée. Utilisez
abrMaxWithRealBitratedans hls.js ou vérifiez avec ffprobe que vos pics VBR ne dépassent pas la valeur déclarée. - Moins d'échelons, c'est souvent mieux. Une échelle à 5 échelons avec des renditions bien espacées surpassera une échelle à 10 échelons dont la moitié sont trop proches. Le spectateur n'a pas besoin de 12 niveaux de qualité. Il en a besoin de 4 ou 5 qui semblent chacun significativement différent et qui se lisent de manière fiable.
L'objectif de l'ABR est d'être invisible. Le spectateur ne devrait pas remarquer qu'il fonctionne. S'il le remarque, quelque chose ne va pas — et neuf fois sur dix, c'est l'échelle.
Références :