Erste Schritte mit CastPlayer

Der CastPlayer ist eine Jetpack Media3 Player-Implementierung, die sowohl die lokale Wiedergabe als auch das Streaming auf ein für Google Cast optimiertes Remote-Gerät unterstützt. CastPlayer vereinfacht das Hinzufügen von Cast-Funktionen zu deiner App und bietet umfangreiche Funktionen, um nahtlos zwischen lokaler und Remote-Wiedergabe zu wechseln. In dieser Anleitung erfährst du, wie du CastPlayer in deine Media-App einbindest.

Informationen zum Einbinden von Cast in andere Plattformen findest du im Cast SDK.

Gerät für Google Cast optimiert besorgen

Zum Testen von CastPlayer benötigst du ein für Google Cast optimiertes Gerät. Zur Auswahl stehen Android TV, Chromecast, Smart Speaker und Smart Displays. Achte darauf, dass dein Gerät eingerichtet und mit demselben WLAN wie dein mobiles Entwicklungsgerät verbunden ist, damit es erkannt werden kann.

Build-Abhängigkeiten hinzufügen

Wenn du CastPlayer verwenden möchtest, füge die AndroidX Media3- und CastPlayer-Abhängigkeiten der Datei build.gradle deines App-Moduls hinzu.

Kotlin

implementation("androidx.media3:media3-exoplayer:1.10.0")
implementation("androidx.media3:media3-ui:1.10.0")
implementation("androidx.media3:media3-session:1.10.0")
implementation("androidx.media3:media3-cast:1.10.0")

Groovy

implementation "androidx.media3:media3-exoplayer:1.10.0"
implementation "androidx.media3:media3-ui:1.10.0"
implementation "androidx.media3:media3-session:1.10.0"
implementation "androidx.media3:media3-cast:1.10.0"

CastPlayer konfigurieren

Um den CastPlayer zu konfigurieren, aktualisiere die Datei AndroidManifest.xml mit einem Optionsanbieter.

Optionsanbieter

Der CastPlayer benötigt einen Optionsanbieter, um sein Verhalten zu konfigurieren. Für eine einfache Einrichtung kannst du den DefaultCastOptionsProvider verwenden, indem du ihn der Datei AndroidManifest.xml hinzufügst. Dabei werden Standardeinstellungen verwendet, einschließlich der Standardempfängeranwendung.

<application>
  ...
  <meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
  ...
</application>

Wenn du die Konfiguration anpassen möchtest, implementiere einen eigenen benutzerdefinierten OptionsProvider. Weitere Informationen findest du in der Anleitung zu CastOptions.

Empfänger für Medienübertragungen hinzufügen

Wenn du deinem Manifest einen MediaTransferReceiver hinzufügst, kann die System-UI für Google Cast optimierte Geräte im Netzwerk erkennen und Medien umleiten, ohne die App-Aktivität zu öffnen. Ein Nutzer kann beispielsweise das Gerät, auf dem die Medien deiner App wiedergegeben werden, über die Medienbenachrichtigung ändern.

<application>
  ...
  <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver" />
  ...
</application>

CastPlayer erstellen

Für die Remote-Wiedergabe mit Cast muss deine App die Wiedergabe auch dann verwalten können, wenn der Nutzer nicht mit einer Activity deiner App interagiert, z. B. über die System-Medienbenachrichtigung. Aus diesem Grund solltest du deine ExoPlayer (für die lokale Wiedergabe) und CastPlayer (für die Remote-Wiedergabe) Instanzen in einem Dienst wie MediaSessionService oder MediaLibraryService erstellen. Erstelle zuerst deine ExoPlayer-Instanz und lege dann beim Erstellen deiner CastPlayer-Instanz ExoPlayer als lokale Player-Instanz fest. Anschließend kannst du die Medienwiedergabe über die Medienbenachrichtigung oder die Benachrichtigung auf dem Sperrbildschirm zwischen deinem Mobilgerät und dem für Google Cast optimierten Gerät wechseln. Media3 verwendet die Funktion Ausgabewechsel , um Player-Übertragungen zu verarbeiten, wenn sich die Ausgaberoute von lokal zu remote oder von remote zu lokal ändert.

