O que tem dentro de um manifesto HLS

14 de julho de 2026 · 4 min de leitura

  • hls
  • video-streaming
  • frontend
  • webdev

Antes de um único frame de vídeo adaptativo chegar à tela, o player baixa um arquivinho de texto: o manifesto. Tudo o que ele faz (qual qualidade escolher, onde estão os segmentos, se é ao vivo ou sob demanda) sai da leitura desse arquivo. No HLS, ele é um playlist .m3u8, e depois que você aprende a ler um, muito "por que o vídeo está fazendo isso?" deixa de ser mistério.

São dois tipos, e o player os lê em ordem.

O master playlist: a escada de qualidades

O primeiro arquivo que o player busca é o master (ou multivariant). Ele não tem vídeo nenhum, só ponteiros pra outros playlists, um por degrau de qualidade:

#EXTM3U #EXT-X-VERSION:3 #EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=720x480,CODECS="avc1.42c01e,mp4a.40.2" 480p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2560000,RESOLUTION=1280x720,CODECS="avc1.4d401f,mp4a.40.2" 720p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=6000000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2" 1080p.m3u8

Cada linha #EXT-X-STREAM-INF é um degrau da escada de bitrate adaptativo (ABR). BANDWIDTH é o número que a lógica de ABR observa: o player estima sua banda e pega o degrau mais alto que aguenta sem travar. CODECS deixa ele pular degraus que o aparelho não decodifica; RESOLUTION deixa ele evitar degraus maiores que a tela em que está desenhando.

Esse é o cardápio inteiro, e o player ainda não tocou um byte de vídeo.

O media playlist: os segmentos

Escolha um degrau e você recebe um media playlist, a lista de fato dos pedaços:

#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:6 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:6.000, segment0.ts #EXTINF:6.000, segment1.ts #EXT-X-ENDLIST

#EXTINF é a duração de cada segmento; #EXT-X-TARGETDURATION é o teto pra qualquer um deles. O player baixa os segmentos em ordem, joga num buffer e reproduz.

Trocar de qualidade é só buscar o próximo segmento de outro media playlist: mesma timeline, outro degrau. É por isso que o ABR muda a resolução no meio da reprodução sem um reload visível.

Isso só funciona porque cada degrau é cortado nas mesmas fronteiras de segmento, com um keyframe no início de cada segmento, então o player troca de degrau em qualquer fronteira sem reiniciar a reprodução. Erre esse alinhamento na hora de codificar e as trocas travam ou piscam. É a parte sem glamour que decide se o ABR parece fluido.

Ao vivo vs. sob demanda: uma linha

A diferença entre um filme e um canal ao vivo é quase uma tag só. #EXT-X-ENDLIST diz "acabaram os segmentos", ou seja, sob demanda. Tire ela, e o playlist é ao vivo: o player re-busca ele a cada poucos segundos pra descobrir segmentos novos conforme são codificados, usando #EXT-X-MEDIA-SEQUENCE pra saber onde parou.

#EXT-X-DISCONTINUITY marca uma quebra na timeline (uma inserção de anúncio, um splice) pra o decoder resetar limpo em vez de dar glitch.

Esse é o núcleo: uma escada de qualidades, uma lista de segmentos por qualidade, e um par de tags que dizem ao-vivo-ou-não e onde estão as emendas, tudo em texto simples que você dá curl e lê.

Deixei de fora o que torna isso interessante em escala: segmentos fMP4/CMAF, faixas alternativas de áudio e legenda via #EXT-X-MEDIA, requisições por byte-range, e o Low-Latency HLS. O formato acima é a espinha por baixo de tudo.

Experimente

Você não precisa de player, porque o manifesto é só texto. Dois curl percorrem o caminho inteiro que um player faz. (No Windows, rode no WSL ou use curl.exe; o alias curl do PowerShell é outra ferramenta.)

1. Busque o master playlist, a escada de qualidades:

curl -s https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8

Esperado: um #EXT-X-STREAM-INF por degrau, cada um seguido da URL do playlist daquele degrau.

#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=1280x720,NAME="720" url_0/193039199_mp4_h264_aac_hd_7.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=246440,CODECS="mp4a.40.5,avc1.42000d",RESOLUTION=320x184,NAME="240" url_2/193039199_mp4_h264_aac_ld_7.m3u8 ...

Repare que a escada não vem ordenada. A ordem no master não significa nada, porque o player escolhe por BANDWIDTH.

2. Busque o media playlist de um degrau, a lista de segmentos. Resolva a URL relativa contra o diretório do próprio master (…/x36xhzz/), não a raiz do host:

curl -s https://test-streams.mux.dev/x36xhzz/url_0/193039199_mp4_h264_aac_hd_7.m3u8

Esperado: pares #EXTINF + .ts, terminando em #EXT-X-ENDLIST porque este stream é VOD (tire essa tag e ele seria ao vivo).

#EXTM3U #EXT-X-VERSION:3 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-TARGETDURATION:11 #EXTINF:10.000, url_462/193039199_mp4_h264_aac_hd_7.ts #EXTINF:10.000, url_463/193039199_mp4_h264_aac_hd_7.ts ... #EXT-X-ENDLIST

3. Veja um player escolher entre eles. Mantenho um sandbox hls.js em /pt/lab no mesmo stream. Abra com a aba Network aberta: você vê o master ser buscado primeiro, depois um media playlist, depois as requisições de segmento (.ts). Limite a banda na aba Network (ex.: Slow 3G) e você vê também o degrau trocar conforme o ABR desce.

Escrito por Wilian Revejes, engenheiro front-end (OTT / Smart TV & vídeo). Mais em /pt/cv.

Este post descreve técnica geral e padrão da indústria. Não é baseado em, nem descreve, o sistema de nenhum empregador ou cliente específico.