O ExoPlayer é usado com frequência para streaming de mídia pela Internet. Ela oferece suporte a várias pilhas de rede para fazer as solicitações de rede subjacentes. A escolha da pilha de rede pode ter um impacto significativo na performance de streaming.
Nesta página, explicamos como configurar o ExoPlayer para usar a pilha de rede de sua escolha, 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.
Configurar o ExoPlayer para usar uma pilha de rede específica
O ExoPlayer carrega dados por componentes DataSource, que ele recebe de instâncias DataSource.Factory injetadas do código do app.
Se o app só precisar reproduzir conteúdo http(s), basta atualizar as instâncias DataSource.Factory que o app injeta para serem instâncias do HttpDataSource.Factory correspondente à 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( ... /* 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 que não sejam http(s), como arquivos locais.
O exemplo a seguir mostra como criar um ExoPlayer que usa a pilha de rede
do Cronet e também oferece 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 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 desde a API 34 (ou extensões
S 7). Na maioria dos casos, ele usa a pilha de rede do Cronet internamente,
com suporte para HTTP, HTTP/2 e HTTP/3 em protocolos QUIC.
O ExoPlayer é compatível com HttpEngine usando o HttpEngineDataSource.Factory. Você pode
injetar essa fábrica de fontes de dados conforme descrito em 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. 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 é compatível com a Cronet pela
biblioteca Cronet.
Consulte o README.md da biblioteca para instruções detalhadas sobre como usar
o recurso. A biblioteca Cronet pode usar três implementações
subjacentes do Cronet:
- Google Play Services:recomendamos usar essa implementação na maioria dos casos e voltar à pilha de rede integrada do Android (
DefaultHttpDataSource) se o Google Play Services não estiver disponível. - Cronet incorporada: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 do Cronet Embedded é que ele adiciona aproximadamente 8 MB ao app.
- Fallback da Cronet:a implementação de fallback 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 (com
DefaultHttpDataSource) é mais eficiente.
OkHttp
O OkHttp (link em inglês) é outra pilha de rede moderna que é amplamente usada por muitos apps Android conhecidos. Ele é compatível com HTTP e HTTP/2, mas ainda não com HTTP/3 via QUIC.
O ExoPlayer oferece suporte ao OkHttp pela
biblioteca OkHttp.
Consulte o README.md da biblioteca para instruções detalhadas sobre como usar
o recurso. Ao usar a biblioteca OkHttp, a pilha de rede é incorporada ao app. Isso é semelhante à Cronet incorporada, 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 subjacente. Na maioria dos dispositivos, apenas o HTTP é compatível (ou seja, o HTTP/2 e o 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 encapsule a pilha de rede,
junto com um HttpDataSource.Factory correspondente. As bibliotecas Cronet e OkHttp do ExoPlayer são bons exemplos de como fazer isso.
Ao integrar com uma pilha de rede Java pura, é recomendável implementar um
DataSourceContractTest para verificar se a implementação do HttpDataSource
está funcionando 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 via QUIC |
Nenhuma | Disponível apenas na API 34 ou nas extensões S 7 |
| Cronet (Google Play Services) | HTTP HTTP/2 HTTP/3 via QUIC |
Pequeno (<100 KB) |
Requer o Google Play Services. A versão da Cronet foi atualizada automaticamente |
| Cronet (incorporado) | HTTP HTTP/2 HTTP/3 via QUIC |
Grande (~8MB) |
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 (<1MB) |
|
| Pilha de rede integrada | HTTP (varia de acordo com o dispositivo) |
Nenhuma | 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. Em particular, ao transmitir mídia adaptativa distribuída usando uma rede de distribuição de conteúdo (CDN), há casos em que o uso desses protocolos permite que as CDNs operem com muito mais eficiência. Por isso, o suporte do HttpEngine e do Cronet para HTTP/2 e HTTP/3 via 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 atinge um bom equilíbrio entre permitir o uso de HTTP/2 e HTTP/3 sobre 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 fração significativa de dispositivos que vão executar seu app, usar o Cronet Embedded ou o OkHttp pode ser mais adequado. 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 pequena parte da funcionalidade do app.
Além da mídia, geralmente é uma boa ideia escolher uma única pilha de rede para toda a rede realizada pelo app. Isso permite que os recursos (como sockets) sejam agrupados e compartilhados de maneira eficiente entre o ExoPlayer e outros componentes do app.
Como é provável que seu app precise realizar operações de rede não relacionadas à reprodução de mídia, a escolha da pilha de rede deve considerar nossas recomendações acima para streaming de mídia isolado, os requisitos de quaisquer outros componentes que realizam operações de rede e a importância relativa deles para seu 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 quando você quer voltar na mídia atual ou repetir o mesmo item.
O armazenamento em cache requer uma instância SimpleCache que aponta 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();