Screenshot der Benutzeroberfläche für die Ausgabewahl in Benachrichtigungen
Abbildung 1: (a) Geräte-Chip in der Medienbenachrichtigung, (b) für Google Cast optimierte Geräte, die beim Tippen auf den Geräte-Chip angezeigt werden, (c) Geräte-Chip in der Benachrichtigung auf dem Sperrbildschirm

Kotlin

override fun onCreate() {
  super.onCreate()

  val exoPlayer = ExoPlayer.Builder(context).build()
  val castPlayer = CastPlayer.Builder(context).setLocalPlayer(exoPlayer).build()

  mediaSession = MediaSession.Builder(context, castPlayer).build()
}

Java

@Override
public void onCreate() {
  super.onCreate();

  ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build();
  CastPlayer castPlayer = new CastPlayer.Builder(context).setLocalPlayer(exoPlayer).build();

  mediaSession =
      new MediaSession.Builder(/* context= */ context, /* player= */ castPlayer).build();
}

UI-Elemente hinzufügen

Füge der UI deiner App einen MediaRouteButton hinzu. Wenn du auf den MediaRouteButton tippst, wird ein Dialogfeld mit einer Liste der verfügbaren für Google Cast optimierten Geräte im Netzwerk geöffnet. Wenn der Nutzer ein Gerät auswählt, wird die Medienwiedergabe vom Mobilgerät auf das ausgewählte Empfängergerät übertragen. In diesem Abschnitt erfährst du, wie du die Schaltfläche hinzufügst und auf Ereignisse wartest, um die UI zu aktualisieren, wenn die Wiedergabe zwischen lokalen und Remote-Geräten wechselt.

MediaRouteButton festlegen

Es gibt vier Möglichkeiten, den MediaRouteButton der UI deiner Aktivität hinzuzufügen. Die beste Wahl hängt vom Design und den Anforderungen deiner App ab.

  • Compose-UI: Füge eine Schaltfläche hinzu.
  • Views-UI:
    • Füge die Schaltfläche dem App-Leistenmenü hinzu.
    • Füge die Schaltfläche in PlayerView ein.
    • Füge die Schaltfläche als Standard-View hinzu.
Screenshot mit der Schaltfläche „MediaRouteButton“ auf der Benutzeroberfläche.
Abbildung 2: (a) MediaRouteButton in der Menüleiste, (b) als View, (c) in PlayerView und (d) Dialogfeld mit für Google Cast optimierten Geräten.

Composable MediaRouteButton zum Player hinzufügen

Du kannst den MediaRouteButton Composable der UI deines Players hinzufügen. Weitere Informationen findest du in der Compose-Anleitung.

@Composable
fun PlayerComposeView(player: Player, modifier: Modifier = Modifier) {
  var controlsVisible by remember { mutableStateOf(false) }

  Box(
    modifier = modifier.clickable { controlsVisible = true },
    contentAlignment = Alignment.Center,
  ) {
    PlayerSurface(player = player, modifier = modifier)
    AnimatedVisibility(visible = controlsVisible, enter = fadeIn(), exit = fadeOut()) {
      Box(modifier = Modifier.fillMaxSize()) {
        MediaRouteButton(modifier = Modifier.align(Alignment.TopEnd))
        PrimaryControls(player = player, modifier = Modifier.align(Alignment.Center))
      }
    }
  }
}

@Composable
fun PrimaryControls(player: Player, modifier: Modifier = Modifier) {
  // ...
}

MediaRouteButton zu PlayerView hinzufügen

Du kannst den MediaRouteButton direkt in den PlayerView's UI Steuerelementen hinzufügen. Nachdem du MediaController als Player für deine PlayerView festgelegt hast, gib einen MediaRouteButtonViewProvider an, um die Cast Schaltfläche auf dem Player anzuzeigen.

Kotlin

override fun onStart() {
  super.onStart()

  playerView.player = mediaController
  playerView.setMediaRouteButtonViewProvider(MediaRouteButtonViewProvider())
}

Java

