Wiedergabe mit einer MediaSession steuern und bewerben

Mediensitzungen bieten eine universelle Möglichkeit, mit einem Audio- oder Videoplayer zu interagieren. In Media3 ist der Standardplayer die Klasse ExoPlayer, die die Schnittstelle Player implementiert. Durch die Verbindung der Mediensitzung mit dem Player kann eine App die Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen.

Befehle können von physischen Tasten stammen, z. B. von der Wiedergabetaste auf einem Headset oder einer TV-Fernbedienung. Sie können auch von Client-Apps mit einem Media-Controller stammen, z. B. wenn Google Assistant angewiesen wird, die Wiedergabe zu pausieren. In der Mediensitzung werden diese Befehle an den Player der Media-App delegiert.

Wann sollte eine Mediensitzung verwendet werden?

Wenn Sie MediaSession implementieren, können Nutzer die Wiedergabe steuern:

  • Über ihre Kopfhörer. Oft gibt es Tasten oder Touch-Interaktionen, die Nutzer auf ihren Kopfhörern ausführen können, um Medien wiederzugeben oder zu pausieren oder zum nächsten oder vorherigen Titel zu wechseln.
  • Mit Google Assistant sprechen Ein häufiges Muster ist, „OK Google, pausiere“ zu sagen, um die Wiedergabe von Medien zu pausieren, die gerade auf dem Gerät wiedergegeben werden.
  • Über ihre Wear OS-Smartwatch. So können Nutzer während der Wiedergabe auf dem Smartphone einfacher auf die gängigsten Wiedergabesteuerelemente zugreifen.
  • Über die Mediensteuerung. In diesem Karussell werden Steuerelemente für jede laufende Mediensitzung angezeigt.
  • Auf TV Ermöglicht Aktionen mit physischen Wiedergabetasten, die Steuerung der Plattformwiedergabe und die Energieverwaltung (z. B. wenn der Fernseher, die Soundbar oder der A/V‑Receiver ausgeschaltet oder der Eingang gewechselt wird, sollte die Wiedergabe in der App beendet werden).
  • Über die Android Auto-Mediensteuerung. So kann die Wiedergabe während der Fahrt sicher gesteuert werden.
  • Und alle anderen externen Prozesse, die die Wiedergabe beeinflussen müssen.

Das ist für viele Anwendungsfälle ideal. Insbesondere sollten Sie MediaSession in folgenden Fällen in Betracht ziehen:

  • Du streamst Videoinhalte im Langformat, z. B. Filme oder Live-TV.
  • Du streamst Audioinhalte im Langformat, z. B. Podcasts oder Musik-Playlists.
  • Sie entwickeln eine TV-App.

Allerdings sind nicht alle Anwendungsfälle für die MediaSession geeignet. Möglicherweise möchten Sie nur die Player in den folgenden Fällen verwenden:

  • Sie zeigen Kurzinhalte, für die keine externe Steuerung oder Hintergrundwiedergabe erforderlich ist.
  • Es gibt kein einzelnes aktives Video, z. B. wenn der Nutzer durch eine Liste scrollt und mehrere Videos gleichzeitig auf dem Bildschirm angezeigt werden.
  • Sie spielen ein einmaliges Einführungs- oder Erklärvideo ab, das sich der Nutzer aktiv ansehen soll, ohne dass externe Wiedergabesteuerelemente erforderlich sind.
  • Ihre Inhalte sind datenschutzrelevant und Sie möchten nicht, dass externe Prozesse auf die Media-Metadaten zugreifen (z. B. im Inkognitomodus eines Browsers).

Wenn Ihr Anwendungsfall nicht zu den oben aufgeführten passt, sollten Sie überlegen, ob Sie damit einverstanden sind, dass die Wiedergabe in Ihrer App fortgesetzt wird, wenn der Nutzer nicht aktiv mit den Inhalten interagiert. Wenn die Antwort „Ja“ lautet, sollten Sie wahrscheinlich MediaSession auswählen. Wenn die Antwort „Nein“ lautet, sollten Sie wahrscheinlich stattdessen Player verwenden.

Mediensitzung erstellen

