Pilhas de rede

O ExoPlayer é usado com frequência para fazer streaming de mídia pela Internet. Ele é compatível com várias pilhas de rede para fazer as solicitações de rede subjacentes. A escolha da pilha de rede pode ter um impacto significativo no desempenho do streaming.

Nesta página, descrevemos como configurar o ExoPlayer para usar a pilha de rede escolhida, listamos as opções disponíveis, fornecemos algumas orientações sobre como escolher uma pilha de rede para seu app e explicamos como ativar o armazenamento em cache para mídia transmitida.

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 do código do app.

Se o app só precisa reproduzir conteúdo http(s), selecionar uma pilha de rede é tão simples quanto atualizar as instâncias de DataSource.Factory injetadas pelo app para que sejam instâncias do HttpDataSource.Factory que corresponde à pilha de rede que você quer usar. Se o app também precisar reproduzir conteúdo que não seja HTTP(S), como arquivos locais, use DefaultDataSource.Factory:

Kotlin

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

Java

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

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

O exemplo a seguir mostra como criar um ExoPlayer que vai usar a pilha de rede do Cronet e também oferecer suporte à reprodução de conteúdo que não seja 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

HttpEngine é a pilha de rede padrão recomendada no Android a partir da API 34 (ou S extensions 7). Na maioria dos casos, ela usa a pilha de rede do Cronet internamente, com suporte aos protocolos HTTP, HTTP/2 e HTTP/3 sobre QUIC.

O ExoPlayer oferece suporte a HttpEngine com o HttpEngineDataSource.Factory. É possível inserir essa fábrica de fontes de dados conforme descrito em Como configurar o ExoPlayer para usar uma pilha de rede específica.

Cronet

A Cronet é a pilha de rede do Chromium disponibilizada para os apps Android como uma biblioteca. Ela usa várias tecnologias para diminuir 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 é compatível nativamente com os 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 é compatível com a Cronet pela biblioteca Cronet (link em inglês). Consulte o README.md da biblioteca para ver instruções detalhadas sobre como usá-la. A biblioteca Cronet pode usar três implementações subjacentes:

  1. Google Play Services:recomendamos usar essa implementação na maioria dos casos e voltar para a pilha de rede integrada do Android (DefaultHttpDataSource) se o Google Play Services não estiver disponível.
  2. Cronet Embedded:pode ser uma boa opção se uma grande porcentagem dos seus usuários estiver em mercados onde o Google Play Services não está amplamente disponível ou se você quiser controlar a versão exata da implementação da Cronet que está sendo usada. A principal desvantagem da Cronet Embedded é que ela adiciona aproximadamente 8 MB ao app.
  3. Cronet Fallback:a implementação de substituição da Cronet implementa a API dela como um wrapper em torno da 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 (com DefaultHttpDataSource) é mais eficiente.

OkHttp

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

O ExoPlayer oferece suporte ao OkHttp por meio da 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. Isso é semelhante ao Cronet Embedded, mas o OkHttp é significativamente menor, adicionando menos de 1 MB ao app.

Pilha de rede integrada do Android

O ExoPlayer é compatível com o uso da pilha de rede integrada do Android com DefaultHttpDataSource e DefaultHttpDataSource.Factory, que fazem parte da biblioteca principal do ExoPlayer.

A implementação exata da pilha de rede depende do software em execução no dispositivo. Na maioria dos dispositivos, apenas o HTTP é compatível. Ou seja, HTTP/2 e HTTP/3 sobre QUIC não são compatíveis.

Outras pilhas de rede

Os apps também podem integrar outras pilhas de rede ao 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, é recomendável implementar um DataSourceContractTest para verificar se a implementação de HttpDataSource se comporta corretamente. OkHttpDataSourceContractTest na biblioteca OkHttp é 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 compatíveis com o ExoPlayer.

Pilha de rede Protocolos Impacto no tamanho do APK Observações
HttpEngine HTTP
HTTP/2
HTTP/3 sobre QUIC
Nenhum Disponível apenas na API 34 ou nas extensões S 7
Cronet (Google Play Services) HTTP
HTTP/2
HTTP/3 sobre QUIC
Pequeno
(<100 KB)
Requer o Google Play Services. Versão do Cronet atualizada automaticamente
Cronet (incorporada) HTTP
HTTP/2
HTTP/3 sobre QUIC
Grande
(~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 o 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 de streaming de mídia. Em particular, ao fazer streaming de mídia adaptativa distribuída usando uma rede de distribuição de conteúdo (CDN, na sigla em inglês), há casos em que o uso desses protocolos pode permitir que as CDNs operem com muito mais eficiência. Por esse motivo, o suporte do HttpEngine e do Cronet para HTTP/2 e HTTP/3 sobre QUIC (e o suporte do OkHttp para HTTP/2) é um grande benefício em comparação com o uso da pilha de rede integrada do Android, desde que os servidores em que o conteúdo está hospedado também ofereçam suporte a esses protocolos.

Ao considerar o streaming de mídia isoladamente, recomendamos o uso do HttpEngine ou da Cronet fornecida pelo Google Play Services, voltando para DefaultHttpDataSource se o Google Play Services não estiver disponível. Essa recomendação equilibra o uso de HTTP/2 e HTTP/3 sobre QUIC na maioria dos dispositivos e evita um aumento significativo no tamanho do APK. Há exceções a essa recomendação. Para casos em que o Google Play Services provavelmente não estará disponível em uma fração significativa de dispositivos que vão executar seu app, o uso da 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 secundária da funcionalidade do app.

Além da mídia, normalmente é uma boa ideia escolher uma única pilha de rede para todas as redes realizadas pelo app. Isso permite que os recursos (como soquetes) sejam agrupados e compartilhados de forma eficiente entre o ExoPlayer e outros componentes do app.

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

Armazenamento de mídia em cache

O ExoPlayer é compatível com o armazenamento em cache de bytes carregados no disco para evitar o carregamento repetido dos mesmos bytes da rede. Isso é útil ao retroceder 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();