Pierwsze kroki z CastPlayer

CastPlayer to implementacja odtwarzacza Jetpack Media3, która obsługuje zarówno odtwarzanie lokalne, jak i przesyłanie na zdalne urządzenie obsługujące Cast. CastPlayer ułatwia dodawanie funkcji przesyłania do aplikacji i udostępnia zaawansowane funkcje umożliwiające płynne przełączanie się między odtwarzaniem lokalnym a zdalnym. Z tego przewodnika dowiesz się, jak zintegrować CastPlayer z aplikacją do multimediów.

Aby zintegrować Cast z innymi platformami, zapoznaj się z pakietem Cast SDK.

Uzyskiwanie urządzenia obsługującego Cast

Aby przetestować CastPlayer, potrzebujesz urządzenia obsługującego Cast. Może to być Android TV, Chromecast, głośnik inteligentny lub inteligentny wyświetlacz. Sprawdź, czy urządzenie jest skonfigurowane i połączone z tą samą siecią Wi-Fi co urządzenie mobilne, na którym prowadzisz testy.

Dodawanie zależności kompilacji

Aby zacząć korzystać z CastPlayer, dodaj zależności AndroidX Media3 i CastPlayer do pliku build.gradle modułu aplikacji.

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")

Dynamiczny

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"

Konfigurowanie CastPlayer

Aby skonfigurować CastPlayer, zaktualizuj plik AndroidManifest.xml o dostawcę opcji.

Dostawca opcji

CastPlayer wymaga dostawcy opcji, aby skonfigurować jego działanie. W przypadku a podstawowej konfiguracji możesz użyć DefaultCastOptionsProvider, dodając go do swojego AndroidManifest.xml pliku. Spowoduje to użycie ustawień domyślnych, w tym domyślnej aplikacji odbiornika.

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

Aby dostosować konfigurację, zaimplementuj własny OptionsProvider. Więcej informacji znajdziesz w przewodniku CastOptions.

Dodawanie odbiornika do przesyłania multimediów

Dodanie MediaTransferReceiver do pliku manifestu umożliwia interfejsowi systemowemu wykrywanie urządzeń obsługujących Cast w sieci i przekierowywanie multimediów bez otwierania aktywności aplikacji. Użytkownik może na przykład zmienić urządzenie odtwarzające multimedia z aplikacji w powiadomieniu o multimediach.

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

Tworzenie CastPlayer

W przypadku odtwarzania zdalnego za pomocą Cast aplikacja powinna być w stanie zarządzać odtwarzaniem nawet wtedy, gdy użytkownik nie wchodzi w interakcję z aktywnością z aplikacji, np. za pomocą systemowego powiadomienia o multimediach. Z tego powodu instancje ExoPlayer (do odtwarzania lokalnego) i CastPlayer (do odtwarzania zdalnego) należy utworzyć w usłudze, takiej jak MediaSessionService lub MediaLibraryService. Najpierw utwórz instancję ExoPlayer, a potem, podczas tworzenia instancji CastPlayer, ustaw ExoPlayer jako lokalną instancję odtwarzacza. Następnie możesz przełączać odtwarzanie multimediów między urządzeniem mobilnym a urządzeniem obsługującym Cast z poziomu powiadomienia o multimediach lub powiadomienia na ekranie blokady. Media3 używa funkcji Przełącznik wyjścia do obsługi przenoszenia odtwarzacza, gdy trasa wyjścia zmienia się z lokalnej na zdalną lub ze zdalnej na lokalną.

Zrzut ekranu przedstawiający interfejs przełącznika wyjścia w powiadomieniach.
Rysunek 1. (a) Chip urządzenia w powiadomieniu o multimediach, (b) urządzenia obsługujące Cast wyświetlane po kliknięciu chipa urządzenia, (c) chip urządzenia w powiadomieniu na ekranie blokady.

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

Dodawanie elementów interfejsu

Dodaj MediaRouteButton do interfejsu aplikacji. Kliknięcie MediaRouteButton otwiera okno z listą dostępnych w sieci urządzeń obsługujących Cast. Gdy użytkownik wybierze urządzenie, odtwarzanie multimediów zostanie przeniesione z urządzenia mobilnego na wybrane urządzenie odbiornika. W tej sekcji dowiesz się, jak dodać przycisk i nasłuchiwać zdarzeń, aby aktualizować interfejs, gdy odtwarzanie przełącza się między urządzeniami lokalnymi i zdalnymi.

Ustawianie MediaRouteButton

MediaRouteButton możesz dodać do interfejsu aktywności na 4 sposoby. Najlepszy wybór zależy od projektu i wymagań aplikacji.

  • Interfejs Compose: dodaj przycisk composable.
  • Interfejs widoków:
    • Dodaj przycisk do menu paska aplikacji.
    • Dodaj przycisk w PlayerView.
    • Dodaj przycisk jako standardowy View.
Zrzut ekranu przedstawiający przycisk MediaRouteButton w interfejsie.
Rysunek 2. (a) MediaRouteButton na pasku menu, (b) jako widok, (c) w PlayerView i (d) okno urządzeń obsługujących Cast.

Dodawanie composable MediaRouteButton do odtwarzacza

Do interfejsu odtwarzacza możesz dodać composable MediaRouteButton. Więcej informacji znajdziesz w przewodniku po Compose.

@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) {
  // ...
}

Dodawanie MediaRouteButton do PlayerView

MediaRouteButton możesz dodać bezpośrednio w elementach sterujących interfejsu PlayerView's UI. Po ustawieniu MediaController jako odtwarzacza dla PlayerView podaj MediaRouteButtonViewProvider, aby wyświetlić przycisk Cast w odtwarzaczu.

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

Dodawanie MediaRouteButton do menu paska aplikacji

Aby skonfigurować MediaRouteButton w menu paska aplikacji, utwórz menu XML i zastąp onCreateOptionsMenu w 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;
}

Dodawanie MediaRouteButton jako widoku

MediaRouteButton możesz skonfigurować w pliku układu activity.xml.

  <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" />

Aby dokończyć konfigurację MediaRouteButton, użyj Media3 Cast MediaRouteButtonFactory w kodzie Activity.

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

Detektor aktywności

Utwórz Player.Listener w Activity, aby nasłuchiwać zmian miejsca odtwarzania multimediów. Gdy playbackType zmieni się z PLAYBACK_TYPE_LOCAL na PLAYBACK_TYPE_REMOTE lub odwrotnie, możesz dostosować interfejs. Aby zapobiec wyciekom pamięci i ograniczyć aktywność detektora tylko do czasu, gdy aplikacja jest widoczna, zarejestruj detektor w onStart i wyrejestruj go w onStop:

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

Więcej informacji o nasłuchiwaniu zdarzeń odtwarzania i reagowaniu na nie znajdziesz w przewodniku po zdarzeniach odtwarzacza.