Pilas de red

ExoPlayer se usa comúnmente para transmitir contenido multimedia por Internet. Admite varias pilas de red para realizar las solicitudes de red subyacentes. La pila de red elegida puede tener un impacto significativo en el rendimiento de la transmisión.

En esta página, se describe cómo configurar ExoPlayer para usar la pila de red que prefieras, se enumeran las opciones disponibles, se proporciona orientación sobre cómo elegir una pila de red para tu app y se explica cómo habilitar el almacenamiento en caché para contenido multimedia transmitido.

Configura ExoPlayer para usar una pila de red específica

ExoPlayer carga datos a través de componentes DataSource, que obtiene de instancias de DataSource.Factory que se insertan desde el código de la app.

Si tu app solo necesita reproducir contenido HTTP(S), seleccionar una pila de red es tan simple como actualizar cualquier instancia de DataSource.Factory que tu app inserte para que sean instancias de HttpDataSource.Factory que corresponden a la pila de red que quieres usar. Si la app también necesita reproducir contenido que no sea HTTP, como archivos locales, usa DefaultDataSource.Factory:

Kotlin

DefaultDataSource.Factory(
  ...
  /* baseDataSourceFactory= */ PreferredHttpDataSource.Factory(...))

Java

new DefaultDataSource.Factory(
    ...
    /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(...));

En este ejemplo, PreferredHttpDataSource.Factory es la fábrica correspondiente a tu pila de red preferida. La capa DefaultDataSource.Factory agrega compatibilidad con fuentes que no son HTTP, como archivos locales.

En el siguiente ejemplo, se muestra cómo compilar un ExoPlayer que usará la pila de red de Cronet y también admitirá la reproducción de contenido que no sea HTTP.

Kotlin

// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
val cronetDataSourceFactory = CronetDataSource.Factory(cronetEngine, executor)

// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (such as files, resources,
// etc).
val dataSourceFactory =
  DefaultDataSource.Factory(context, /* baseDataSourceFactory= */ cronetDataSourceFactory)

// Inject the DefaultDataSource.Factory when creating the player.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
CronetDataSource.Factory cronetDataSourceFactory =
    new CronetDataSource.Factory(cronetEngine, executor);

// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (such as files, resources,
// etc).
DefaultDataSource.Factory dataSourceFactory =
    new DefaultDataSource.Factory(
        context, /* baseDataSourceFactory= */ cronetDataSourceFactory);

// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

Pilas de red compatibles

ExoPlayer proporciona compatibilidad directa con Cronet, OkHttp y la pila de red integrada de Android. ExoPlayer también se puede extender para admitir cualquier otra pila de red que funcione en Android.

Cronet

Cronet es la pila de red de Chromium que está disponible para las apps para Android como una biblioteca. Cronet aprovecha las múltiples tecnologías que reducen la latencia y aumentan la capacidad de procesamiento de las solicitudes de red que tu app necesita para funcionar, incluidas las que realiza ExoPlayer. Es compatible de forma nativa con los protocolos HTTP, HTTP/2 y HTTP/3 en los protocolos QUIC. Algunas de las apps de streaming más grandes del mundo, como YouTube, usan Cronet.

ExoPlayer admite Cronet a través de su biblioteca de Cronet. Consulta el archivo README.md de la biblioteca para obtener instrucciones detalladas sobre su uso. Ten en cuenta que la biblioteca de Cronet puede usar tres implementaciones subyacentes de Cronet:

  1. Servicios de Google Play: Te recomendamos que uses esta implementación en la mayoría de los casos y recurras a la pila de red integrada de Android (DefaultHttpDataSource) si los Servicios de Google Play no están disponibles.
  2. Cronet Embedded: Puede ser una buena opción si un gran porcentaje de tus usuarios se encuentra en mercados en los que los Servicios de Google Play no están ampliamente disponibles o si quieres controlar la versión exacta de la implementación de Cronet que se utiliza. La mayor desventaja de Cronet Embedded es que agrega aproximadamente 8 MB a la app.
  3. Resguardo de Cronet: La implementación de resguardo de Cronet implementa la API de Cronet como un wrapper en la pila de red integrada de Android. No debe usarse con ExoPlayer, ya que usar directamente la pila de red integrada de Android (mediante DefaultHttpDataSource) es más eficiente.

OkHttp

OkHttp es otra pila de red moderna que usan muchas apps para Android populares. Admite HTTP y HTTP/2, pero aún no es compatible con HTTP/3 en QUIC.

ExoPlayer admite OkHttp a través de su biblioteca de OkHttp. Consulta el archivo README.md de la biblioteca para obtener instrucciones detalladas sobre su uso. Cuando se usa la biblioteca de OkHttp, la pila de red se incorpora dentro de la app. Esto es similar a Cronet Embedded, pero OkHttp es mucho más pequeño, ya que agrega menos de 1 MB a tu app.

Pila de red integrada de Android

ExoPlayer admite el uso de la pila de red integrada de Android con DefaultHttpDataSource y DefaultHttpDataSource.Factory, que son parte de la biblioteca principal de ExoPlayer.

