O ExoPlayer é comumente usado para streaming de mídia pela Internet. Ele é compatível com várias pilhas de rede para fazer as solicitações de rede. A escolha da pilha de rede pode ter um impacto significativo no desempenho do streaming.
Esta página descreve como configurar o ExoPlayer para usar a pilha de rede que você quiser, lista as opções disponíveis, fornece algumas orientações sobre como escolher uma pilha de rede para o app e explica como ativar o armazenamento em cache para mídia streaming.
Como configurar o ExoPlayer para usar uma pilha de rede específica
O ExoPlayer carrega dados usando componentes DataSource
, que são extraídos de
instâncias de DataSource.Factory
que são injetadas no código do app.
Se o app só precisa reproduzir conteúdo http(s), selecionar uma pilha
de rede é tão simples quanto atualizar todas as instâncias de DataSource.Factory
que seu
app injeta como instâncias do HttpDataSource.Factory
que correspondem à pilha de rede que você quer usar. Se o app também
precisar reproduzir conteúdo 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 à
pilha de rede preferencial. A camada DefaultDataSource.Factory
adiciona suporte
a origens que não são http(s), como arquivos locais.
O exemplo abaixo mostra como criar uma ExoPlayer
que usará a pilha de rede
da Cronet e também oferecer suporte à 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 à Cronet, OkHttp e à pilha de rede integrada do Android. O ExoPlayer também pode ser estendido para oferecer suporte a qualquer outra pilha de rede que funcione no Android.
Cronet
A Cronet é a pilha de rede do Chromium disponibilizada para os apps Android como uma biblioteca. Ela aproveita várias tecnologias que reduzem a latência e aumentam a capacidade das solicitações de rede que o app precisa para funcionar, incluindo as feitas pelo ExoPlayer. Ele oferece suporte nativo a HTTP, HTTP/2 e HTTP/3 em vez de protocolos QUIC. A Cronet é usada por alguns dos maiores apps de streaming do mundo, incluindo o YouTube.
O ExoPlayer oferece suporte à Cronet usando a
biblioteca Cronet (link em inglês).
Consulte o README.md
da biblioteca para instruções detalhadas sobre como
usá-lo. A biblioteca da Cronet pode usar três implementações da Cronet
subjacentes:
- Google Play Services:recomendamos o uso dessa implementação na maioria dos
casos. Caso o Google Play Services não esteja disponível, use a pilha de rede integrada do Android
(
DefaultHttpDataSource
). - 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.
- Cronet Fallback:a implementação substituta da Cronet implementa
a API da Cronet como um wrapper na pilha de rede integrada do Android. Ela não
pode ser usada com o ExoPlayer, porque usar a pilha de rede integrada do Android
diretamente (usando
DefaultHttpDataSource
) é mais eficiente.
OkHttp
O OkHttp é outra pilha de rede moderna muito usada por muitos apps Android conhecidos. Ele aceita HTTP e HTTP/2, mas ainda não aceita HTTP/3 em vez de QUIC.
O ExoPlayer oferece suporte ao OkHttp usando a
biblioteca OkHttp (link em inglês).
Consulte o README.md
da biblioteca para instruções detalhadas sobre como
usá-lo. Quando a biblioteca OkHttp é usada, a pilha de rede é incorporada ao
app. Ela é semelhante à Cronet Embedded, mas o OkHttp é significativamente
menor, adicionando menos de 1 MB ao app.
A pilha de rede integrada do Android
O ExoPlayer oferece suporte ao 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 subjacente. Na maioria dos dispositivos (a partir de 2021), apenas HTTP é compatível, ou seja, HTTP/2 e HTTP/3 via QUIC não são aceitos.
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. O OkHttpDataSourceContractTest
na biblioteca OkHttp é um
bom exemplo de como fazer isso.
Como escolher uma pilha de rede
A tabela abaixo descreve os prós e contras das pilhas de rede com suporte do ExoPlayer.
Pilha de rede | Protocolos | Impacto no tamanho do APK | Observações |
---|---|---|---|
Cronet (Google Play Services) | HTTP HTTP/2 HTTP/3 sobre QUIC |
Pequeno (<100 KB) |
Requer o Google Play Services. Versão da Cronet atualizada automaticamente |
Cronet (incorporada) | HTTP HTTP/2 HTTP/3 sobre QUIC |
Grande (aproximadamente 8 MB) |
Versão da 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) |
Exige o ambiente de execução do Kotlin |
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 em vez de QUIC podem melhorar significativamente o desempenho do streaming de mídia. Em particular, ao transmitir mídia adaptativa que é distribuída usando uma rede de distribuição de conteúdo (CDN), há casos em que o uso desses protocolos pode permitir que as CDNs operem de maneira muito mais eficiente. Por esse motivo, a compatibilidade da Cronet com HTTP/2 e HTTP/3 via QUIC (e a compatibilidade do OkHttp com HTTP/2) é um grande benefício em comparação ao uso da pilha de rede integrada do Android, desde que os servidores em que o conteúdo está hospedado também sejam compatíveis com esses protocolos.
Ao considerar o streaming de mídia isoladamente, recomendamos o uso da Cronet
fornecida pelo Google Play Services, voltando para DefaultHttpDataSource
se
o Google Play Services não estiver disponível. Essa recomendação atinge um bom equilíbrio entre permitir o uso de HTTP/2 e HTTP/3 em vez de QUIC na maioria dos dispositivos e evitar 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 parcela significativa dos dispositivos que vão executar seu app,
o uso da Cronet Embedded ou OkHttp pode ser mais apropriado. O uso da pilha
de rede integrada poderá ser aceitável se o tamanho do APK for uma preocupação crítica ou se o streaming
de mídia for apenas uma pequena parte da funcionalidade do app.
Além de mídia, normalmente é uma boa ideia escolher uma única pilha de rede para toda a rede realizada 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 vai precisar executar redes 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 isolada, os requisitos de outros componentes que executam rede e a importância relativa para o app.
Como armazenar mídias em cache
O ExoPlayer oferece suporte ao armazenamento em cache de bytes carregados no disco para evitar carregar repetidamente os mesmos bytes da rede. Isso é útil ao buscar novamente na mídia atual ou ao 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();