Le sessioni multimediali offrono un modo universale di interagire con un lettore audio o video. In Media3, il player predefinito è la classe ExoPlayer
, che implementa
l'interfaccia Player
. Il collegamento della sessione multimediale al player consente a un'app di pubblicizzare la riproduzione di contenuti multimediali esternamente e di ricevere comandi di riproduzione da fonti esterne.
I comandi possono provenire da pulsanti fisici, come il pulsante di riproduzione sulle cuffie o sul telecomando della TV. Potrebbero provenire anche da app client dotate di controller multimediale, ad esempio mettere in pausa l'Assistente Google. La sessione multimediale delega questi comandi al player dell'app multimediale.
Quando scegliere una sessione multimediale
Quando implementi MediaSession
, consenti agli utenti di controllare la riproduzione:
- Tramite le cuffie. Spesso l'utente può interagire con i pulsanti o con il tocco sulle cuffie per riprodurre o mettere in pausa i contenuti multimediali oppure per passare al brano successivo o precedente.
- Parlando con l'Assistente Google. Generalmente, basta dire "Hey Google, metti in pausa" per mettere in pausa i contenuti multimediali attualmente in riproduzione sul dispositivo.
- Tramite il suo smartwatch Wear OS. Ciò consente di accedere più facilmente ai controlli di riproduzione più comuni durante il gioco sul telefono.
- Tramite i Controlli multimediali. Questo carosello mostra i controlli per ogni sessione multimediale in esecuzione.
- Sulla TV. Consente azioni con pulsanti di riproduzione fisici, controllo della riproduzione della piattaforma e gestione dell'alimentazione (ad esempio, se la TV, la soundbar o il ricevitore A/V si disattivano o se l'ingresso viene cambiato, la riproduzione deve interrompersi nell'app).
- E qualsiasi altro processo esterno che debba influenzare la riproduzione.
È un ottimo modo per molti casi d'uso. In particolare, ti consigliamo vivamente di
utilizzare MediaSession
quando:
- Stai trasmettendo in streaming contenuti video nel formato lungo, come film o TV in diretta.
- Stai trasmettendo in streaming contenuti audio di lunga durata, come podcast o playlist musicali.
- Stai creando un'app TV.
Tuttavia, non tutti i casi d'uso si adattano bene a MediaSession
. Ti consigliamo di utilizzare
solo Player
nei seguenti casi:
- Stai mostrando contenuti nel formato breve in cui il coinvolgimento e l'interazione degli utenti sono fondamentali.
- Non è presente un solo video attivo, ad esempio l'utente sta scorrendo un elenco e sullo schermo vengono visualizzati più video contemporaneamente.
- Stai riproducendo un video introduttivo o esplicativo una tantum, che l'utente dovrebbe guardare attivamente.
- I tuoi contenuti sono riservati alla privacy e non vuoi che processi esterni accedano ai metadati multimediali (ad esempio la modalità di navigazione in incognito in un browser)
Se il tuo caso d'uso non rientra tra quelli elencati sopra, valuta se vuoi che la riproduzione dell'app continui anche quando l'utente non interagisce attivamente con i contenuti. Se la risposta è sì, probabilmente vorrai scegliere MediaSession
. Se la risposta è no, probabilmente vorrai utilizzare
Player
.
Creare una sessione multimediale
Una sessione multimediale si trova accanto al player che gestisce. Puoi creare una sessione multimediale con un oggetto Context
e un Player
. Devi creare e
inizializzare una sessione multimediale quando necessario, ad esempio il metodo del ciclo di vita onStart()
o
onResume()
del metodo Activity
o Fragment
oppure onCreate()
del metodo Service
proprietario della sessione multimediale e del relativo player associato.
Per creare una sessione multimediale, inizializza un Player
e forniscilo a
MediaSession.Builder
in questo modo:
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Gestione automatica dello stato
La raccolta Media3 aggiorna automaticamente la sessione multimediale in base allo stato del player. Pertanto, non è necessario gestire manualmente la mappatura dal player alla sessione.
Si tratta di una pausa rispetto all'approccio precedente in cui dovevi creare e gestire
un PlaybackStateCompat
in modo indipendente dal player stesso, ad esempio per
indicare eventuali errori.
ID sessione univoco
Per impostazione predefinita, MediaSession.Builder
crea una sessione con una stringa vuota come ID sessione. Ciò è sufficiente se un'app intende creare una sola istanza di sessione, che è il caso più comune.
Se un'app vuole gestire contemporaneamente più istanze di sessione, deve assicurarsi che l'ID sessione di ogni sessione sia univoco. L'ID sessione può essere impostato
durante la creazione della sessione con MediaSession.Builder.setId(String id)
.
Se viene visualizzato un IllegalStateException
che causa l'arresto anomalo della tua app con il messaggio di errore IllegalStateException: Session ID must be unique. ID=
, è probabile che una sessione sia stata creata in modo imprevisto prima del rilascio di un'istanza creata in precedenza con lo stesso ID. Per evitare che le sessioni vengano trapelate a causa di un
errore di programmazione, questi casi vengono rilevati e avvisati lanciando
un'eccezione.
Concedi il controllo ad altri clienti
La sessione multimediale è la chiave per controllare la riproduzione. Ti consente di indirizzare i comandi da origini esterne al player che si occupa di riprodurre i tuoi contenuti multimediali. Queste fonti possono essere pulsanti fisici, come il pulsante di riproduzione sul telecomando della TV o delle cuffie, o comandi indiretti come la richiesta di mettere in pausa all'Assistente Google. Analogamente, potresti concedere l'accesso al sistema Android per facilitare i controlli delle notifiche e della schermata di blocco oppure a un orologio Wear OS in modo da poter controllare la riproduzione dal quadrante. I client esterni possono utilizzare un controller multimediale per inviare comandi di riproduzione alla tua app multimediale. Questi vengono ricevuti dalla sessione multimediale, che alla fine delega i comandi al lettore multimediale.
Quando un controller sta per connettersi alla sessione multimediale, viene chiamato il metodo onConnect()
. Puoi utilizzare il ControllerInfo
fornito per decidere se accettare o rifiutare la richiesta. Consulta un esempio di accettazione di una richiesta di connessione nella sezione Dichiarare i comandi disponibili.
Dopo la connessione, un controller può inviare comandi di riproduzione alla sessione. La sessione delega quindi questi comandi al player. I comandi di riproduzione e playlist definiti nell'interfaccia Player
vengono gestiti automaticamente dalla sessione.
Altri metodi di callback ti consentono di gestire, ad esempio, le richieste di
comandi di riproduzione personalizzati e
la modifica della playlist.
In modo simile, questi callback includono un oggetto ControllerInfo
che ti consente di modificare
il modo in cui rispondi a ogni richiesta in base al titolare.
Modificare la playlist
Una sessione multimediale può modificare direttamente la playlist del relativo player come spiegato nella
guida di ExoPlayer per le playlist.
I controller possono anche modificare la playlist se
COMMAND_SET_MEDIA_ITEM
o COMMAND_CHANGE_MEDIA_ITEMS
sono disponibili per il controller.
Quando aggiungi nuovi elementi alla playlist, il player in genere richiede istanze MediaItem
con un
URI definito
per renderli riproducibili. Per impostazione predefinita, gli elementi appena aggiunti vengono inoltrati automaticamente
ai metodi del player come player.addMediaItem
, se hanno un URI definito.
Se vuoi personalizzare le istanze MediaItem
aggiunte al player, puoi eseguire l'override di onAddMediaItems()
.
Questo passaggio è necessario se vuoi supportare i controller che richiedono contenuti multimediali senza un URI definito. In genere, MediaItem
ha uno o più dei seguenti campi impostati per descrivere l'elemento multimediale richiesto:
MediaItem.id
: un ID generico che identifica i contenuti multimediali.MediaItem.RequestMetadata.mediaUri
: un URI della richiesta che può utilizzare uno schema personalizzato e non è necessariamente riproducibile direttamente dal player.MediaItem.RequestMetadata.searchQuery
: una query di ricerca testuale, ad esempio dall'Assistente Google.MediaItem.MediaMetadata
: metadati strutturati come "title" o "artist".
Per ulteriori opzioni di personalizzazione per playlist completamente nuove, puoi
anche eseguire l'override dell'opzione
onSetMediaItems()
che ti consente di definire l'elemento iniziale e la posizione nella playlist. Ad esempio,
puoi espandere un singolo elemento richiesto in un'intera playlist e indicare al
player di iniziare dall'indice dell'elemento richiesto inizialmente. Un'implementazione di esempio di onSetMediaItems()
con questa funzionalità è disponibile nell'app demo della sessione.
Gestire il layout e i comandi personalizzati
Le seguenti sezioni descrivono come pubblicizzare un layout personalizzato di pulsanti di comando personalizzati alle app client e autorizzare i controller a inviare comandi personalizzati.
Definisci il layout personalizzato della sessione
Per indicare alle app client i controlli di riproduzione che vuoi mostrare all'utente, imposta il layout personalizzato della sessione durante la creazione di MediaSession
nel metodo onCreate()
del tuo servizio.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
Dichiara i comandi personalizzati e del player disponibili
Le applicazioni multimediali possono definire comandi personalizzati che, ad esempio, possono essere utilizzati in un layout personalizzato. Ad esempio, potresti voler implementare pulsanti che consentano all'utente di salvare un elemento multimediale in un elenco di elementi preferiti. MediaController
invia comandi personalizzati e il MediaSession.Callback
li riceve.
Puoi definire quali comandi di sessione personalizzata sono disponibili per un MediaController
quando si connette alla sessione multimediale. Puoi farlo eseguendo l'override di MediaSession.Callback.onConnect()
. Configura e restituisci il set di comandi disponibili quando accetti una richiesta di connessione da un MediaController
nel metodo di callback onConnect
:
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
Per ricevere richieste di comandi personalizzati da un MediaController
, sostituisci il metodo onCustomCommand()
in Callback
.
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
Puoi monitorare quale controller multimediale sta effettuando una richiesta utilizzando la proprietà packageName
dell'oggetto MediaSession.ControllerInfo
passato ai metodi Callback
. Ciò ti consente di personalizzare il comportamento della tua app in risposta a un determinato comando se proviene dal sistema, dalla tua app o da altre app client.
Aggiornare il layout personalizzato dopo un'interazione dell'utente
Dopo aver gestito un comando personalizzato o qualsiasi altra interazione con il player, potrebbe essere opportuno aggiornare il layout visualizzato nell'interfaccia utente del controller. Un esempio tipico è un pulsante di attivazione/disattivazione che cambia la propria icona dopo aver attivato l'azione associata a questo pulsante. Per aggiornare il layout, puoi utilizzare
MediaSession.setCustomLayout
:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
Personalizza il comportamento del comando di riproduzione
Per personalizzare il comportamento di un comando definito nell'interfaccia Player
, ad esempio
play()
o seekToNext()
, aggrega il tuo Player
in un elemento ForwardingPlayer
.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Per ulteriori informazioni su ForwardingPlayer
, consulta la guida di ExoPlayer sulla
Personalizzazione.
Identifica il controller che richiede un comando del player
Quando una chiamata a un metodo Player
proviene da un MediaController
, puoi
identificare l'origine di origine con MediaSession.controllerForCurrentRequest
e acquisire ControllerInfo
per la richiesta attuale:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
Rispondi ai pulsanti multimediali
I pulsanti multimediali sono pulsanti hardware presenti su dispositivi Android e altri dispositivi periferici, ad esempio i pulsanti di riproduzione/pausa sulle cuffie Bluetooth. Media3 gestisce gli eventi del pulsante multimediale per te quando arrivano alla sessione e chiama il metodo Player
appropriato sul player della sessione.
Un'app può eseguire l'override del comportamento predefinito eseguendo l'override di MediaSession.Callback.onMediaButtonEvent(Intent)
. In tal caso, l'app può/deve gestire autonomamente tutte le specifiche dell'API.