La implementación exacta de la pila de red depende del software que se ejecuta en el dispositivo subyacente. En la mayoría de los dispositivos (a partir de 2021), solo se admite HTTP (es decir, no se admiten HTTP/2 y HTTP/3 a través de QUIC).

Otras pilas de red

Las apps también pueden integrar otras pilas de red con ExoPlayer. Para ello, implementa un HttpDataSource que una la pila de red junto con un HttpDataSource.Factory correspondiente. Las bibliotecas Cronet y OkHttp de ExoPlayer son buenos ejemplos de cómo hacerlo.

Cuando realizas una integración con una pila de red de Java pura, se recomienda implementar un DataSourceContractTest para verificar que tu implementación de HttpDataSource se comporte correctamente. OkHttpDataSourceContractTest, en la biblioteca de OkHttp, es un buen ejemplo de cómo hacerlo.

Elige una pila de red

En la siguiente tabla, se describen las ventajas y desventajas de las pilas de red compatibles con ExoPlayer.

Pila de red Protocolos Impacto del tamaño del APK Notas
Cronet (Servicios de Google Play) HTTP
HTTP/2
HTTP/3 a través de QUIC
Pequeño
(<100 KB)
Requiere los Servicios de Google Play. La versión de Cronet se actualiza automáticamente
Cronet (incorporado) HTTP
HTTP/2
HTTP/3 a través de QUIC
Grande
(~8 MB)
El desarrollador de la app controla la versión de Cronet
Cronet (resguardo) HTTP
(varía según el dispositivo)
Pequeño
(<100 KB)
No se recomienda para ExoPlayer
OkHttp HTTP
HTTP/2
Pequeño
(<1 MB)
Requiere el tiempo de ejecución de Kotlin.
Pila de red integrada HTTP
(varía según el dispositivo)
Ninguno La implementación varía según el dispositivo

Los protocolos HTTP/2 y HTTP/3 sobre QUIC pueden mejorar significativamente el rendimiento de la transmisión de medios. En particular, cuando se transmite contenido multimedia adaptable que se distribuye a través de una red de distribución de contenido (CDN), hay casos en los que el uso de estos protocolos puede permitir que las CDN funcionen de manera mucho más eficiente. Por este motivo, la compatibilidad de Cronet con HTTP/2 y HTTP/3 sobre QUIC (y la compatibilidad de OkHttp con HTTP/2) es un gran beneficio en comparación con el uso de la pila de red integrada de Android, siempre que los servidores en los que se aloja el contenido también admitan estos protocolos.

Cuando consideres la transmisión de contenido multimedia de forma aislada, recomendamos el uso de Cronet que proporcionan los Servicios de Google Play y recurrir a DefaultHttpDataSource si los Servicios de Google Play no están disponibles. Esta recomendación logra un buen equilibrio entre habilitar el uso de HTTP/2 y HTTP/3 a través de QUIC en la mayoría de los dispositivos y evitar un aumento significativo en el tamaño del APK. Hay excepciones para esta recomendación. Para los casos en los que es probable que los Servicios de Google Play no estén disponibles en una fracción significativa de los dispositivos en los que se ejecutará tu app, puede ser más adecuado usar OkHttp o Cronet Embedded. El uso de la pila de red integrada puede ser aceptable si el tamaño del APK es una preocupación importante o si la transmisión de contenido multimedia es solo una pequeña parte de la funcionalidad de tu app.

Más allá de solo el contenido multimedia, suele ser una buena idea elegir una sola pila de red para toda la red que realiza tu app. Esto permite que los recursos (como los sockets) se reúnan y se compartan de manera eficiente entre ExoPlayer y otros componentes de la app.

Debido a que lo más probable es que tu app necesite realizar conexiones de red no relacionadas con la reproducción de contenido multimedia, la pila de red que elijas, en última instancia, debería tener en cuenta nuestras recomendaciones anteriores sobre la transmisión de contenido multimedia de forma aislada, los requisitos de cualquier otro componente que realice redes y su importancia relativa para tu app.

Almacenamiento en caché de contenido multimedia

ExoPlayer admite el almacenamiento en caché de bytes cargados en el disco para evitar que se carguen repetidamente los mismos bytes desde la red. Esto es útil cuando retrocedes en el contenido multimedia actual o cuando se repite el mismo elemento.

El almacenamiento en caché requiere una instancia de SimpleCache que apunte a un directorio de caché dedicado y un CacheDataSource.Factory:

Kotlin

// Note: This should be a singleton in your app.
val databaseProvider = StandaloneDatabaseProvider(context)

// An on-the-fly cache should evict media when reaching a maximum disk space limit.
val cache =
    SimpleCache(
        downloadDirectory, LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider)

// Configure the DataSource.Factory with the cache and factory for the desired HTTP stack.
val cacheDataSourceFactory =
    CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory)

// Inject the DefaultDataSource.Factory when creating the player.
val player =
    ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory))
        .build()

Java

// Note: This should be a singleton in your app.
DatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context);

// An on-the-fly cache should evict media when reaching a maximum disk space limit.
Cache cache =
    new SimpleCache(
        downloadDirectory, new LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider);

// Configure the DataSource.Factory with the cache and factory for the desired HTTP stack.
DataSource.Factory cacheDataSourceFactory =
    new CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory);

// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory))
        .build();