SITE EN PRÉPARATION — LE SERVICE N’EST PAS ENCORE ACTIF. N’ACHETEZ PAS : AUCUN PRODUIT NE SERA LIVRÉ TANT QUE CE MESSAGE EST AFFICHÉ.Ouverture très bientôt.
#m3u8.wiki
Extrait en lecture libre · Partie 1 · Un film qui joue

HLS en dix minutes

Des fichiers texte et des morceaux de vidéo servis en HTTP ordinaire. Le master, la playlist de média, les segments — et les cinq tags qui décident de tout.

À la fintu lis un master et une playlist de média sans outil, et tu sais ce que le lecteur va en faire.

HLS (HTTP Live Streaming) est le format de tout ce qu'une plateforme de streaming lit — et de tout ce que tu vas lire dans ce guide. Il a gagné pour une raison simple : il ne demande rien de plus qu'un serveur de fichiers. Une vidéo HLS, c'est une playlist texte qui pointe vers d'autres playlists texte, qui pointent vers des morceaux de vidéo de quelques secondes. Un CDN sert tout ça comme il servirait des images.

Ce chapitre lit un vrai manifeste ligne par ligne. Le script manifeste.py, en bibliothèque standard, le fait pour toi ; on s'en servira à chaque nouvelle source.

master.m3u81080p.m3u8#EXT-X-STREAM-INF480p.m3u8#EXT-X-STREAM-INFfr.m3u8#EXT-X-MEDIA audiofr.vtt.m3u8#EXT-X-MEDIA subsseg 0…3… #EXTINF par segment, #EXT-X-ENDLIST à la fin.ts (MPEG-TS, octet 0x47)ou .m4s (MP4 fragmenté, #EXT-X-MAP)
Une vidéo HLS : un master, des playlists, des segments.

Le master

La première ligne d'une playlist HLS est toujours #EXTM3U. Si elle n'y est pas, ce que tu regardes n'est pas une playlist — le plus souvent une page d'erreur HTML servie en 200. Voici le début d'un master public, celui des exemples d'Apple :

#EXTM3U
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="eng",NAME="BipBop Audio 1",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="eng",NAME="BipBop Audio 2",AUTOSELECT=NO,DEFAULT=NO,URI="alternate_audio_aac/prog_index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="fr",URI="subtitles/fra/prog_index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="fr",URI="subtitles/fra_forced/prog_index.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=263851,CODECS="mp4a.40.2, avc1.4d400d",RESOLUTION=416x234,AUDIO="bipbop_audio",SUBTITLES="subs"
gear1/prog_index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1927833,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=1920x1080,AUDIO="bipbop_audio",SUBTITLES="subs"
gear5/prog_index.m3u8

Deux sortes de lignes, et c'est tout ce qu'un master contient :

#EXT-X-STREAM-INF déclare une variante : une qualité. Ses attributs qui comptent sont BANDWIDTH (le débit de pointe, en bits par seconde), RESOLUTION, CODECS, et les références AUDIO et SUBTITLES vers des groupes de pistes. La ligne suivante est l'URI de sa playlist de média, relative au master.

#EXT-X-MEDIA déclare une piste : audio, sous-titres, parfois vidéo alternative. GROUP-ID la rattache aux variantes ; NAME est ce qu'on affiche ; LANGUAGE est un code de langue ; DEFAULT et AUTOSELECT disent au lecteur quoi choisir au démarrage ; FORCED=YES marque les sous-titres qu'on affiche même quand l'utilisateur n'en veut pas (les dialogues en langue étrangère). Une piste audio sans URI est celle qui est mélangée dans les segments vidéo ; une piste avec URI est servie à part.

Ce que ce master dit, si tu sais le lire : deux pistes audio anglaises dont une en fichiers séparés, des sous-titres dans quatre langues avec leurs versions forcées, et six qualités de 234p à 1080p. Le script te le résume :

$ python3 manifeste.py https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8
6 variante(s) vidéo :
  audio seul    41 kb/s  codecs=mp4a.40.2  audio=bipbop_audio
     416x234   264 kb/s  codecs=mp4a.40.2, avc1.4d400d  audio=bipbop_audio
     640x360   578 kb/s  codecs=mp4a.40.2, avc1.4d401e  audio=bipbop_audio
     960x540   911 kb/s  codecs=mp4a.40.2, avc1.4d401f  audio=bipbop_audio
    1280x720   1.0 Mb/s  codecs=mp4a.40.2, avc1.4d401f  audio=bipbop_audio
   1920x1080   1.9 Mb/s  codecs=mp4a.40.2, avc1.4d401f  audio=bipbop_audio
10 piste(s) déclarée(s) par #EXT-X-MEDIA :
      AUDIO  groupe=bipbop_audio  nom='BipBop Audio 1'  langue=eng  défaut=YES
      AUDIO  groupe=bipbop_audio  nom='BipBop Audio 2'  langue=eng  défaut=NO
  SUBTITLES  groupe=subs  nom='Français'  langue=fr  défaut=NO
  …
Variante la plus lourde : 181 segments, 30.0 min, VOD (#EXT-X-ENDLIST), en clair

Beaucoup de sources que tu croiseras n'ont aucune ligne #EXT-X-MEDIA : l'audio est mélangé dans chaque variante, et il n'y a qu'une langue. D'autres déclarent deux pistes fra avec le même NAME — deux doublages, indistinguables dans le manifeste. Le chapitre 4.1 s'en occupe. Pour l'instant, retiens que les pistes réelles sont celles du manifeste, pas celles que tu supposes.

La playlist de média

Suis l'URI d'une variante et tu tombes sur la liste des segments :

#EXTM3U
#EXT-X-TARGETDURATION:11
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:9.9766,
#EXT-X-BYTERANGE:326744@0
main.ts
#EXTINF:9.9766,
#EXT-X-BYTERANGE:335268@326744
main.ts
…
#EXT-X-ENDLIST

Trois tags de plus, que tu verras tôt ou tard :

Les segments

Un segment est un fichier de quelques secondes de vidéo et d'audio. Deux conteneurs existent :

MPEG-TS (.ts) : l'historique. Des paquets de 188 octets qui commencent tous par l'octet 0x47. C'est ce que servent la majorité des sources. Robuste, mais les navigateurs ne le lisent pas nativement — sauf Safari.

MP4 fragmenté (.m4s, .mp4) : le moderne. Un segment d'initialisation puis des fragments. C'est ce que le navigateur sait lire via Media Source Extensions, dans tous les cas.

Les URL relatives

Presque toutes les URI d'un manifeste sont relatives : gear1/prog_index.m3u8, main.ts, url_462/…. Le lecteur les résout par rapport à l'URL de la playlist qui les contient — pas par rapport au master, pas par rapport à la page. Deux conséquences pratiques :

  1. Si un serveur répond avec une redirection (302) vers un autre domaine, c'est l'URL finale qui sert de base, pas celle demandée. Un lecteur ou un téléchargeur qui garde l'URL d'origine résout les segments au mauvais endroit et n'obtient que des 404.
  2. Si tu réécris un manifeste (tu ne devrais pas — mais si), tu dois réécrire toutes les URI, y compris celles cachées dans les attributs URI="…" des tags #EXT-X-MEDIA, #EXT-X-KEY et #EXT-X-MAP. Un manifeste réécrit à moitié donne une vidéo qui démarre et un changement de langue qui échoue.

C'est un chapitre sur soixante-cinq. Le guide complet — douze parties, le code de référence, le carnet des intégrations, l'aide sur le frontend, les mises à jour — coûte 50 €, une fois, accès à vie.

Acheter — 50 €   Retour à la présentation