Eine Mediensitzung wird zusammen mit dem Player ausgeführt, den sie verwaltet. Sie können eine Mediensitzung mit einem Context- und einem Player-Objekt erstellen. Sie sollten eine Media-Sitzung erstellen und initialisieren, wenn sie benötigt wird, z. B. in der onStart()- oder onResume()-Lifecycle-Methode von Activity oder Fragment oder in der onCreate()-Methode von Service, die die Media-Sitzung und den zugehörigen Player enthält.

Um eine Mediensitzung zu erstellen, initialisieren Sie ein Player und übergeben Sie es so an MediaSession.Builder:

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();

Automatische Statusverwaltung

Die Media3-Bibliothek aktualisiert die Mediensitzung automatisch anhand des Status des Players. Sie müssen die Zuordnung von Spieler zu Sitzung also nicht manuell verarbeiten.

Das unterscheidet sich von der Media-Sitzung der Plattform, bei der Sie unabhängig vom Player selbst ein PlaybackState erstellen und verwalten mussten, um beispielsweise Fehler anzugeben.

Eindeutige Sitzungs-ID

Standardmäßig erstellt MediaSession.Builder eine Sitzung mit einem leeren String als Sitzungs-ID. Das ist ausreichend, wenn eine App nur eine einzelne Sitzungsinstanz erstellen soll. Das ist der häufigste Fall.

Wenn eine App mehrere Sitzungsinstanzen gleichzeitig verwalten möchte, muss sie dafür sorgen, dass die Sitzungs-ID jeder Sitzung eindeutig ist. Die Sitzungs-ID kann beim Erstellen der Sitzung mit MediaSession.Builder.setId(String id) festgelegt werden.

Wenn IllegalStateException Ihre App mit der Fehlermeldung IllegalStateException: Session ID must be unique. ID= zum Absturz bringt, wurde wahrscheinlich eine Sitzung unerwartet erstellt, bevor eine zuvor erstellte Instanz mit derselben ID freigegeben wurde. Um zu verhindern, dass Sitzungen durch einen Programmierfehler offengelegt werden, werden solche Fälle erkannt und durch Auslösen einer Ausnahme gemeldet.

Anderen Kunden die Kontrolle überlassen

Die Mediensitzung ist der Schlüssel zur Steuerung der Wiedergabe. So können Sie Befehle von externen Quellen an den Player weiterleiten, der Ihre Medien wiedergibt. Dabei kann es sich um physische Tasten wie die Wiedergabetaste auf einem Headset oder einer TV-Fernbedienung oder um indirekte Befehle wie „Pause“ für Google Assistant handeln. Möglicherweise möchten Sie auch Zugriff auf das Android-System gewähren, um Benachrichtigungen und Steuerelemente auf dem Sperrbildschirm zu ermöglichen, oder auf eine Wear OS-Smartwatch, damit Sie die Wiedergabe über das Zifferblatt steuern können. Externe Clients können mit einem Media-Controller Wiedergabebefehle an Ihre Media-App senden. Diese werden von Ihrer Media-Session empfangen, die Befehle letztendlich an den Mediaplayer weiterleitet.

Ein Diagramm, das die Interaktion zwischen einer MediaSession und einem MediaController zeigt.
Abbildung 1: Der Media-Controller ermöglicht die Übergabe von Befehlen von externen Quellen an die Media-Session.
.

Wenn ein Controller eine Verbindung zu Ihrer Mediensitzung herstellt, wird die Methode onConnect() aufgerufen. Anhand der bereitgestellten ControllerInfo können Sie entscheiden, ob Sie die Anfrage annehmen oder ablehnen. Ein Beispiel für das Annehmen einer Verbindungsanfrage finden Sie im Abschnitt Benutzerdefinierte Befehle deklarieren.

Nachdem eine Verbindung hergestellt wurde, kann ein Controller Wiedergabebefehle an die Sitzung senden. Die Sitzung delegiert diese Befehle dann an den Player. Wiedergabe- und Playlist-Befehle, die in der Player-Schnittstelle definiert sind, werden automatisch von der Sitzung verarbeitet.

Mit anderen Callback-Methoden können Sie beispielsweise Anfragen für benutzerdefinierte Befehle und Änderungen an der Playlist verarbeiten. Diese Callbacks enthalten ebenfalls ein ControllerInfo-Objekt, sodass Sie die Reaktion auf jede Anfrage controllerbasiert anpassen können.

