Cómo comenzar a usar CastPlayer

El CastPlayer es una implementación de Jetpack Media3 Player que admite la reproducción local y la transmisión a un dispositivo remoto compatible con Cast. CastPlayer simplifica la adición de la funcionalidad de Cast a tu app y proporciona funciones enriquecidas para cambiar sin problemas entre la reproducción local y la remota. En esta guía, se muestra cómo integrar CastPlayer en tu app de contenido multimedia.

Para integrar Cast con otras plataformas, consulta el SDK de Cast.

Obtén un dispositivo compatible con Cast

Para probar CastPlayer, necesitas un dispositivo compatible con Cast. Las opciones incluyen Android TV, Chromecast, bocinas inteligentes y pantallas inteligentes. Verifica que tu dispositivo esté configurado y conectado a la misma red Wi-Fi que tu dispositivo móvil de desarrollo para el descubrimiento.

Cómo agregar dependencias de compilación

Para comenzar a usar CastPlayer, agrega las dependencias de AndroidX Media3 y CastPlayer al archivo build.gradle de tu módulo de app.

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"

Configura tu CastPlayer

Para configurar CastPlayer, actualiza tu archivo AndroidManifest.xml con un proveedor de opciones.

Proveedor de opciones

El CastPlayer requiere un proveedor de opciones para configurar su comportamiento. Para una configuración básica, puedes usar DefaultCastOptionsProvider agregándolo a tu AndroidManifest.xml archivo. Esto usa la configuración predeterminada, incluida la aplicación receptora predeterminada.

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

Para personalizar la configuración, implementa tu propio OptionsProvider personalizado. Consulta la guía de CastOptions para obtener más información.

Agrega un receptor para las transferencias de contenido multimedia

Si agregas un MediaTransferReceiver a tu manifiesto, se habilita la IU del sistema para descubrir dispositivos compatibles con Cast en la red y redireccionar el contenido multimedia sin abrir la actividad de la app. Por ejemplo, un usuario puede cambiar el dispositivo que reproduce el contenido multimedia de tu app desde la notificación de contenido multimedia.

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

Compila un CastPlayer

Para la reproducción remota con Cast, tu app debe poder administrar la reproducción incluso cuando el usuario no interactúa con una Activity de tu app, por ejemplo, a través de la notificación de contenido multimedia del sistema. Por este motivo, debes crear tus ExoPlayer (para la reproducción local) y CastPlayer (para la reproducción remota) instancias en un servicio, como MediaSessionService o MediaLibraryService. Primero, crea tu instancia ExoPlayer y, luego, cuando compiles tu CastPlayer instancia, establece ExoPlayer como la instancia del reproductor local. Luego, puedes cambiar la reproducción de contenido multimedia entre tu dispositivo móvil y el dispositivo compatible con Cast desde la notificación de contenido multimedia o la notificación de la pantalla de bloqueo. Media3 usa la función Output Switcher para controlar las transferencias del reproductor cuando la ruta de salida cambia de local a remota o de remota a local.

Captura de pantalla que muestra la IU del Selector de salida en las notificaciones.
Figura 1: (a) Chip del dispositivo en la notificación de contenido multimedia (b) Dispositivos compatibles con Cast que se muestran cuando se presiona el chip del dispositivo (c) Chip del dispositivo en la notificación de la pantalla de bloqueo

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

Cómo agregar elementos de IU

Agrega un MediaRouteButton a la IU de tu app. Si presionas MediaRouteButton , se abrirá un diálogo que muestra una lista de los dispositivos compatibles con Cast disponibles en la red. Cuando el usuario selecciona un dispositivo, la reproducción de contenido multimedia se transfiere del dispositivo móvil al dispositivo receptor seleccionado. En esta sección, se muestra cómo agregar el botón y escuchar eventos para actualizar la IU cuando la reproducción cambia entre dispositivos locales y remotos.

Configura el MediaRouteButton

Hay cuatro formas de agregar el MediaRouteButton a la IU de tu actividad. La mejor opción depende del diseño y los requisitos de tu app.

  • IU de Compose: Agrega un botón componible.
  • IU de Views:
    • Agrega el botón al menú de la barra de la app.
    • Agrega el botón dentro de PlayerView.
    • Agrega el botón como un View estándar.
Captura de pantalla en la que se muestra el botón MediaRouteButton en la IU.
Figura 2: (a) MediaRouteButton en la barra de menú, (b) como una vista, (c) en PlayerView y (d) diálogo de dispositivos compatibles con Cast.

Agrega un MediaRouteButton componible al reproductor

Puedes agregar el MediaRouteButton componible a la IU de tu reproductor. Para obtener más información, consulta la guía de 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) {
  // ...
}

Agrega el MediaRouteButton a PlayerView

Puedes agregar el MediaRouteButton directamente dentro de los controles de la IU de PlayerView. Después de configurar el MediaController como el reproductor de tu PlayerView, proporciona un MediaRouteButtonViewProvider para mostrar el botón de Cast en el reproductor.

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

Agrega el MediaRouteButton al menú de la barra de la app

Para configurar un MediaRouteButton en el menú de la barra de la app, crea un menú XML y anula onCreateOptionsMenu en tu 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;
}

Agrega el MediaRouteButton como una vista

Puedes configurar un MediaRouteButton en tu diseño de actividad.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" />

Para completar la configuración de MediaRouteButton, usa el Media3 Cast MediaRouteButtonFactory en tu Activity código.

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

Crea un Player.Listener en tu Activity para escuchar los cambios en la ubicación de reproducción de contenido multimedia. Cuando el playbackType cambia entre PLAYBACK_TYPE_LOCAL y PLAYBACK_TYPE_REMOTE, puedes ajustar la IU según sea necesario. Para evitar pérdidas de memoria y limitar la actividad del objeto de escucha solo cuando la app está visible, registra el objeto de escucha en onStart y anula su registro en 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);
}

Para obtener más información sobre cómo escuchar y responder a eventos de reproducción, consulta la guía de eventos del reproductor.