Introducción a HTTP Live Streaming: HLS en Android y más
Publicado: 2022-03-11La transmisión de video es una parte integral de la experiencia moderna de Internet. Está en todas partes: en teléfonos móviles, computadoras de escritorio, televisores e incluso dispositivos portátiles. Debe funcionar perfectamente en todos los dispositivos y tipos de red, ya sea en conexiones móviles lentas, WiFi, detrás de firewalls, etc. HTTP Live Streaming (HLS) de Apple se creó exactamente con estos desafíos en mente.
Casi todos los dispositivos modernos vienen equipados con hardware moderno que es lo suficientemente rápido para reproducir video, por lo que la velocidad y la confiabilidad de la red surgen como el mayor problema. ¿Porqué es eso? Hasta hace unos años, la forma canónica de almacenar y publicar videos eran protocolos basados en UDP como RTP. Esto resultó problemático en muchos sentidos, para enumerar solo algunos:
- Necesita un servicio de servidor (daemon) para transmitir contenido.
- La mayoría de los cortafuegos están configurados para permitir solo puertos estándar y tipos de tráfico de red, como http, correo electrónico, etc.
- Si su audiencia es global, necesita una copia de su servicio de daemon de transmisión que se ejecuta en todas las regiones principales.
Por supuesto, usted puede pensar que todos estos problemas son fáciles de resolver. Simplemente almacene archivos de video (por ejemplo, archivos mp4) en su servidor http y use su servicio CDN favorito para servirlos en cualquier parte del mundo.
Donde la transmisión de video heredada se queda corta
Esta está lejos de ser la mejor solución por varias razones, siendo la eficiencia una de ellas. Si almacena archivos de video originales en resolución completa, los usuarios en áreas rurales o partes del mundo con poca conectividad tendrán dificultades para disfrutarlos. Sus reproductores de video tendrán dificultades para descargar suficientes datos para reproducirlos en tiempo de ejecución.
Por lo tanto, necesita una versión especial del archivo para que la cantidad de video descargado sea aproximadamente la misma que se puede reproducir. Por ejemplo, si la resolución y la calidad del video son tales que en cinco segundos puede descargar otros cinco segundos de video, eso es óptimo. Sin embargo, si tarda cinco segundos en descargar solo tres segundos de video, el reproductor se detendrá y esperará a que se descargue la siguiente parte de la transmisión.
Por otro lado, reducir aún más la calidad y la resolución solo degradaría la experiencia del usuario en conexiones más rápidas, ya que estaría ahorrando ancho de banda innecesariamente. Sin embargo, hay una tercera vía.
Transmisión de tasa de bits adaptativa
Si bien puede cargar diferentes versiones de video para diferentes usuarios, luego debe tener la capacidad de controlar sus reproductores y calcular cuál es la mejor transmisión para su conexión y dispositivo. Luego, el jugador necesita cambiar entre ellos (por ejemplo, cuando un usuario cambia de 3G a WiFi). E incluso entonces, ¿qué pasa si el cliente cambia el tipo de red? Luego, el reproductor debe cambiar a un video diferente, pero debe comenzar a reproducirse no desde el principio, sino en algún lugar en el medio del video. Entonces, ¿cómo se calcula el rango de bytes para solicitar?
Sería genial si los reproductores de video pudieran detectar cambios en el tipo de red y el ancho de banda disponible, y luego cambiar de forma transparente entre diferentes transmisiones (del mismo video preparado para diferentes velocidades) hasta encontrar la mejor.
Eso es exactamente lo que resuelve la transmisión de tasa de bits adaptativa.
Nota: este tutorial de HLS no cubrirá el cifrado, las reproducciones sincronizadas ni IMSC1.
¿Qué es HLS?
HTTP Live Streaming es un protocolo de transmisión de tasa de bits adaptable introducido por Apple en 2009. Utiliza archivos m3u8 para describir transmisiones multimedia y utiliza HTTP para la comunicación entre el servidor y el cliente. Es el protocolo de transmisión de medios predeterminado para todos los dispositivos iOS, pero se puede usar en Android y navegadores web.
Los componentes básicos de un flujo HLS son:
- Listas de reproducción de M3U8
- Archivos multimedia para varios flujos
Listas de reproducción de M3U8
Comencemos respondiendo una pregunta básica: ¿Qué son los archivos M3U8 ?
M3U (o M3U8) es un formato de archivo de texto sin formato creado originalmente para organizar colecciones de archivos MP3. El formato se amplía para HLS, donde se usa para definir flujos de medios. En HLS hay dos tipos de archivos m3u8:
- Lista de reproducción multimedia: contiene las URL de los archivos necesarios para la transmisión (es decir, fragmentos del video original que se reproducirá).
- Lista de reproducción maestra: contiene direcciones URL de listas de reproducción de medios que, a su vez, contienen variantes del mismo video preparadas para diferentes anchos de banda.
Una llamada URL de transmisión en vivo de M3U8 no es más que URL de archivos M3U8, como: https://s3-us-west-2.amazonaws.com/hls-playground/hls.m3u8.
Muestra de archivo M3U8 para HLS Stream
Un archivo M3U8 contiene una lista de direcciones URL o rutas de archivos locales con algunos metadatos adicionales. Las líneas de metadatos comienzan con #.
Este ejemplo ilustra cómo se ve un archivo M3U8 para una transmisión HLS simple:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:11 #EXTINF:5.215111, 00000.ts #EXTINF:10.344822, 00001.ts #EXTINF:10.344822, 00002.ts #EXTINF:9.310344, 00003.ts #EXTINF:10.344822, 00004.ts ... #EXT-X-ENDLIST
- Las primeras cuatro líneas son metadatos globales (encabezado) para esta lista de reproducción M3U8.
- La
EXT-X-VERSION
es la versión del formato M3U8 (debe haber al menos 3 si queremos usar entradasEXTINF
). - La etiqueta
EXT-X-TARGETDURATION
contiene la duración máxima de cada “fragmento” de video. Por lo general, este valor es de alrededor de 10 s. - El resto del documento contiene pares de líneas como:
#EXTINF:10.344822, 00001.ts
Este es un "fragmento" de video. Este representa el fragmento 00001.ts
que tiene exactamente 10,344822 segundos de duración. Cuando un reproductor de video cliente necesita iniciar un video desde un punto determinado de dicho video, puede calcular fácilmente qué archivo .ts
necesita solicitar al sumar las duraciones de los fragmentos vistos anteriormente. La segunda línea puede ser un nombre de archivo local o una URL para ese archivo.
El archivo M3U8 con sus archivos .ts
representa la forma más simple de una transmisión HLS: una lista de reproducción multimedia.
Tenga en cuenta que no todos los navegadores pueden reproducir transmisiones HLS de forma predeterminada.
Lista de reproducción maestra o archivo de índice M3U8
El ejemplo anterior de M3U8 apunta a una serie de fragmentos .ts
. Se crean a partir del archivo de vídeo original, cuyo tamaño se codifica y se divide en fragmentos.
Eso significa que todavía tenemos el problema descrito en la introducción: ¿qué pasa con los clientes en redes muy lentas (o inusualmente rápidas)? ¿O clientes en redes rápidas con tamaños de pantalla muy pequeños? No tiene sentido transmitir un archivo en máxima resolución si no se puede mostrar en todo su esplendor en su teléfono nuevo y reluciente.
HLS resuelve este problema al introducir otra "capa" de M3U8. Este archivo M3U8 no contendrá punteros a archivos .ts
, pero tiene punteros a otros archivos M3U8 que, a su vez, contienen archivos de video preparados de antemano para velocidades de bits y resoluciones específicas.
Aquí hay un ejemplo de un archivo M3U8 de este tipo:
#EXTM3U #EXT-X-STREAM-INF:BANDWIDTH=1296,RESOLUTION=640x360 https://.../640x360_1200.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=264,RESOLUTION=416x234 https://.../416x234_200.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=464,RESOLUTION=480x270 https://.../480x270_400.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1628,RESOLUTION=960x540 https://.../960x540_1500.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2628,RESOLUTION=1280x720 https://.../1280x720_2500.m3u8
El reproductor de video elegirá pares de líneas como:
#EXT-X-STREAM-INF:BANDWIDTH=1296,RESOLUTION=640x360 https://.../640x360_1200.m3u8
Se denominan variantes de un mismo vídeo preparado para distintas velocidades de red y resoluciones de pantalla. Este archivo M3U8 específico ( 640x360_1200.m3u8
) contiene fragmentos del archivo de video redimensionado a 640x360 píxeles y preparado para velocidades de bits de 1296 kbps . Tenga en cuenta que la tasa de bits notificada debe tener en cuenta las secuencias de video y audio en el video.
El reproductor de video generalmente comenzará a reproducir desde la primera variante de transmisión (en el ejemplo anterior, esto es 640x360_1200.m3u8). Por eso, debes tener especial cuidado para decidir qué variante será la primera de la lista. El orden de las otras variantes no es importante.
Si el primer archivo .ts tarda demasiado en descargarse (causando "almacenamiento en búfer", es decir, esperando el siguiente fragmento), el reproductor de video cambiará a una transmisión con una tasa de bits más pequeña. Y, por supuesto, si se carga lo suficientemente rápido, significa que puede cambiar a una variante de mejor calidad, pero solo si tiene sentido para la resolución de la pantalla.
Si el primer flujo en la lista del índice M3U8 no es el mejor, el cliente necesitará uno o dos ciclos hasta que se asiente con la variante correcta.
Entonces, ahora tenemos tres capas de HLS:
- Índice del archivo M3U8 (la lista de reproducción maestra) que contiene punteros (URL) a variantes.
- Archivos variantes M3U8 (la lista de reproducción de medios) para diferentes transmisiones para diferentes tamaños de pantalla y velocidades de red. Contienen punteros (URL) a archivos .ts.
- Archivos
.ts
(fragmentos) que son archivos binarios con partes del video.
Puede ver un archivo de índice M3U8 de ejemplo aquí (nuevamente, depende de su navegador/SO).
A veces, sabe de antemano que el cliente está en una red lenta o rápida. En ese caso, puede ayudar al cliente a elegir la variante correcta proporcionando un archivo de índice M3U8 con una primera variante diferente. Hay dos maneras de hacer esto.
- El primero es tener varios archivos de índice preparados para diferentes tipos de red y preparar al cliente con anticipación para solicitar el correcto. El cliente deberá verificar el tipo de red y luego solicitar, por ejemplo
http://.../index_wifi.m3u8
ohttp://.../index_mobile.m3u8
. - También puede asegurarse de que el cliente envíe el tipo de red como parte de la solicitud http (por ejemplo, si está conectado a un wifi o móvil 2G/3G/...) y luego tener el archivo de índice M3U8 preparado dinámicamente para cada solicitud. Solo el archivo de índice M3U8 necesita una versión dinámica, las secuencias individuales (variantes de archivos M3U8) aún se pueden almacenar como archivos estáticos.
Preparación de archivos de video para HLS
Hay dos bloques de construcción importantes de HTTP Live Streaming de Apple. Una es la forma en que se almacenan los archivos de video (para que se sirvan a través de HTTP más tarde) y la otra son los archivos de índice M3U8 que le indican al reproductor (la aplicación de cliente de transmisión) dónde obtener qué archivo de video.
Comencemos con los archivos de video. El protocolo HLS espera que los archivos de video se almacenen en fragmentos más pequeños de igual duración, generalmente de 10 segundos cada uno. Originalmente, esos archivos debían almacenarse en archivos MPEG-2 TS ( .ts
) y codificarse con el formato H.264 con audio en MP3, HE-AAC o AC-3.
Eso significa que un video de 30 segundos se dividirá en 3 archivos .ts
más pequeños, cada uno de aproximadamente 10 segundos de duración.
Tenga en cuenta que la última versión de HLS también permite archivos .mp4 fragmentados. Dado que todavía es algo nuevo, y algunos reproductores de video aún necesitan implementarlo, los ejemplos de este artículo usarán archivos .ts
.

