Pilhas de rede

O ExoPlayer é usado com frequência para streaming de mídia pela Internet. Ele oferece suporte múltiplas pilhas de rede para fazer as solicitações de rede subjacentes. Sua escolha de pilha de rede pode ter um impacto significativo no desempenho do streaming.

Esta página descreve como configurar o ExoPlayer para usar sua pilha de rede de escolha, lista as opções disponíveis, fornece orientações sobre como escolher uma pilha de rede para seu aplicativo e explica como ativar o armazenamento em cache para mídia.

Como configurar o ExoPlayer para usar uma pilha de rede específica

O ExoPlayer carrega dados usando componentes DataSource, que são obtidos de instâncias DataSource.Factory injetadas pelo código do app.

Se o app só precisa reproduzir conteúdo http(s), selecionar uma pilha de rede é tão simples quanto atualizar qualquer instância DataSource.Factory que o app injeta para ser uma instância do HttpDataSource.Factory que corresponde à pilha de rede que você quer usar. Se o app também precisa reproduzir conteúdo que não é http(s), como arquivos locais, use DefaultDataSource.Factory:

Kotlin

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

Java

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

Neste exemplo, PreferredHttpDataSource.Factory é a fábrica correspondente à sua pilha de rede preferida. A camada DefaultDataSource.Factory adiciona suporte para fontes não HTTP(s), como arquivos locais.

O exemplo abaixo mostra como criar uma ExoPlayer que usará a Cronet pilha de rede e também suporta a reprodução de conteúdo não HTTP(s).

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();

Pilhas de rede compatíveis

O ExoPlayer oferece suporte direto para HttpEngine, Cronet, OkHttp e a pilha de rede padrão integrada do Android. O ExoPlayer também pode ser estendido para oferecer suporte a qualquer outra pilha de rede que funcione no Android.

HttpEngine

A HttpEngine é a pilha de rede padrão recomendada no Android a partir da API 34 (ou extensões S7). Na maioria dos casos, ele usa a pilha de rede da Cronet internamente, com suporte a HTTP, HTTP/2 e HTTP/3 sobre protocolos QUIC.

O ExoPlayer oferece suporte ao HttpEngine com a HttpEngineDataSource.Factory dele. Você pode injetar essa fábrica de fonte de dados, conforme descrito em Como configurar o ExoPlayer para usar um pilha de rede específica.

Cronet

A Cronet é a pilha de rede do Chromium disponibilizada para apps Android como uma biblioteca. A Cronet usa várias tecnologias para reduzir a latência e aumentar a capacidade de processamento das solicitações de rede necessárias para o app funcionar, incluindo as feitas pelo ExoPlayer. Ele oferece suporte nativo aos protocolos HTTP, HTTP/2 e HTTP/3 sobre QUIC. O Cronet é usado por alguns dos maiores apps de streaming do mundo, incluindo o YouTube.

O ExoPlayer oferece suporte à Cronet pela Biblioteca Cronet. Consulte o README.md da biblioteca para ver instruções detalhadas sobre como usá-la. A biblioteca da Cronet pode usar três elementos implementações:

  1. Google Play Services: recomendamos usar essa implementação na maioria dos casos e retornar à pilha de rede integrada do Android (DefaultHttpDataSource) se o Google Play Services não estiver disponível.
  2. Cronet incorporada:pode ser uma boa opção se uma grande porcentagem dos seus usuários estão em mercados onde o Google Play Services não está amplamente disponível ou se você querem controlar a versão exata da implementação da Cronet que está sendo usada. O A principal desvantagem do Cronet Embedded é que ele adiciona aproximadamente 8 MB seu app.
  3. Cronet Fallback:a implementação de substituto da Cronet implementa A API da Cronet como um wrapper na pilha de rede integrada do Android. Ele não deve ser usado com o ExoPlayer, já que usar a pilha de rede integrada do Android diretamente (usando DefaultHttpDataSource) é mais eficiente.

OkHttp

A OkHttp é outra pilha de rede moderna que é amplamente usado por muitos apps Android conhecidos. Ele oferece suporte a HTTP e HTTP/2, mas ainda não é compatível com HTTP/3 sobre QUIC.

O ExoPlayer oferece suporte ao OkHttp pela biblioteca OkHttp. Consulte o README.md da biblioteca para ver instruções detalhadas sobre como usá-la. Ao usar a biblioteca OkHttp, a pilha de rede é incorporada ao app. É semelhante à Cronet Embedded, mas o OkHttp é significativamente menor, adicionando menos de 1 MB ao seu aplicativo.