@Override
public void onStart() {
  super.onStart();

  playerView.setPlayer(mediaController);
  playerView.setMediaRouteButtonViewProvider(new MediaRouteButtonViewProvider());
}

MediaRouteButton dem App-Leistenmenü hinzufügen

Um ein MediaRouteButton im App-Leistenmenü einzurichten, erstelle ein XML-Menü und überschreibe onCreateOptionsMenu in deiner Activity.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto">
  <item android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:showAsAction="always"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
</menu>

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
  // ...
  menuInflater.inflate(R.menu.sample_media_route_button_menu, menu)
  val menuItemFuture: ListenableFuture<MenuItem> =
    MediaRouteButtonFactory.setUpMediaRouteButton(context, menu, R.id.media_route_menu_item)
  Futures.addCallback(
    menuItemFuture,
    object : FutureCallback<MenuItem> {
      override fun onSuccess(menuItem: MenuItem?) {
        // Do something with the menu item.
      }

      override fun onFailure(t: Throwable) {
        // Handle the failure.
      }
    },
    executor,
  )
  // ...
  return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  // ...
  getMenuInflater().inflate(R.menu.sample_media_route_button_menu, menu);
  ListenableFuture<MenuItem> menuItemFuture =
      MediaRouteButtonFactory.setUpMediaRouteButton(context, menu, R.id.media_route_menu_item);
  Futures.addCallback(
      menuItemFuture,
      new FutureCallback<MenuItem>() {
        @Override
        public void onSuccess(MenuItem menuItem) {
          // Do something with the menu item.
        }

        @Override
        public void onFailure(Throwable t) {
          // Handle the failure.
        }
      },
      executor);
  // ...
  return true;
}

MediaRouteButton als View hinzufügen

Du kannst einen MediaRouteButton in der Datei layout.xml deiner Aktivität einrichten.

  <androidx.mediarouter.app.MediaRouteButton
      android:id="@+id/media_route_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:mediaRouteButtonTint="@android:color/white" />

Verwende zum Abschließen der Einrichtung für den MediaRouteButton die Media3 Cast MediaRouteButtonFactory in deinem Activity-Code.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  findViewById<MediaRouteButton>(R.id.media_route_button)?.also {
    val unused = MediaRouteButtonFactory.setUpMediaRouteButton(context, it)
  }
}

Java

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  // ...
  MediaRouteButton button = findViewById(R.id.media_route_button);
  ListenableFuture<Void> setUpFuture =
      MediaRouteButtonFactory.setUpMediaRouteButton(context, button);
}

Activity-Listener

Erstelle in deiner Activity einen Player.Listener, um auf Änderungen am Wiedergabeort zu warten. Wenn sich playbackType zwischen PLAYBACK_TYPE_LOCAL und PLAYBACK_TYPE_REMOTE ändert, kannst du die UI nach Bedarf anpassen. Um Speicherlecks zu vermeiden und die Listener-Aktivität nur auf den Zeitraum zu beschränken, in dem deine App sichtbar ist, registriere den Listener in onStart und hebe die Registrierung in onStop auf:

Kotlin

private val playerListener: Player.Listener =
  object : Player.Listener {
    override fun onDeviceInfoChanged(deviceInfo: DeviceInfo) {
      if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
        // Add UI changes for local playback.
      } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
        // Add UI changes for remote playback.
      }
    }
  }

override fun onStart() {
  super.onStart()
  mediaController.addListener(playerListener)
}

override fun onStop() {
  super.onStop()
  mediaController.removeListener(playerListener)
}

Java

private final Player.Listener playerListener =
    new Player.Listener() {
      @Override
      public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
        if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
          // Add UI changes for local playback.
        } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
          // Add UI changes for remote playback.
        }
      }
    };

@Override
protected void onStart() {
  super.onStart();
  mediaController.addListener(playerListener);
}

@Override
protected void onStop() {
  super.onStop();
  mediaController.removeListener(playerListener);
}

Weitere Informationen zum Warten auf Wiedergabeereignisse und zum Reagieren darauf findest du in der Anleitung zu Player-Ereignissen.