Novidades sobre produtos
Como melhorar a reprodução de mídia: apresentamos o pré-carregamento com a Media3 – Parte 1
Leitura de 8 minutos
Nos apps atuais com foco em mídia, oferecer uma experiência de reprodução suave 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 pausas.
O principal desafio é a latência. Tradicionalmente, um player de vídeo só começa a funcionar (conectando, fazendo download, analisando, armazenando em buffer) depois que o usuário escolhe um item para reprodução. Essa abordagem reativa é lenta para o contexto de vídeos curtos de hoje. 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 do pré-carregamento.
Os principais benefícios do pré-carregamento incluem:
- 🚀 Início de reprodução mais rápido:os vídeos já estão prontos, o que leva a transições mais rápidas entre os itens e um início mais imediato.
- 📉 Buffer reduzido:ao carregar dados de forma proativa, a reprodução tem muito menos probabilidade de parar, por exemplo, devido a problemas de rede.
- ✨ Experiência do usuário mais suave:a combinação de inícios mais rápidos e menos buffer cria uma interação mais fluida e perfeita para os usuários.
Nesta série de três partes, vamos apresentar e analisar detalhadamente os utilitários avançados da Media3 para componentes de (pré-)carregamento.
- Na Parte 1, vamos abordar os fundamentos: entender as diferentes estratégias de pré-carregamento disponíveis na Media3, ativar a 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 detalhadamente o armazenamento em cache de disco com o DefaultPreloadManager.
Pré-carregamento em ação! 🦸♀️
A ideia principal por trás do pré-carregamento é simples: carregar conteúdo de mídia antes de precisar dele. Quando um usuário desliza para o próximo vídeo, os primeiros segmentos do vídeo já são baixados e ficam disponíveis para reprodução imediata.
Pense nisso como um restaurante. Uma cozinha movimentada não espera um pedido para começar a picar cebolas. 🧅 Eles fazem o trabalho de preparação com antecedência. O pré-carregamento é o trabalho de preparação do seu player de vídeo.
Quando ativado, o pré-carregamento pode ajudar 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 ao próximo item. 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.
Na 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é-carregamento de itens de playlist com PreloadConfiguration
Essa é a abordagem simples, útil para mídias lineares e sequenciais, como playlists em que a ordem de reprodução é previsível (como uma série de episódios). Você fornece ao player a lista completa de itens de mídia usando as APIs de playlist do ExoPlayer e define a PreloadConfiguration para o player. Em seguida, ele pré-carrega automaticamente os 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 ao próximo item.
O pré-carregamento só é iniciado quando nenhuma mídia está sendo carregada para a reprodução em andamento, o que impede que ela concorra à largura de banda com a reprodução principal.
Se você ainda não tiver certeza se precisa de pré-carregamento, essa API é uma ótima opção de baixo custo para testá-la.
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 ativado, o pré-carregamento da playlist pode ser desativado novamente usando PreloadConfiguration.DEFAULT:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Pré-carregamento de listas dinâmicas com 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 avançado na biblioteca Media3 ExoPlayer, projetado especificamente para pré-carregamento proativo. 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 builder do 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, você precisa transmitir um TargetPreloadStatusControl, que o gerenciador de pré-carregamento pode consultar para descobrir quanto carregar para 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 internos deles sejam compartilhados corretamente.
Pronto. Agora você tem um gerenciador pronto para receber instruções.
Como configurar a duração e a classificação com TargetPreloadStatusControl
E se você quiser pré-carregar, digamos, 10 segundos de vídeo? Você pode fornecer 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 a duração do item a ser pré-carregado, poderá informar isso com o DefaultPreloadManager.PreloadStatus retornado.
Por exemplo,
- O item "A" é a maior prioridade, carregue 5 segundos de vídeo.
- O item "B" é de prioridade média, mas, quando você chegar a ele, carregue 3 segundos de vídeo.
- O item "C" tem menos prioridade, carregue apenas faixas.
- O item "D" tem ainda menos prioridade, apenas prepare.
- Quaisquer outros itens estão longe, não pré-carregue nada.
Esse controle granular pode ajudar a otimizar a utilização de recursos, o que é recomendado para uma reprodução perfeita.
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 a PreloadConfiguration só vai procurar os próximos itens.
Como gerenciar itens de pré-carregamento
Com o gerenciador criado, você pode começar a dizer o que trabalhar. À 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 gerenciador a mídia que ele precisa rastrear. Se você estiver começando, poderá adicionar toda a lista que quiser pré-carregar. Posteriormente, você pode continuar adicionando um único item à lista conforme necessário. Você tem controle total sobre quais itens estão 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 gerenciador vai começar a buscar dados para esse MediaItem em segundo plano.
Depois de adicionar, peça ao gerenciador para reavaliar a nova lista (indicando que algo mudou, como adicionar/ remover um item ou o usuário mudar para reproduzir um novo item).
preloadManager.invalidate()
2. Recuperar e reproduzir um item
Aqui está a lógica principal de reprodução. Quando o usuário decide reproduzir esse vídeo, não é necessário criar um novo MediaSource. Em vez disso, você pede ao PreloadManager o que ele já preparou. É possível recuperar o MediaSource do Preload Manager 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ê escolhe 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 que torna o tempo de início mais rápido.
3. Manter o índice atual sincronizado com a interface
Como nosso feed / lista pode ser dinâmico, é importante notificar o PreloadManager do índice de reprodução atual para que ele possa sempre priorizar 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 o gerenciador eficiente, remova os itens que ele não precisa mais rastrear, como itens 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 só vez, chame preloadManager.reset().
5. Liberar o gerenciador
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. É recomendável liberar o gerenciador antes do player, porque o player pode continuar reproduzindo 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. Também é possível conferir o exemplo de código sample da demonstração. (Bônus: ele também mostra a latência de inicialização de cada vídeo)
Qual é a próxima etapa?
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.
Você tem algum feedback para compartilhar? Queremos saber sua opinião.
Fique atento e deixe seu app mais rápido! 🚀
Continuar lendo
-
Novidades sobre produtos
Esta é a segunda parte da nossa série de três partes sobre o pré-carregamento de mídia com a Media3. Essa 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
-
Novidades sobre produtos
O Android Studio Panda 4 agora é estável e está pronto para uso na produção. Essa versão traz o modo de planejamento, a previsão da próxima edição e muito mais, tornando mais fácil do que nunca criar apps Android de alta qualidade.
Matt Dyor • Leitura de 5 minutos
-
Novidades sobre produtos
Se você é um desenvolvedor Android que quer implementar recursos inovadores de IA no seu app, lançamos recentemente novas atualizações avançadas.
Thomas Ezan • Leitura de 3 minutos
Fique por dentro
Receba os insights mais recentes sobre o desenvolvimento do Android na sua caixa de entrada semanalmente.