Pilha de rede integrada do Android

O ExoPlayer oferece suporte à pilha de rede integrada do Android com DefaultHttpDataSource e DefaultHttpDataSource.Factory, que fazem parte biblioteca principal do ExoPlayer.

A implementação exata da pilha de rede depende do software em execução no do dispositivo. Na maioria dos dispositivos, só há suporte para HTTP (ou seja, HTTP/2 e HTTP/3 sobre QUIC não são aceitos).

Outras pilhas de rede

Os apps também podem integrar outras pilhas de rede com o ExoPlayer. Para fazer isso, implemente um HttpDataSource que envolva a pilha de rede. com um HttpDataSource.Factory correspondente. As bibliotecas Cronet e OkHttp do ExoPlayer são bons exemplos de como fazer isso.

Ao fazer a integração com uma pilha de rede Java pura, é uma boa ideia implementar uma DataSourceContractTest para verificar se a implementação de HttpDataSource se comporta corretamente. Na biblioteca OkHttp, o OkHttpDataSourceContractTest é uma um bom exemplo de como fazer isso.

Como escolher uma pilha de rede

A tabela a seguir descreve os prós e contras das pilhas de rede com suporte o ExoPlayer.

Pilha de rede Protocolos Impacto no tamanho do APK Observações
Mecanismo HTTP HTTP
HTTP/2
HTTP/3 por QUIC
Nenhum Disponível apenas na API 34 ou nas extensões S7
Cronet (Google Play Services) HTTP
HTTP/2
HTTP/3 por QUIC
Pequeno
(<100KB)
É necessário ter o Google Play Services. Versão da Cronet atualizada automaticamente
Cronet (incorporada) HTTP
HTTP/2
HTTP/3 por QUIC
Grande
(aproximadamente 8 MB)
Versão do Cronet controlada pelo desenvolvedor do app
Cronet (substituto) HTTP
(varia de acordo com o dispositivo)
Pequeno
(<100 KB)
Não recomendado para ExoPlayer
OkHttp HTTP
HTTP/2
Pequeno
(<1 MB)
Pilha de rede integrada HTTP
(varia de acordo com o dispositivo)
Nenhum A implementação varia de acordo com o dispositivo

Os protocolos HTTP/2 e HTTP/3 sobre QUIC podem melhorar significativamente o desempenho do streaming de mídia. Especificamente, quando o streaming de mídia adaptativa é distribuído usando uma rede de distribuição de conteúdo (CDN), há casos em que o uso desses protocolos permite que as CDNs operem de maneira muito mais eficiente. Por esse motivo, o suporte do HttpEngine e do Cronet para HTTP/2 e HTTP/3 com QUIC (e o suporte do OkHttp para HTTP/2) é um benefício importante em comparação com o uso da pilha de rede integrada do Android, desde que os servidores em que o conteúdo é hospedado também ofereçam suporte a esses protocolos.

Ao considerar o streaming de mídia isoladamente, recomendamos o uso do HttpEngine ou do Cronet fornecido pelo Google Play Services, que retorna para DefaultHttpDataSource se o Google Play Services estiver indisponível. Essa recomendação é uma boa equilibrar a ativação do uso de HTTP/2 e HTTP/3 com o QUIC na maioria dos dispositivos evitando um aumento significativo no tamanho do APK. Há exceções recomendação. Nos casos em que o Google Play Services provavelmente não estará disponível em uma parte significativa dos dispositivos que vão executar seu app, o uso do Cronet Embedded ou do OkHttp pode ser mais apropriado. O uso da pilha de rede integrada pode ser aceitável se o tamanho do APK for uma preocupação crítica ou se o streaming de mídia for apenas uma parte menor da funcionalidade do app.

Além da mídia, normalmente é bom escolher uma pilha de rede para toda a rede realizada pelo seu app. Isso permite que os recursos (como soquetes) para agrupar e compartilhar com eficiência entre o ExoPlayer e outros componentes do app.

Como o app provavelmente precisará realizar conexões de rede não relacionadas à reprodução de mídia, sua escolha de pilha de rede precisa considerar nossas recomendações acima para streaming de mídia isoladamente, os requisitos de qualquer outro componente que realize conexões de rede e a importância relativa deles para o app.

Armazenamento em cache de mídia

O ExoPlayer oferece suporte ao armazenamento em cache de bytes carregados no disco para evitar o carregamento repetido dos mesmos bytes da rede. Isso é útil ao procurar na mídia atual ou repetir o mesmo item.

O armazenamento em cache requer uma instância SimpleCache que aponte para um diretório de cache dedicado e um 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();