Playlist bearbeiten

In einer Media-Sitzung kann die Playlist des Players direkt geändert werden, wie im ExoPlayer-Leitfaden für Playlists beschrieben. Controller können die Playlist auch ändern, wenn entweder COMMAND_SET_MEDIA_ITEM oder COMMAND_CHANGE_MEDIA_ITEMS für den Controller verfügbar ist.

Wenn Sie der Playlist neue Elemente hinzufügen, benötigt der Player in der Regel MediaItem Instanzen mit einem definierten URI, damit sie wiedergegeben werden können. Standardmäßig werden neu hinzugefügte Elemente automatisch an Player-Methoden wie player.addMediaItem weitergeleitet, wenn sie einen URI haben.

Wenn Sie die dem Player hinzugefügten MediaItem-Instanzen anpassen möchten, können Sie onAddMediaItems() überschreiben. Dieser Schritt ist erforderlich, wenn Sie Controller unterstützen möchten, die Medien ohne definierten URI anfordern. Stattdessen sind im MediaItem in der Regel eines oder mehrere der folgenden Felder festgelegt, um die angeforderten Media zu beschreiben:

  • MediaItem.id: Eine generische ID zur Identifizierung der Media.
  • MediaItem.RequestMetadata.mediaUri: Ein Anfrage-URI, der ein benutzerdefiniertes Schema verwenden kann und nicht unbedingt direkt vom Player wiedergegeben werden kann.
  • MediaItem.RequestMetadata.searchQuery: Eine textbasierte Suchanfrage, z. B. von Google Assistant.
  • MediaItem.MediaMetadata: Strukturierte Metadaten wie „Titel“ oder „Künstler“.

Wenn Sie weitere Anpassungsoptionen für völlig neue Playlists benötigen, können Sie zusätzlich onSetMediaItems() überschreiben, um das Startelement und die Position in der Playlist zu definieren. So kannst du beispielsweise einen einzelnen angeforderten Titel zu einer ganzen Playlist erweitern und den Player anweisen, mit dem Index des ursprünglich angeforderten Titels zu beginnen. Eine Beispielimplementierung von onSetMediaItems() mit dieser Funktion finden Sie in der Demo-App für Sitzungen.

Einstellungen für die Schaltfläche „Medien“ verwalten

Jeder Controller, z. B. System-UI, Android Auto oder Wear OS, kann selbst entscheiden, welche Schaltflächen dem Nutzer angezeigt werden. Wenn Sie angeben möchten, welche Wiedergabesteuerelemente dem Nutzer angezeigt werden sollen, können Sie im MediaSession Einstellungen für Medientasten festlegen. Diese Einstellungen bestehen aus einer geordneten Liste von CommandButton-Instanzen, die jeweils eine Einstellung für eine Schaltfläche in der Benutzeroberfläche definieren.

Befehlsschaltflächen definieren

CommandButton-Instanzen werden verwendet, um Einstellungen für Medientasten zu definieren. Jede Schaltfläche definiert drei Aspekte des gewünschten UI-Elements:

  1. Das Symbol, das die visuelle Darstellung definiert. Das Symbol muss beim Erstellen eines CommandButton.Builder auf eine der vordefinierten Konstanten festgelegt werden. Dabei handelt es sich nicht um eine tatsächliche Bitmap- oder Bildressource. Eine generische Konstante hilft Controllern, eine geeignete Ressource für ein einheitliches Erscheinungsbild in der eigenen Benutzeroberfläche auszuwählen. Wenn keine der vordefinierten Symbolkonstanten für Ihren Anwendungsfall geeignet ist, können Sie stattdessen setCustomIconResId verwenden.
  2. Der Befehl, der die Aktion definiert, die ausgelöst wird, wenn der Nutzer mit der Schaltfläche interagiert. Sie können setPlayerCommand für eine Player.Command oder setSessionCommand für eine vordefinierte oder benutzerdefinierte SessionCommand verwenden.
  3. Der Slot definiert, wo die Schaltfläche in der Controller-Benutzeroberfläche platziert werden soll. Dieses Feld ist optional und wird automatisch anhand von Symbol und Befehl festgelegt. So kann beispielsweise festgelegt werden, dass eine Schaltfläche im Navigationsbereich „Vorwärts“ der Benutzeroberfläche anstelle des Standardbereichs „Überlauf“ angezeigt werden soll.