Fotogramas clave
Los fragmentos deben codificarse con un fotograma clave al comienzo de cada archivo. Cada video contiene marcos. Los cuadros son imágenes, pero los formatos de video no almacenan imágenes completas, eso ocuparía demasiado espacio en el disco. Codifican solo la diferencia con el cuadro anterior. Cuando salta a un punto medio en el video, el reproductor necesita un "punto de partida" desde donde aplicar todas esas diferencias para mostrar la imagen inicial y luego comenzar a reproducir el video.
Es por eso que los fragmentos .ts
deben tener un fotograma clave al principio. A veces, los jugadores deben comenzar en el medio del trozo. El jugador siempre puede calcular la imagen actual agregando todas las "diferencias" del primer fotograma clave. Pero, si comienza 9 segundos desde el principio, necesita calcular 9 segundos de "diferencias". Para hacer ese cálculo más rápido, es mejor crear fotogramas clave cada pocos segundos (mejor cca 3s).
Puntos de quiebre HLS
Hay situaciones en las que desea que se reproduzcan varios clips de vídeo en sucesión. Una forma de hacerlo es fusionar los archivos de video originales y luego crear las transmisiones HLS con ese archivo, pero eso es problemático por varias razones. ¿Qué sucede si desea mostrar un anuncio antes o después de su video? Tal vez no quiera hacer eso para todos los usuarios, y probablemente desee diferentes anuncios para diferentes usuarios. Y, por supuesto, no desea preparar archivos HLS con diferentes anuncios por adelantado.
Para solucionar ese problema, hay una etiqueta #EXT-X-DISCONTINUITY
que se puede usar en la lista de reproducción m3u8. Esta línea básicamente le dice al reproductor de video que se prepare con anticipación para el hecho de que a partir de este momento, los archivos .ts
pueden crearse con una configuración diferente (por ejemplo, la resolución puede cambiar). El jugador deberá volver a calcular todo y posiblemente cambiar a otra variante y debe estar preparado para tales puntos de "discontinuidad".
Transmisión en vivo con HLS
Básicamente, hay dos tipos de "transmisión de video". Uno es Video On Demand ( VOD ) para videos grabados con anticipación y transmitidos al usuario cuando así lo decida. Y hay transmisión en vivo . Aunque HLS es una abreviatura de HTTP Live Streaming, todo lo explicado hasta ahora se ha centrado en VOD, pero también hay una manera de hacer streaming en vivo con HLS.
Hay algunos cambios en sus archivos M3U8. Primero, debe haber una etiqueta #EXT-X-MEDIA-SEQUENCE:1
en el archivo de la variante M3U8. Entonces, el archivo M3U8 no debe terminar con #EXT-X-ENDLIST
(que de lo contrario siempre debe colocarse al final).
Mientras graba su transmisión, constantemente tendrá nuevos archivos .ts
. Debe agregarlos en la lista de reproducción M3U8 y cada vez que agregue uno nuevo, el contador en #EXT-X-MEDIA-SEQUENCE:<counter>
debe aumentarse en 1.
El reproductor de video verificará el contador. Si se cambió desde la última vez, sabe si hay nuevos fragmentos para descargar y reproducir. Asegúrese de que el archivo M3U8 se sirva con los encabezados sin caché, porque los clientes seguirán recargando archivos M3U8 esperando que se reproduzcan nuevos fragmentos.
VTT
Otra característica interesante para las transmisiones HLS es que puede incrustar archivos de pista de texto de video web (VTT) en ellas. Los archivos VTT se pueden utilizar para varios usos. Por ejemplo, para un reproductor web HLS, puede especificar instantáneas de imágenes para varias partes del video. Cuando el usuario mueve el mouse sobre el área del temporizador de video (debajo del reproductor de video), el reproductor puede mostrar instantáneas desde esa posición en el video.
Otro uso obvio de los archivos VTT son los subtítulos. La secuencia HLS puede especificar varios subtítulos para varios idiomas:
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-,NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/eng/prog_index.m3u8"
Entonces, theprog_index.m3u8
se ve así:
#EXTM3U #EXT-X-TARGETDURATION:30 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:30, 0000.webvtt #EXTINF:30, 0001.webvtt ...
El VTT real (por ejemplo 0000.webvtt
):
WEBVTT X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000 00:00:01.000 --> 00:00:03.000 Subtitle -Unforced- (00:00:01.000) 00:00:03.000 --> 00:00:05.000 <i>...text here... -Unforced- (00:00:03.000)</i> <i>...text here...</i>
Además de los archivos VTT, Apple anunció recientemente que HLS contará con soporte para IMSC1, un nuevo formato de subtítulos optimizado para la entrega de transmisión. Su ventaja más importante es que se puede diseñar con CSS.
Herramientas de transmisión en vivo HTTP y posibles problemas
Apple presentó una serie de útiles herramientas de HSL, que se describen con más detalle en la guía oficial de HLS.
- Para transmisiones en vivo, Apple preparó una herramienta llamada
mediastreamsegmenter
para crear archivos de segmentos sobre la marcha a partir de una transmisión de video en curso. - Otra herramienta importante es
mediastreamvalidator
. Verificará sus listas de reproducción de M3U8, descargará los archivos de video e informará varios problemas. Por ejemplo, cuando la tasa de bits notificada no es la misma que la calculada a partir de los archivos .ts. - Por supuesto, cuando debe codificar/decodificar/mux/demux/chunk/strip/merge/join/… archivos de video/audio, existe ffmpeg. Esté preparado para compilar su(s) propia(s) versión(es) personalizada(s) de ffmpeg para casos de uso específicos.
Uno de los problemas más frecuentes encontrados en video es la sincronización de audio. Si encuentra que el audio en algunas de sus transmisiones HLS no está sincronizado con el video (es decir, un actor abre la boca, pero nota que la voz se adelanta o retrasa unos milisegundos), es posible que el archivo de video original haya sido filmado. utilizando una velocidad de fotogramas variable. Asegúrese de convertirlo a tasa de bits constante.
Si es posible, es aún mejor asegurarse de que su software esté configurado para grabar video a una velocidad de cuadros constante.
Ejemplo de transmisión en vivo HTTP
Preparé una aplicación HLS para Android que transmite un HLS predefinido usando el reproductor ExoPlayer de Google. Mostrará un video y una lista de "eventos" de HLS debajo. Esos eventos incluyen: cada archivo .ts
descargado, o cada vez que el jugador decide cambiar a una transmisión de tasa de bits más alta o más baja.
Repasemos las partes principales de la inicialización del visor. En el primer paso, recuperaremos el tipo de conexión actual del dispositivo y usaremos esa información para decidir qué archivo m3u8
recuperar.
String m3u8File = "hls.m3u8"; ConnectivityManager connectivity = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = connectivity.getActiveNetworkInfo(); if (activeNetwork != null && activeNetwork.isConnectedOrConnecting()) { int type = activeNetwork.getType(); int subType = activeNetwork.getSubtype(); if (type == ConnectivityManager.TYPE_MOBILE && subType == TelephonyManager.NETWORK_TYPE_GPRS) { m3u8File = "hls_gprs.m3u8"; } } String m3u8URL = "https://s3-us-west-2.amazonaws.com/hls-playground/" + m3u8File;
Tenga en cuenta que esto no es estrictamente necesario. El reproductor HLS siempre se ajustará a la variante HLS correcta después de unos pocos fragmentos, pero eso significa que en los primeros 5 a 20 segundos, es posible que el usuario no vea la variante ideal de la transmisión.
Recuerde, la primera variante en el archivo m3u8
es con la que comenzará el espectador. Como estamos en el lado del cliente y podemos detectar el tipo de conexión, al menos podemos intentar evitar que el jugador inicial cambie entre variantes solicitando el archivo m3u8
que está preparado de antemano para este tipo de conexión.
En el siguiente paso, inicializamos e iniciamos nuestro reproductor HLS:
Handler mainHandler = new Handler(); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder() .setEventListener(mainHandler, bandwidthMeterEventListener) .build(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); LoadControl loadControl = new DefaultLoadControl(); SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
Luego preparamos el reproductor y lo alimentamos con el m3u8 correcto para este tipo de conexión de red:
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "example-hls-app"), bandwidthMeter); HlsMediaSource videoSource = new HlsMediaSource(Uri.parse(m3u8URL), dataSourceFactory, 5, mainHandler, eventListener); player.prepare(videoSource);
Y aqui esta el resultado:
Compatibilidad del navegador HLS, desarrollos futuros
Hay un requisito de Apple para las aplicaciones de transmisión de video en iOS de que deben usar HLS si los videos duran más de 10 minutos o más de 5 MB. Eso en sí mismo es una garantía de que HLS llegó para quedarse. Hubo algunas preocupaciones sobre HLS y MPEG-DASH y cuál será el ganador en el campo de los navegadores web. HLS no está implementado en todos los navegadores modernos (probablemente lo notó si hizo clic en los ejemplos de URL m3u8 anteriores). En Android, por ejemplo, en versiones inferiores a la 4.0 no funcionará en absoluto. De 4.1 a 4.4 funciona solo parcialmente (por ejemplo, falta el audio o falta el video pero el audio funciona).
Pero esta “batalla” se volvió un poco más simple recientemente. Apple anunció que el nuevo protocolo HLS permitirá archivos mp4 fragmentados ( fMP4
). Anteriormente, si quería tener compatibilidad con HLS y MPEG-DASH, tenía que codificar sus videos dos veces. Ahora, podrá reutilizar los mismos archivos de video y volver a empaquetar solo los archivos de metadatos ( .m3u8
para HLS y .mpd
para MPEG-DASH).
Otro anuncio reciente es la compatibilidad con el códec de video de alta eficiencia (HEVC). Si se usa, debe empaquetarse en archivos mp4 fragmentados. Y eso probablemente significa que el futuro de HLS es fMP4
.
La situación actual en el mundo de los navegadores es que solo algunas implementaciones de navegador de la etiqueta <video>
reproducirán HLS de fábrica. Pero existen soluciones comerciales y de código abierto que ofrecen compatibilidad con HLS. La mayoría de ellos ofrecen HLS al tener un respaldo de Flash, pero hay algunas implementaciones completamente escritas en JavaScript.
Terminando
Este artículo se centra específicamente en HTTP Live Streaming, pero conceptualmente también se puede leer como una explicación de cómo funciona Adaptive Bitrate Streaming (ABS). En conclusión, podemos decir que HLS es una tecnología que resuelve numerosos problemas importantes en la transmisión de video:
- Simplifica el almacenamiento de archivos de video.
- CDN
- Reproductores de clientes que manejan diferentes anchos de banda de clientes y cambian entre flujos
- Subtítulos, encriptación, reproducciones sincronizadas y otras características que no se tratan en este artículo
Independientemente de si termina usando HLS o MPEG-DASH, ambos protocolos deberían ofrecer funcionalidades similares y, con la introducción de mp4 fragmentado (fMP4) en HLS, puede usar los mismos archivos de video. Eso significa que, en la mayoría de los casos, deberá comprender los conceptos básicos de ambos protocolos. Afortunadamente, parecen moverse en la misma dirección, lo que debería facilitar su dominio.