Notícias sobre produtos
Melhorar a reprodução de mídia: introdução ao pré-carregamento com o Media3 – Parte 1
Leitura de 8 minutos
Nos apps atuais focados em mídia, oferecer uma experiência de reprodução tranquila e ininterrupta é fundamental para uma experiência do usuário agradável. Os usuários esperam que os vídeos comecem instantaneamente e sejam reproduzidos sem problemas e sem pausas.
O principal desafio é a latência. Tradicionalmente, um player de vídeo só começa a funcionar (conectando, baixando, analisando, armazenando em buffer) depois que o usuário escolhe um item para reprodução. Essa abordagem reativa é lenta para o contexto atual de vídeos curtos. A solução é ser proativo. Precisamos antecipar o que o usuário vai assistir em seguida e preparar o conteúdo com antecedência. Essa é a essência da pré-carga.
Os principais benefícios da pré-carga incluem:
- 🚀 Início mais rápido da reprodução:os vídeos já estão prontos para serem assistidos, o que leva a transições mais rápidas entre os itens e um início mais imediato.
- 📉 Redução do buffer:ao carregar dados de forma proativa, é muito menos provável que a reprodução seja interrompida, por exemplo, devido a falhas na rede.
- ✨ Experiência do usuário mais fluida:a combinação de inicializações mais rápidas e menos armazenamento em buffer cria uma interação mais fluida e perfeita para os usuários.
Nesta série de três partes, vamos apresentar e detalhar os utilitários avançados da Media3 para (pré-)carregar componentes.
- Na Parte 1, vamos abordar os fundamentos: entender as diferentes estratégias de pré-carregamento disponíveis na Media3, ativar o PreloadConfiguration e configurar o DefaultPreloadManager, permitindo que seu app pré-carregue itens. Ao final deste blog, você poderá pré-carregar e reproduzir itens de mídia com a classificação e a duração configuradas.
- Na Parte 2, vamos abordar tópicos mais avançados do DefaultPreloadManager: usar listeners para análise, explorar práticas recomendadas prontas para produção, como o padrão de janela deslizante e componentes compartilhados personalizados do DefaultPreloadManager e do ExoPlayer.
- Na Parte 3, vamos analisar o cache de disco com o DefaultPreloadManager.
Pré-carregamento ao resgate! 🦸♀️
A ideia principal por trás do pré-carregamento é simples: carregar o conteúdo de mídia antes de precisar dele. Quando um usuário desliza para o próximo vídeo, os primeiros segmentos já estão baixados e disponíveis para reprodução imediata.
Pense nisso como um restaurante. Uma cozinha movimentada não espera um pedido para começar a cortar cebolas. 🧅 Eles fazem o trabalho de preparação com antecedência. O pré-carregamento é o trabalho de preparação do player de vídeo.
Quando ativada, a pré-carga ajuda a minimizar a latência de junção quando um usuário pula para o próximo item antes que o buffer de reprodução chegue a ele. O primeiro período da próxima janela é preparado, e as amostras de vídeo, áudio e texto são armazenadas em buffer. O período pré-carregado é enfileirado no player com amostras armazenadas em buffer imediatamente disponíveis e prontas para serem enviadas ao codec para renderização.
No Media3, há duas APIs principais para pré-carregamento, cada uma adequada para diferentes casos de uso. A primeira etapa é escolher a API certa.
1. Pré-carregar itens da playlist com PreloadConfiguration
Essa é a abordagem simples, útil para mídia linear e sequencial, como playlists em que a ordem de reprodução é previsível (como uma série de episódios). Você dá ao player a lista completa de itens de mídia usando as APIs de playlist do ExoPlayer e define a PreloadConfiguration para o player. Assim, ele faz o pré-carregamento automático dos próximos itens na sequência conforme configurado. Essa API tenta otimizar a latência de junção quando um usuário pula para o próximo item antes que o buffer de reprodução já se sobreponha a ele.
O pré-carregamento só é iniciado quando nenhuma mídia está sendo carregada para a reprodução em andamento, o que evita que ele concorra por largura de banda com a reprodução principal.
Se você ainda não tem certeza se precisa do pré-carregamento, essa API é uma ótima opção de baixo custo para testar.
player.preloadConfiguration = PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)
Com a PreloadConfiguration acima, o player tenta pré-carregar cinco segundos de mídia para o próximo item na playlist.
Depois de ativar, é possível desativar o pré-carregamento de playlists usando PreloadConfiguration.DEFAULT:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Pré-carregar listas dinâmicas com o PreloadManager
Para interfaces dinâmicas, como feeds verticais ou carrosséis, em que o próximo item é determinado pela interação do usuário, a API PreloadManager é adequada. Esse é um novo componente independente e eficiente na biblioteca ExoPlayer da Media3, projetado especificamente para pré-carregar de forma proativa. Ele gerencia uma coleção de MediaSources em potencial, priorizando-as com base na proximidade da posição atual do usuário e oferece controle granular sobre o que pré-carregar, adequado para cenários complexos, como feeds dinâmicos de vídeos curtos.
Como configurar o PreloadManager
O DefaultPreloadManager é a implementação canônica do PreloadManager.
O criador de DefaultPreloadManager pode criar o DefaultPreloadManager e todas as instâncias do ExoPlayer que vão reproduzir o conteúdo pré-carregado. Para criar um DefaultPreloadManager, é necessário transmitir um TargetPreloadStatusControl, que o gerenciador de pré-carregamento pode consultar para descobrir quanto carregar de um item. Vamos explicar e definir um exemplo de TargetPreloadStatusControl na seção abaixo.
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
É necessário usar o mesmo builder para o ExoPlayer e o DefaultPreloadManager, o que garante que os componentes deles sejam compartilhados corretamente.
Pronto. Agora você tem um gerente pronto para receber instruções.
Como configurar duração e classificação com TargetPreloadStatusControl
E se você quiser pré-carregar, digamos, 10 segundos de vídeo? Você pode informar a posição dos itens de mídia no carrossel, e o DefaultPreloadManager prioriza o carregamento dos itens com base na proximidade do item que o usuário está reproduzindo.
Se você quiser controlar quanto tempo do item será pré-carregado, informe isso com DefaultPreloadManager.PreloadStatus.
Por exemplo,
- O item "A" é a prioridade mais alta. Carregue 5 segundos de vídeo.
- O item "B" é de prioridade média, mas, quando chegar nele, carregue 3 segundos de vídeo.
- O item "C" tem menos prioridade, carregue apenas as faixas.
- O item "D" é ainda menos prioritário, apenas se prepare.
- Qualquer outro item está longe. Não faça pré-carregamento de nada.
Esse controle granular ajuda a otimizar o uso de recursos, o que é recomendado para uma reprodução sem problemas.
import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus class MyTargetPreloadStatusControl( currentPlayingIndex: Int = C.INDEX_UNSET ) : TargetPreloadStatusControl<Int,PreloadStatus> { // The app is responsible for updating this based on UI state override fun getTargetPreloadStatus(index: Int): PreloadStatus? { val distance = index - currentPlayingIndex // Adjacent items (Next): preload 5 seconds if (distance == 1) { // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position return PreloadStatus.specifiedRangeLoaded(5000L) } // Adjacent items (Previous): preload 3 seconds else if (distance == -1) { // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position return PreloadStatus.specifiedRangeLoaded(3000L) } // Items two positions away: just select tracks else if (distance) == 2) { // Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED return PreloadStatus.TRACKS_SELECTED } // Items four positions away: just select prepare else if (abs(distance) <= 4) { // Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED return PreloadStatus.SOURCE_PREPARED } // All other items are too far away return null } }
Dica: o PreloadManager pode manter os itens anteriores e seguintes pré-carregados, enquanto o PreloadConfiguration só procura os próximos itens.
Como gerenciar itens de pré-carregamento
Agora que você criou o gerente, pode começar a dizer o que ele precisa fazer. À medida que o usuário rola um feed, você identifica os próximos vídeos e os adiciona ao gerenciador. A interação com o PreloadManager é uma conversa orientada por estado entre a interface e o mecanismo de pré-carregamento.
1. Adicionar itens de mídia
Ao preencher o feed, informe ao gerente a mídia que precisa ser rastreada. Se você estiver começando, adicione toda a lista que quer pré-carregar. Depois, você pode continuar adicionando um único item à lista conforme necessário. Você tem controle total sobre os itens na lista de pré-carregamento, o que significa que também precisa gerenciar o que é adicionado e removido do gerenciador.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}O gerente vai começar a buscar dados para esse MediaItem em segundo plano.
Depois de adicionar, peça ao gerente para reavaliar a nova lista, indicando que algo mudou, como a adição ou remoção de um item, ou que o usuário mudou para jogar um novo item.
preloadManager.invalidate()
2. Recuperar e reproduzir um item
Aqui está a principal lógica de reprodução. Quando o usuário decidir reproduzir esse vídeo, não será necessário criar um novo MediaSource. Em vez disso, peça ao PreloadManager o que ele já preparou. É possível recuperar o MediaSource do PreloadManager usando o MediaItem.
Se o item recuperado do PreloadManager for nulo, isso significa que o mediaItem ainda não foi pré-carregado ou adicionado ao PreloadMamager. Portanto, você pode definir o mediaItem diretamente.
// When a media item is about to display on the screen val mediaSource = preloadManager.getMediaSource(mediaItem) if (mediaSource!= null) { player.setMediaSource(mediaSource) } else { // If mediaSource is null, that mediaItem hasn't been added yet. // So, send it directly to the player. player.setMediaItem(mediaItem) } player.prepare() // When the media item is displaying at the center of the screen player.play()
Ao preparar o MediaSource recuperado do PreloadManager, você faz a transição perfeita do pré-carregamento para a reprodução, usando os dados que já estão na memória. Isso acelera o tempo de inicialização.
3. Manter o índice atual sincronizado com a interface
Como nosso feed / lista pode ser dinâmico, é importante notificar o PreloadManager sobre o índice de reprodução atual para que ele sempre priorize os itens mais próximos do índice atual para pré-carregamento.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. Remover um item
Para manter a eficiência do gerenciador, remova os itens que ele não precisa mais rastrear, como os que estão longe da posição atual do usuário.
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
Se você precisar limpar todos os itens de uma vez, chame preloadManager.reset().
5. Solte o gerente
Quando você não precisar mais do PreloadManager (por exemplo, quando a interface for destruída), libere-o para liberar os recursos. Um bom lugar para fazer isso é onde você já está liberando os recursos do player. Recomendamos liberar o gerenciador antes do player, já que o jogador pode continuar jogando se você não precisar de mais pré-carregamento.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
Demonstração
Confira ao vivo 👍
Na demonstração abaixo, vemos o impacto do PreloadManager no lado direito, que tem tempos de carregamento mais rápidos, enquanto o lado esquerdo mostra a experiência atual. Você também pode conferir o exemplo de código da demonstração. Bônus: ele também mostra a latência de inicialização de cada vídeo.
Qual é a próxima etapa?
E chegamos ao fim da Parte 1! Agora você tem as ferramentas para criar um sistema de pré-carregamento dinâmico. Você pode usar PreloadConfiguration para pré-carregar o próximo item de uma playlist no ExoPlayer ou configurar um DefaultPreloadManager, adicionar e remover itens rapidamente, configurar o status de pré-carregamento de destino e recuperar corretamente o conteúdo pré-carregado para reprodução.
Na Parte 2, vamos nos aprofundar no DefaultPreloadManager. Vamos explorar como detectar eventos de pré-carregamento, discutir práticas recomendadas, como usar uma janela deslizante para evitar problemas de memória, e analisar os componentes compartilhados personalizados do ExoPlayer e do DefaultPreloadManager.
Quer compartilhar algum feedback? Queremos saber sua opinião.
Fique por dentro e acelere seu app! 🚀
Continuar lendo
-
Notícias sobre produtos
Esta é a segunda parte da nossa série de três partes sobre pré-carregamento de mídia com a Media3. Esta série foi criada para orientar você no processo de criação de experiências de mídia altamente responsivas e de baixa latência nos seus apps Android.
Mayuri Khinvasara Khabya • Leitura de 9 minutos
-
Notícias sobre produtos
O fluxo de trabalho e as necessidades de IA de cada desenvolvedor são únicos. Por isso, é importante poder escolher como a IA ajuda no desenvolvimento. Em janeiro, lançamos a opção de escolher qualquer modelo de IA local ou remoto para ativar a funcionalidade de IA no Android Studio.
Matthew Warner • Leitura de 2 minutos
-
Notícias sobre produtos
O Android Studio Panda 3 agora está estável e pronto para uso em produção. Com essa versão, você tem ainda mais controle e personalização sobre seus fluxos de trabalho com tecnologia de IA, o que facilita a criação de apps Android de alta qualidade.
Matt Dyor • 3 min de leitura
Fique por dentro
Receba os insights mais recentes sobre desenvolvimento Android na sua caixa de entrada semanalmente.