Kotlin

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

Java

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

Wenn die Einstellungen für die Media-Schaltfläche aufgelöst werden, wird der folgende Algorithmus angewendet:

  1. Platzieren Sie für jedes CommandButton in den Einstellungen für Media-Schaltflächen die Schaltfläche im ersten verfügbaren und zulässigen Slot.
  2. Wenn einer der zentralen, Vorwärts- und Rückwärts-Slots nicht mit einer Schaltfläche gefüllt ist, fügen Sie Standardschaltflächen für diesen Slot hinzu.

Mit CommandButton.DisplayConstraints können Sie eine Vorschau davon generieren, wie die Einstellungen für die Media-Schaltfläche je nach Einschränkungen der Benutzeroberfläche aufgelöst werden.

Einstellungen für die Schaltfläche „Medien“ festlegen

Am einfachsten ist es, die Einstellungen für die Media-Schaltfläche beim Erstellen von MediaSession zu definieren. Alternativ können Sie MediaSession.Callback.onConnect überschreiben, um die Einstellungen für die Medientaste für jeden verbundenen Controller anzupassen.

Kotlin

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

Java

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

Einstellungen für die Media-Schaltfläche nach einer Nutzerinteraktion aktualisieren

Nachdem Sie eine Interaktion mit dem Player verarbeitet haben, möchten Sie möglicherweise die in der Controller-Benutzeroberfläche angezeigten Schaltflächen aktualisieren. Ein typisches Beispiel ist eine Umschaltfläche, deren Symbol und Aktion sich ändern, nachdem die mit dieser Schaltfläche verknüpfte Aktion ausgelöst wurde. Wenn du die Einstellungen für die Medientaste aktualisieren möchtest, kannst du MediaSession.setMediaButtonPreferences verwenden, um die Einstellungen für alle Controller oder einen bestimmten Controller zu aktualisieren:

Kotlin

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

Java

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

Benutzerdefinierte Befehle hinzufügen und Standardverhalten anpassen

Die verfügbaren Player-Befehle können durch benutzerdefinierte Befehle erweitert werden. Außerdem ist es möglich, eingehende Player-Befehle und Media-Schaltflächen abzufangen, um das Standardverhalten zu ändern.

Benutzerdefinierte Befehle deklarieren und verarbeiten

Media-Apps können benutzerdefinierte Befehle definieren, die beispielsweise in den Einstellungen für die Media-Schaltfläche verwendet werden können. Sie können beispielsweise Schaltflächen implementieren, mit denen der Nutzer ein Media-Element in einer Liste mit Favoriten speichern kann. Mit MediaController werden benutzerdefinierte Befehle gesendet und mit MediaSession.Callback werden sie empfangen.

Wenn Sie benutzerdefinierte Befehle definieren möchten, müssen Sie MediaSession.Callback.onConnect() überschreiben, um die verfügbaren benutzerdefinierten Befehle für jeden verbundenen Controller festzulegen.

Kotlin

private 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();
  }
}

Wenn Sie benutzerdefinierte Befehlsanfragen von einem MediaController erhalten möchten, überschreiben Sie die Methode onCustomCommand() im Callback.

Kotlin

private 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)
      );
    }
    ...
  }
}

Sie können nachvollziehen, welcher Media-Controller eine Anfrage stellt, indem Sie die packageName-Property des MediaSession.ControllerInfo-Objekts verwenden, das an Callback-Methoden übergeben wird. So können Sie das Verhalten Ihrer App anpassen, wenn ein bestimmter Befehl vom System, von Ihrer eigenen App oder von anderen Client-Apps stammt.

Standardmäßige Playerbefehle anpassen

Alle Standardbefehle und die Statusverwaltung werden an die Player auf der MediaSession delegiert. Wenn Sie das Verhalten eines Befehls anpassen möchten, der in der Player-Schnittstelle definiert ist, z. B. play() oder seekToNext(), umschließen Sie Ihre Player mit einem ForwardingSimpleBasePlayer, bevor Sie sie an MediaSession übergeben:

