L'interfaccia Player
è il fulcro della libreria ExoPlayer. Player
espone le funzionalità tradizionali di media player di alto livello, come la capacità
eseguire il buffering di contenuti multimediali, riprodurre, mettere in pausa e cercare contenuti. L'implementazione predefinita ExoPlayer
è
progettato per fare poche ipotesi (e quindi per imporre restrizioni)
il tipo di contenuti multimediali riprodotti, come e dove sono archiviati e come
eseguire il rendering. Invece di implementare direttamente il caricamento
e il rendering dei contenuti multimediali,
ExoPlayer
implementazioni delegano questo lavoro ai componenti che vengono inseriti
quando viene creato un player o quando vengono trasmesse a quest'ultimo nuove fonti multimediali.
I componenti comuni a tutte le implementazioni di ExoPlayer
sono:
MediaSource
di istanze che definiscono i contenuti multimediali da riprodurre, caricano i contenuti multimediali e da cui è possibile leggere i contenuti multimediali caricati. Viene creata un'istanzaMediaSource
daMediaItem
di unMediaSource.Factory
all'interno del player. Possono anche Deve essere passata direttamente al player tramite l'API per la playlist basata su origine multimediale.- Un'istanza
MediaSource.Factory
che converte unMediaItem
inMediaSource
. La L'elementoMediaSource.Factory
viene inserito alla creazione del player. Renderer
di istanze che mostrano i singoli componenti del contenuto multimediale. Si tratta di viene inserito quando viene creato il player.- Un
TrackSelector
che seleziona le tracce fornite daMediaSource
consumato da ogniRenderer
disponibile. È stato inserito unTrackSelector
quando viene creato il player. - Un
LoadControl
che controlla quandoMediaSource
memorizza più contenuti multimediali nel buffer, la quantità di contenuti multimediali memorizzati nel buffer. Viene inserito unLoadControl
quando il player è stato creato. - Un
LivePlaybackSpeedControl
che controlla la velocità di riproduzione durante le live per consentire al player di rimanere vicino a un offset dal vivo configurato. R L'elementoLivePlaybackSpeedControl
viene inserito alla creazione del player.
Concetto di inserire componenti che implementano elementi del player in tutta la libreria. Le implementazioni predefinite alcuni componenti delegano il lavoro a ulteriori componenti inseriti. Ciò consente a molti singoli sottocomponenti da sostituire singolarmente con implementazioni configurati in modo personalizzato.
Personalizzazione del player
Alcuni esempi comuni di personalizzazione del player tramite l'inserimento di componenti sono: descritti di seguito.
Configurazione dello stack di rete
È disponibile una pagina relativa alla personalizzazione dello stack di rete utilizzato da ExoPlayer.
Memorizzazione nella cache dei dati caricati dalla rete
Consulta le guide per memorizzazione nella cache temporanea e il download di contenuti multimediali.
Personalizzazione delle interazioni con il server
Alcune app potrebbero voler intercettare le richieste e le risposte HTTP. Potresti voler inserire intestazioni di richiesta personalizzate, leggere le intestazioni delle risposte del server, modificare richieste URI e così via. Ad esempio, la tua app potrebbe autenticarsi inserendo un token come intestazione quando richiedi i segmenti multimediali.
L'esempio seguente mostra come implementare questi comportamenti
inserendo un valore DataSource.Factory
personalizzato in DefaultMediaSourceFactory
:
Kotlin
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
Nello snippet di codice riportato sopra, il campo HttpDataSource
inserito include l'intestazione
"Header: Value"
in ogni richiesta HTTP. Questo comportamento è corretto per ogni
un'interazione con un'origine HTTP.
Per un approccio più granulare, puoi inserire il comportamento just-in-time utilizzando un
ResolvingDataSource
. Il seguente snippet di codice mostra come inserire
delle richieste immediatamente prima di interagire con un'origine HTTP:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
Puoi usare un ResolvingDataSource
anche per eseguire
modifiche just-in-time dell'URI, come mostrato nello snippet seguente:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
Personalizzazione della gestione degli errori
L'implementazione di una LoadErrorHandlingPolicy
personalizzata consente alle app di personalizzare
il modo in cui ExoPlayer reagisce agli errori di caricamento. Ad esempio, un'app può decidere di avere un errore rapido
anziché riprovare più volte, o potrebbe essere opportuno personalizzare la logica di backoff
controlla l'intervallo di tempo che deve trascorrere tra un nuovo tentativo e l'altro. Il seguente snippet
mostra come implementare una logica di backoff personalizzata:
Kotlin
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
Java
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
L'argomento LoadErrorInfo
contiene ulteriori informazioni sul caricamento non riuscito in
personalizzare la logica in base al tipo di errore o alla richiesta non riuscita.
Personalizzazione dei flag degli estrattori
I flag dell'estrattore possono essere usati per personalizzare le modalità di estrazione dei singoli formati
dai media progressivi. Possono essere impostate sul DefaultExtractorsFactory
forniti al DefaultMediaSourceFactory
. L'esempio seguente trasmette un flag
che consente la ricerca basata su indice per gli stream MP3.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
Attivazione della ricerca con velocità in bit costante
Per gli stream MP3, ADTS e AMR, puoi attivare la ricerca approssimativa utilizzando un
ipotesi costante della velocità in bit con FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
flag.
Questi flag possono essere impostati per singoli estrattori usando l'impostazione
DefaultExtractorsFactory.setXyzExtractorFlags
come descritto sopra. A
per abilitare la ricerca a velocità in bit costante per tutti gli estrattori che la supportano, usa
DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
ExtractorsFactory
può quindi essere inserito tramite DefaultMediaSourceFactory
come
descritti sopra per personalizzare i flag degli estrattori.
Attivazione dell'accodamento del buffer asincrono
L'accodamento nel buffer asincrono è un miglioramento del rendering di ExoPlayer
che gestisce MediaCodec
istanze in modalità asincrona e
utilizza thread aggiuntivi per pianificare la decodifica e il rendering dei dati. Abilitazione
può ridurre i frame interrotti e le
inadeguatezze audio.
L'accodamento con buffer asincrono è attivo per impostazione predefinita sui dispositivi con Android 12 (livello API 31) e versioni successive, e possono essere attivate manualmente a partire da Android 6.0 (livello API 23). Potresti attivare la funzionalità per i dispositivi specifici su cui riscontri un calo fotogrammi o l'audio insufficiente, in particolare durante la riproduzione con protezione DRM o con una frequenza fotogrammi contenuti.
Nel caso più semplice, devi inserire un'istruzione DefaultRenderersFactory
nella
player come segue:
Kotlin
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
Java
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
Se stai creando un'istanza direttamente per renderer, passa un'istruzione
AsynchronousMediaCodecAdapter.Factory
per MediaCodecVideoRenderer
e
MediaCodecAudioRenderer
costruttori.
Intercettazione delle chiamate al metodo con ForwardingPlayer
Puoi personalizzare parte del comportamento di un'istanza Player
eseguendone il wrapping in
una sottoclasse ForwardingPlayer
e i metodi di override per eseguire qualsiasi
le seguenti:
- Accedi ai parametri prima di trasmetterli al delegato
Player
. - Accedi al valore restituito dal delegato
Player
prima di restituirlo. - Implementa nuovamente il metodo.
Quando esegui l'override dei metodi ForwardingPlayer
, è importante assicurarsi che
l'implementazione rimane autocoerente e conforme alle Player
dell'interfaccia utente, in particolare quando si utilizzano metodi che mirano a
con un comportamento identico o correlato. Ad esempio:
- Se vuoi sostituire ogni "riproduzione" devi eseguire l'override
ForwardingPlayer.play
eForwardingPlayer.setPlayWhenReady
, perché si aspetta che il comportamento di questi metodi sia identicoplayWhenReady = true
. - Per modificare l'incremento in avanti, devi sostituire entrambi
ForwardingPlayer.seekForward
per eseguire una ricerca con il tuo incrementare eForwardingPlayer.getSeekForwardIncrement
per generare report l'incremento personalizzato corretto al chiamante. - Se vuoi controllare quali contenuti
Player.Commands
vengono pubblicizzati da un giocatore istanza, devi eseguire l'override diPlayer.getAvailableCommands()
ePlayer.isCommandAvailable()
e ascolta anche callback diPlayer.Listener.onAvailableCommandsChanged()
per ricevere una notifica modifiche provenienti dal player sottostante.
Personalizzazione di MediaSource
Gli esempi precedenti inseriscono componenti personalizzati da utilizzare durante la riproduzione di tutti
MediaItem
oggetti passati al player. mentre la personalizzazione granulare è
è anche possibile inserire componenti personalizzati
MediaSource
istanze, che possono essere trasmesse direttamente al player. L'esempio
di seguito mostra come personalizzare un ProgressiveMediaSource
per l'utilizzo di un
DataSource.Factory
, ExtractorsFactory
e LoadErrorHandlingPolicy
:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
Creazione di componenti personalizzati
La libreria fornisce implementazioni predefinite dei componenti elencati in alto
di questa pagina per i casi d'uso comuni. Un ExoPlayer
può utilizzare questi componenti, ma
possono anche essere create per utilizzare implementazioni personalizzate se vengono rilevati comportamenti non standard
obbligatorio. Di seguito sono riportati alcuni casi d'uso per le implementazioni personalizzate:
Renderer
- Potresti voler implementare unRenderer
personalizzato per gestire una tipo di media non supportato dalle implementazioni predefinite fornite dalla libreria.TrackSelector
: l'implementazione di un valoreTrackSelector
personalizzato consente a un'app sviluppatore di modificare il modo in cui le tracce esposte da unMediaSource
vengono selezionati per il consumo da parte di ciascuno deiRenderer
disponibili.LoadControl
: l'implementazione di un valoreLoadControl
personalizzato consente a un'app sviluppatore di modificare il criterio di buffering del player.Extractor
: se devi supportare un formato contenitore al momento non disponibile supportata dalla libreria, valuta la possibilità di implementare una classeExtractor
personalizzata.MediaSource
: l'implementazione di una classeMediaSource
personalizzata può essere appropriato se desideri ottenere campioni multimediali da inviare ai renderer in una in modo personalizzato o se vuoi implementare una composizioneMediaSource
personalizzata comportamento degli utenti.MediaSource.Factory
– Implementazione di unMediaSource.Factory
personalizzato consente a un'applicazione di personalizzare il modo in cui viene creato unMediaSource
da unMediaItem
.DataSource
- Il pacchetto upstream di ExoPlayer contiene già un certo numero diDataSource
implementazioni per diversi casi d'uso. Potresti voler implementa la tua classeDataSource
per caricare i dati in un altro modo, ad esempio oltre da un protocollo personalizzato, utilizzando uno stack HTTP personalizzato o da un .
Quando crei componenti personalizzati, segui questi consigli:
- Se un componente personalizzato deve segnalare gli eventi all'app, ti consigliamo
mediante lo stesso modello dei componenti ExoPlayer esistenti, ad esempio
esempio utilizzando classi
EventDispatcher
o passando unHandler
insieme a un listener per il costruttore del componente. - Consigliamo per i componenti personalizzati di utilizzare lo stesso modello dell'ExoPlayer esistente
per consentire la riconfigurazione da parte dell'app durante la riproduzione. Per farlo,
i componenti personalizzati devono implementare
PlayerMessage.Target
e ricevere modifiche alla configurazione nel metodohandleMessage
. Il codice dell'applicazione passa le modifiche alla configurazione chiamando il metodocreateMessage
di ExoPlayer, configurando il messaggio e inviandolo al componente utilizzandoPlayerMessage.send
. Invio di messaggi da recapitare nel thread di riproduzione in corso... ne assicura l'esecuzione in ordine, mentre qualsiasi altra operazione eseguita sul player.