Novità sul prodotto
Migliorare la riproduzione multimediale: introduzione del precaricamento con Media3 - Parte 1
Lettura di 8 minuti
Nelle app incentrate sui contenuti multimediali di oggi, offrire un'esperienza di riproduzione fluida e senza interruzioni è fondamentale per un'esperienza utente piacevole. Gli utenti si aspettano che i loro video vengano riprodotti immediatamente e senza interruzioni.
La sfida principale è la latenza. Tradizionalmente, un video player inizia il suo lavoro (connessione, download, analisi, buffering) solo dopo che l'utente ha scelto un elemento da riprodurre. Questo approccio reattivo è lento per il contesto attuale dei video nel formato breve. La soluzione è essere proattivi. Dobbiamo anticipare i contenuti che l'utente guarderà successivamente e prepararli in anticipo. Questa è l'essenza del precaricamento.
I principali vantaggi del precaricamento includono:
- 🚀 Avvio della riproduzione più rapido: i video sono già pronti, il che consente transizioni più rapide tra gli elementi e un avvio più immediato.
- 📉 Buffering ridotto: caricando i dati in modo proattivo, è molto meno probabile che la riproduzione si interrompa, ad esempio a causa di problemi di rete.
- ✨ Esperienza utente più fluida: la combinazione di avvii più rapidi e meno buffering crea un'interazione più fluida e senza interruzioni per gli utenti.
In questa serie in tre parti, introdurremo e analizzeremo in dettaglio le potenti utilità di Media3 per il (pre)caricamento dei componenti.
- Nella prima parte, tratteremo le basi: la comprensione delle diverse strategie di precaricamento disponibili in Media3, l'attivazione di PreloadConfiguration e la configurazione di DefaultPreloadManager, che consente all'app di precaricare gli elementi. Al termine di questo post del blog, dovresti essere in grado di precaricare e riprodurre gli elementi multimediali con la classificazione e la durata configurate.
- Nella Parte 2, approfondiremo argomenti più avanzati di DefaultPreloadManager: l'utilizzo di listener per l'analisi, l'esplorazione delle best practice pronte per la produzione come il pattern della finestra scorrevole e i componenti condivisi personalizzati di DefaultPreloadManager ed ExoPlayer.
- Nella parte 3, analizzeremo nel dettaglio la memorizzazione nella cache su disco con DefaultPreloadManager.
Il precaricamento in soccorso. 🦸♀️
L'idea di base del precaricamento è semplice: caricare i contenuti multimediali prima che ti servano. Quando un utente scorre per passare al video successivo, i primi segmenti del video sono già stati scaricati e sono disponibili per la riproduzione immediata.
Immagina che sia come un ristorante. Una cucina affollata non aspetta un ordine per iniziare a tagliare le cipolle. 🧅 Si preparano in anticipo. Il precaricamento è il lavoro di preparazione del video player.
Se attivato, il precaricamento può contribuire a ridurre al minimo la latenza di unione quando un utente passa all'elemento successivo prima che il buffer di riproduzione raggiunga l'elemento successivo. Il primo periodo della finestra successiva viene preparato e i campioni di video, audio e testo vengono memorizzati nel buffer. Il periodo precaricato viene successivamente messo in coda nel player con i campioni memorizzati nel buffer immediatamente disponibili e pronti per essere inviati al codec per il rendering.
In Media3 esistono due API principali per il precaricamento, ciascuna adatta a casi d'uso diversi. La scelta dell'API giusta è il primo passo.
1. Precaricare gli elementi della playlist con PreloadConfiguration
Si tratta dell'approccio semplice, utile per contenuti multimediali lineari e sequenziali come le playlist in cui l'ordine di riproduzione è prevedibile (come una serie di episodi). Fornisci al giocatore l'elenco completo degli elementi multimediali utilizzando le API playlist di ExoPlayer e imposta PreloadConfiguration per il player, che precarica automaticamente gli elementi successivi nella sequenza come configurato. Questa API tenta di ottimizzare la latenza di unione quando un utente passa all'elemento successivo prima che il buffer di riproduzione si sovrapponga già all'elemento successivo.
Il precaricamento viene avviato solo quando non vengono caricati contenuti multimediali per la riproduzione in corso, in modo da non competere per la larghezza di banda con la riproduzione principale.
Se non hai ancora deciso se hai bisogno del precaricamento, questa API è un'ottima opzione a basso impegno per provarla.
player.preloadConfiguration =
PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)
Con la PreloadConfiguration riportata sopra, il player tenta di precaricare cinque secondi di contenuti multimediali per l'elemento successivo della playlist.
Una volta attivato, il precaricamento delle playlist può essere disattivato di nuovo utilizzando PreloadConfiguration.DEFAULT per disabilitarlo:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Precaricare gli elenchi dinamici con PreloadManager
Per le UI dinamiche come i feed verticali o i caroselli, in cui l'elemento "successivo" è determinato dall'interazione dell'utente, è appropriata l'API PreloadManager. Si tratta di un nuovo componente potente e autonomo all'interno della libreria Media3 ExoPlayer progettato specificamente per il precaricamento proattivo. Gestisce una raccolta di potenziali MediaSource, dando la priorità a quelli più vicini alla posizione attuale dell'utente e offre un controllo granulare su cosa precaricare, adatto a scenari complessi come i feed dinamici di video in formato breve.
Configurazione di PreloadManager
DefaultPreloadManager è l'implementazione canonica di PreloadManager.
Il builder di DefaultPreloadManager può creare sia DefaultPreloadManager sia qualsiasi istanza di ExoPlayer che riprodurrà i contenuti precaricati. Per creare un DefaultPreloadManager, devi passare un TargetPreloadStatusControl, che il gestore del precaricamento può interrogare per scoprire quanto caricare per un elemento. Nella sezione seguente spiegheremo e definiremo un esempio di TargetPreloadStatusControl.
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
È necessario utilizzare lo stesso builder sia per ExoPlayer che per DefaultPreloadManager, in modo da garantire la corretta condivisione dei componenti sottostanti.
Ed è fatta. Ora hai un gestore pronto a ricevere istruzioni.
Configurazione di durata e ranking con TargetPreloadStatusControl
Cosa succede se vuoi precaricare, ad esempio, 10 secondi di video? Puoi fornire la posizione degli elementi multimediali nel carosello e DefaultPreloadManager dà la priorità al caricamento degli elementi in base alla loro vicinanza all'elemento che l'utente sta riproducendo.
Se vuoi controllare la durata dell'elemento da precaricare, puoi farlo con DefaultPreloadManager.PreloadStatus.
Ad esempio,
- L'elemento "A" ha la priorità più alta, carica 5 secondi di video.
- L'elemento "B" ha una priorità media, ma quando lo raggiungi, carica 3 secondi di video.
- L'elemento "C" ha una priorità inferiore, carica solo le tracce.
- L'elemento "D" ha una priorità ancora inferiore, quindi preparati.
- Tutti gli altri elementi sono lontani, non precaricare nulla.
Questo controllo granulare può aiutarti a ottimizzare l'utilizzo delle risorse, il che è consigliato per una riproduzione senza interruzioni.
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
}
}
Suggerimento: PreloadManager può mantenere precaricati sia gli elementi precedenti che quelli successivi, mentre PreloadConfiguration guarda solo avanti, agli elementi successivi.
Gestione degli elementi di precaricamento
Ora che hai creato il tuo manager, puoi iniziare a dirgli su cosa lavorare. Man mano che l'utente scorre un feed, identificherai i video in arrivo e li aggiungerai al gestore. L'interazione con PreloadManager è una conversazione basata sullo stato tra la tua UI e il motore di precaricamento.
1. Aggiungere elementi multimediali
Man mano che compili il feed, devi comunicare al gestore i contenuti multimediali da monitorare. Se stai iniziando, puoi aggiungere l'intero elenco che vuoi precaricare. Successivamente, puoi continuare ad aggiungere un singolo elemento all'elenco in base alle tue esigenze. Hai il pieno controllo sugli elementi presenti nell'elenco di precaricamento, il che significa che devi anche gestire gli elementi aggiunti e rimossi dal gestore.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}
L'amministratore inizierà ora a recuperare i dati per questo MediaItem in background.
Dopo l'aggiunta, chiedi al gestore di rivalutare il nuovo elenco (suggerendo che qualcosa è cambiato, ad esempio l'aggiunta/ rimozione di un elemento o il passaggio a un nuovo elemento).
preloadManager.invalidate()
2. Recuperare e riprodurre un elemento
Ecco la logica di riproduzione principale. Quando l'utente decide di riprodurre il video, non è necessario creare un nuovo MediaSource. Invece, chiedi a PreloadManager quello che ha già preparato. Puoi recuperare MediaSource da Preload Manager utilizzando MediaItem.
Se l'elemento recuperato da PreloadManager è nullo, significa che mediaItem non è ancora precaricato o aggiunto a PreloadManager, quindi scegli di impostare mediaItem direttamente.
// 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()
Preparando MediaSource recuperato da PreloadManager, la transizione dal precaricamento alla riproduzione avviene senza problemi, utilizzando i dati già presenti in memoria. Questo è ciò che rende più veloce l'avvio.
3. Mantieni l'indice corrente sincronizzato con la UI
Poiché il nostro feed / elenco potrebbe essere dinamico, è importante comunicare a PreloadManager l'indice di riproduzione corrente in modo che possa sempre dare la priorità al precaricamento degli elementi più vicini all'indice corrente.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. Rimuovere un elemento
Per mantenere efficiente il gestore, devi rimuovere gli elementi che non deve più monitorare, ad esempio quelli molto distanti dalla posizione attuale dell'utente.
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
Se devi cancellare tutti gli articoli contemporaneamente, puoi chiamare il numero preloadManager.reset().
5. Rilasciare il Manager
Quando non hai più bisogno di PreloadManager (ad esempio, quando la UI viene eliminata), devi rilasciarlo per liberare le sue risorse. Un buon punto di partenza è la posizione in cui rilasci già le risorse del giocatore. Ti consigliamo di rilasciare il manager prima del giocatore, in quanto il giocatore può continuare a giocare se non hai bisogno di ulteriore precaricamento.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
Demo
Guarda come funziona in tempo reale 👍
Nella demo riportata di seguito, vediamo l'impatto di PreloadManager sul lato destro, che ha tempi di caricamento più rapidi, mentre il lato sinistro mostra l'esperienza esistente. Puoi anche visualizzare il codice campione per la demo. Bonus: mostra anche la latenza di avvio per ogni video.
Qual è il passaggio successivo?
E con questo si conclude la Parte 1. Ora hai gli strumenti per creare un sistema di precaricamento dinamico. Puoi utilizzare PreloadConfiguration per precaricare l'elemento successivo di una playlist in ExoPlayer oppure configurare un DefaultPreloadManager, aggiungere e rimuovere elementi al volo, configurare lo stato di precaricamento di destinazione e recuperare correttamente i contenuti precaricati per la riproduzione.
Nella parte 2, approfondiremo l'argomento DefaultPreloadManager. Esploreremo come rilevare gli eventi di precaricamento, discuteremo delle best practice come l'utilizzo di una finestra scorrevole per evitare problemi di memoria e daremo un'occhiata ai componenti condivisi personalizzati di ExoPlayer e DefaultPreloadManager.
Hai un feedback da condividere? Non vediamo l'ora di ricevere il tuo feedback.
Continua a seguirci e velocizza la tua app. 🚀
Continua a leggere
-
Novità sul prodotto
Ti diamo il benvenuto alla seconda parte della nostra serie in tre parti sul precaricamento dei contenuti multimediali con Media3. Questa serie è progettata per guidarti nel processo di creazione di esperienze multimediali a bassa latenza e altamente reattive nelle tue app per Android.
Mayuri Khinvasara Khabya • Lettura di 9 minuti
-
Novità sul prodotto
Android Studio Panda 4 è ora stabile e pronto per l'uso in produzione. Questa release introduce la modalità Pianificazione, la previsione della modifica successiva e altro ancora, semplificando la creazione di app per Android di alta qualità.
Matt Dyor • Lettura di 5 minuti
-
Novità sul prodotto
Se sei uno sviluppatore Android che vuole implementare funzionalità di AI innovative nella tua app, di recente abbiamo lanciato nuovi potenti aggiornamenti.
Thomas Ezan • Lettura di 3 minuti
Segui gli aggiornamenti
Ricevi ogni settimana gli ultimi approfondimenti sullo sviluppo per Android direttamente nella tua casella di posta.