Kotlin

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

Java

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

Weitere Informationen zu ForwardingSimpleBasePlayer finden Sie im ExoPlayer-Leitfaden unter Anpassung.

Anfordernden Controller eines Player-Befehls ermitteln

Wenn ein Aufruf einer Player-Methode von einem MediaController stammt, können Sie die Quelle mit MediaSession.controllerForCurrentRequest identifizieren und die ControllerInfo für die aktuelle Anfrage abrufen:

Kotlin

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

Java

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

Umgang mit Medientasten anpassen

Media-Schaltflächen sind Hardware-Schaltflächen auf Android-Geräten und anderen Peripheriegeräten, z. B. die Wiedergabe-/Pausetaste auf einem Bluetooth-Headset. Media3 verarbeitet Media-Schaltflächenereignisse für dich, wenn sie in der Sitzung eintreffen, und ruft die entsprechende Player-Methode für den Sitzungsplayer auf.

Es wird empfohlen, alle eingehenden Media-Schaltflächenereignisse in der entsprechenden Player-Methode zu verarbeiten. Bei komplexeren Anwendungsfällen können die Ereignisse der Medientaste in MediaSession.Callback.onMediaButtonEvent(Intent) abgefangen werden.

Fehlerbehandlung und ‑berichte

Es gibt zwei Arten von Fehlern, die von einer Sitzung ausgegeben und an Controller gemeldet werden. Schwerwiegende Fehler weisen auf einen technischen Wiedergabefehler des Sitzungsplayers hin, der die Wiedergabe unterbricht. Schwerwiegende Fehler werden dem Controller automatisch gemeldet, wenn sie auftreten. Nicht schwerwiegende Fehler sind nicht technischer Natur oder Richtlinienfehler, die die Wiedergabe nicht unterbrechen und manuell von der Anwendung an Controller gesendet werden.

Schwerwiegende Wiedergabefehler

Ein schwerwiegender Wiedergabefehler wird vom Player an die Sitzung und dann an die Controller gemeldet, um Player.Listener.onPlayerError(PlaybackException) und Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) aufzurufen.

In diesem Fall wird der Wiedergabestatus in STATE_IDLE geändert und MediaController.getPlaybackError() gibt den PlaybackException zurück, der die Änderung verursacht hat. Ein Controller kann PlayerException.errorCode prüfen, um Informationen zum Grund für den Fehler zu erhalten.

Zur Interoperabilität wird ein schwerwiegender Fehler in der Plattformsitzung repliziert, indem der Status in STATE_ERROR geändert und der Fehlercode und die Meldung gemäß PlaybackException festgelegt werden.

Schwerwiegende Fehler anpassen

Um dem Nutzer lokalisierte und aussagekräftige Informationen zu geben, können der Fehlercode, die Fehlermeldung und die zusätzlichen Fehlerinformationen eines schwerwiegenden Wiedergabefehlers mithilfe von ForwardingPlayer beim Erstellen der Sitzung angepasst werden:

Kotlin

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

Java

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

Der Weiterleitungsplayer kann ForwardingSimpleBasePlayer verwenden, um den Fehler abzufangen und den Fehlercode, die Meldung oder die Extras anzupassen. Auf dieselbe Weise können Sie auch neue Fehler generieren, die im ursprünglichen Player nicht vorhanden sind:

Kotlin

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

Java

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

Nicht schwerwiegende Fehler

Nicht schwerwiegende Fehler, die nicht auf einer technischen Ausnahme beruhen, können von einer App an alle oder an einen bestimmten Controller gesendet werden:

Kotlin

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

Java

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

Wenn ein nicht schwerwiegender Fehler an den Media Notification Controller gesendet wird, werden der Fehlercode und die Fehlermeldung in die Media-Sitzung der Plattform repliziert, während PlaybackState.state nicht in STATE_ERROR geändert wird.

Nicht schwerwiegende Fehler erhalten

Ein MediaController erhält einen nicht schwerwiegenden Fehler durch die Implementierung von MediaController.Listener.onError:

Kotlin

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

Java

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });