Descripción general de MediaRouter

Para usar el marco de trabajo de MediaRouter en tu app, debes obtener una instancia del objeto MediaRouter y adjuntar un objeto MediaRouter.Callback para escuchar los eventos de enrutamiento. El contenido enviado a través de una ruta de contenido multimedia pasa por el MediaRouteProvider asociado a la ruta (excepto en algunos casos especiales, como un dispositivo de salida Bluetooth). En la figura 1, se incluye una vista de alto nivel de las clases utilizadas para enrutar contenido entre dispositivos.

Figura 1: Descripción general de las clases clave de routers de contenido multimedia que utilizan las apps

Nota: Si deseas que tu app sea compatible con dispositivos Google Cast, deberías usar el SDK de Cast y compilar tu app como dispositivo de envío de Cast. Sigue las instrucciones incluidas en la documentación de Cast, en lugar de usar directamente el marco de trabajo de MediaRouter.

El botón de ruta de contenido multimedia

Las apps para Android deben usar un botón de ruta de contenido multimedia para controlar el enrutamiento de este tipo de contenido. El marco de trabajo de MediaRouter proporciona una interfaz estándar para el botón, que ayuda a los usuarios a reconocer y usar el enrutamiento cuando está disponible. Por lo general, el botón de ruta de contenido multimedia se coloca en el lado derecho de la barra de acciones de la app, como se muestra en la figura 2.

Figura 2: Botón de ruta de contenido multimedia en la barra de acciones

Cuando el usuario presiona el botón de ruta de contenido multimedia, las rutas disponibles aparecen en una lista, como se muestra en la figura 3.

Figura 3: Una lista de las rutas de contenido multimedia disponibles, que se muestra después de presionar el botón de ruta de contenido multimedia

Sigue estos pasos para crear un botón de ruta de contenido multimedia:

  1. Usa una AppCompatActivity.
  2. Define el elemento de menú del botón de la ruta de contenido multimedia.
  3. Crea un MediaRouteSelector.
  4. Agrega el botón de ruta de contenido multimedia a la barra de acciones.
  5. Crea y administra los métodos de MediaRouter.Callback en el ciclo de vida de tu actividad.

En esta sección, se describen los primeros cuatro pasos. En la siguiente sección, se describen los métodos de devolución de llamada.

Usa una AppCompatActivity

Cuando uses el marco de trabajo del router de contenido multimedia en una actividad, deberás extender la actividad desde AppCompatActivity e importar el paquete android.support.v7.media. Debes agregar las bibliotecas de compatibilidad v7-appcompat y v7-mediarouter al proyecto de desarrollo de tu app. Para obtener más información sobre cómo agregar bibliotecas de compatibilidad a tu proyecto, consulta Configuración de la biblioteca de compatibilidad.

Precaución: Asegúrate de utilizar la implementación android.support.v7.media del marco de trabajo del router de contenido multimedia. No uses el paquete android.media más antiguo.

Crea un archivo xml en el que se defina un elemento de menú para el botón de la ruta de contenido multimedia. La acción del elemento debería ser de la clase MediaRouteActionProvider. A continuación, se muestra un archivo de ejemplo:

    // myMediaRouteButtonMenuItem.xml
    <?xml version="1.0" encoding="utf-8"?>
    <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:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
            app:showAsAction="always"
        />
    </menu>
    

Crea un MediaRouteSelector

Las rutas que aparecen en el menú del botón de la ruta de contenido multimedia están determinadas por un MediaRouteSelector. Extiende tu actividad desde AppCompatActivity y compila el selector cuando se cree la actividad llamando MediaRouteSelector.Builder desde el método onCreate(), como se muestra en el siguiente ejemplo de código. Ten en cuenta que se guarda el selector en una variable de clase, y se especifican los tipos de ruta permitidos agregando objetos MediaControlIntent:

Kotlin

    class MediaRouterPlaybackActivity : AppCompatActivity() {

        private var mSelector: MediaRouteSelector? = null

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            // Create a route selector for the type of routes your app supports.
            mSelector = MediaRouteSelector.Builder()
                    // These are the framework-supported intents
                    .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                    .build()
        }
    }
    

Java

    public class MediaRouterPlaybackActivity extends AppCompatActivity {
        private MediaRouteSelector mSelector;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            // Create a route selector for the type of routes your app supports.
            mSelector = new MediaRouteSelector.Builder()
                    // These are the framework-supported intents
                    .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                    .build();
        }
    }
    

Para la mayoría de las apps, el único tipo de ruta necesario es CATEGORY_REMOTE_PLAYBACK. Este tipo de ruta trata el dispositivo que ejecuta tu app como un control remoto. El dispositivo receptor conectado controla toda la recuperación, la decodificación y la reproducción de datos de contenido. Así funcionan las apps compatibles con Google Cast, como Chromecast.

Algunos fabricantes ofrecen compatibilidad con una opción de enrutamiento especial denominada "salida secundaria". Con este tipo de enrutamiento, tu app de contenido multimedia recupera, procesa y transmite video o música directo a la pantalla y/o las bocinas del dispositivo receptor remoto que se seleccionó. Usa la salida secundaria para enviar contenido a sistemas de música o pantallas de video inalámbricos. Para habilitar la detección y selección de estos dispositivos, debes agregar las categorías de control CATEGORY_LIVE_AUDIO o CATEGORY_LIVE_VIDEO al MediaRouteSelector. Además, debes crear y controlar tu propio diálogo de Presentation.

Agrega el botón de ruta de contenido multimedia a la barra de acciones

Si ya definiste el menú de ruta de contenido multimedia y MediaRouteSelector, puedes agregar el botón de ruta de contenido multimedia a una actividad. Anula el método onCreateOptionsMenu() para cada una de tus actividades y agrega un menú de opciones.

Kotlin

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)

        // Inflate the menu and configure the media router action provider.
        menuInflater.inflate(R.menu.sample_media_router_menu, menu)

        // Attach the MediaRouteSelector to the menu item
        val mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item)
        val mediaRouteActionProvider =
                MenuItemCompat.getActionProvider(mediaRouteMenuItem) as MediaRouteActionProvider

        // Attach the MediaRouteSelector that you built in onCreate()
        selector?.also(mediaRouteActionProvider::setRouteSelector)

        // Return true to show the menu.
        return true
    }
    

Java

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        // Inflate the menu and configure the media router action provider.
        getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);

        // Attach the MediaRouteSelector to the menu item
        MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
        MediaRouteActionProvider mediaRouteActionProvider =
                (MediaRouteActionProvider)MenuItemCompat.getActionProvider(
                mediaRouteMenuItem);
        // Attach the MediaRouteSelector that you built in onCreate()
        mediaRouteActionProvider.setRouteSelector(selector);

        // Return true to show the menu.
        return true;
    }
    

Para obtener más información sobre la implementación de la barra de acciones en tu app, consulta la guía para desarrolladores de la Barra de acciones.

También puedes agregar un botón de ruta de contenido multimedia como MediaRouteButton en cualquier vista. Debes adjuntar un MediaRouteSelector al botón usando el método setRouteSelector(). Consulta la Lista de tareas de diseño de Google Cast para obtener pautas sobre cómo incorporar en tu app el botón de la ruta de contenido multimedia.

Devoluciones de llamada de MediaRouter

Todas las apps que se ejecutan en el mismo dispositivo comparten una sola instancia de MediaRouter y sus rutas (que el MediaRouteSelector filtra por app). Cada actividad se comunica con el MediaRouter mediante su propia implementación de métodos MediaRouter.Callback. El MediaRouter llama a los métodos de devolución de llamada cada vez que el usuario selecciona, cambia o desconecta una ruta.

En la devolución de llamada, hay varios métodos que puedes anular para recibir información sobre eventos de enrutamiento. Como mínimo, la implementación de la clase MediaRouter.Callback debe anular a onRouteSelected() y onRouteUnselected().

Debido a que el MediaRouter es un recurso compartido, tu app debe administrar las devoluciones de llamada de MediaRouter en respuesta a las devoluciones de llamada habituales del ciclo de vida:

  • Cuando se crea la actividad (onCreate(Bundle)), toma un puntero al MediaRouter y mantenlo durante todo el ciclo de vida de la app.
  • Adjunta devoluciones de llamada a MediaRouter cuando la actividad se vuelva visible (onStart()), y sepáralas cuando esté oculta (onStop()).

En el siguiente ejemplo de código, se muestra cómo crear y guardar el objeto de devolución de llamada, cómo obtener una instancia de MediaRouter y cómo administrar devoluciones de llamada. Ten en cuenta el uso de la marca CALLBACK_FLAG_REQUEST_DISCOVERY cuando adjuntes las devoluciones de llamada en onStart(). Esto permite que tu MediaRouteSelector actualice la lista de rutas disponibles del botón de ruta de contenido multimedia.

Kotlin

    class MediaRouterPlaybackActivity : AppCompatActivity() {

        private var mediaRouter: MediaRouter? = null
        private var mSelector: MediaRouteSelector? = null

        // Variables to hold the currently selected route and its playback client
        private var mRoute: MediaRouter.RouteInfo? = null
        private var remotePlaybackClient: RemotePlaybackClient? = null

        // Define the Callback object and its methods, save the object in a class variable
        private val mediaRouterCallback = object : MediaRouter.Callback() {

            override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) {
                Log.d(TAG, "onRouteSelected: route=$route")
                if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
                    // Stop local playback (if necessary)
                    // ...

                    // Save the new route
                    mRoute = route

                    // Attach a new playback client
                    remotePlaybackClient =
                        RemotePlaybackClient(this@MediaRouterPlaybackActivity, mRoute)

                    // Start remote playback (if necessary)
                    // ...
                }
            }

            override fun onRouteUnselected(
                    router: MediaRouter,
                    route: MediaRouter.RouteInfo,
                    reason: Int
            ) {
                Log.d(TAG, "onRouteUnselected: route=$route")
                if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {

                    // Changed route: tear down previous client
                    mRoute?.also {
                        remotePlaybackClient?.release()
                        remotePlaybackClient = null
                    }

                    // Save the new route
                    mRoute = route

                    when (reason) {
                        MediaRouter.UNSELECT_REASON_ROUTE_CHANGED -> {
                            // Resume local playback (if necessary)
                            // ...
                        }
                    }
                }
            }
        }

        // Retain a pointer to the MediaRouter
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            // Get the media router service.
            mediaRouter = MediaRouter.getInstance(this)
            ...
        }

        // Use this callback to run your MediaRouteSelector to generate the
        // list of available media routes
        override fun onStart() {
            mSelector?.also { selector ->
                mediaRouter?.addCallback(selector, mediaRouterCallback,
                        MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY)
            }
            super.onStart()
        }

        // Remove the selector on stop to tell the media router that it no longer
        // needs to discover routes for your app.
        override fun onStop() {
            mediaRouter?.removeCallback(mediaRouterCallback)
            super.onStop()
        }
        ...
    }
    

Java

    public class MediaRouterPlaybackActivity extends AppCompatActivity {
        private MediaRouter mediaRouter;
        private MediaRouteSelector mSelector;

        // Variables to hold the currently selected route and its playback client
        private MediaRouter.RouteInfo mRoute;
        private RemotePlaybackClient remotePlaybackClient;

        // Define the Callback object and its methods, save the object in a class variable
        private final MediaRouter.Callback mediaRouterCallback =
                new MediaRouter.Callback() {

            @Override
            public void onRouteSelected(MediaRouter router, RouteInfo route) {
                Log.d(TAG, "onRouteSelected: route=" + route);

                if (route.supportsControlCategory(
                    MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){
                    // Stop local playback (if necessary)
                    // ...

                    // Save the new route
                    mRoute = route;

                    // Attach a new playback client
                    remotePlaybackClient = new RemotePlaybackClient(this, mRoute);

                    // Start remote playback (if necessary)
                    // ...
                }
            }

            @Override
            public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) {
                Log.d(TAG, "onRouteUnselected: route=" + route);

                if (route.supportsControlCategory(
                    MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){

                    // Changed route: tear down previous client
                    if (mRoute != null && remotePlaybackClient != null) {
                        remotePlaybackClient.release();
                        remotePlaybackClient = null;
                    }

                    // Save the new route
                    mRoute = route;

                    if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
                        // Resume local playback  (if necessary)
                        // ...
                    }
                }
            }
        }

        // Retain a pointer to the MediaRouter
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            // Get the media router service.
            mediaRouter = MediaRouter.getInstance(this);
            ...
        }

        // Use this callback to run your MediaRouteSelector to generate the list of available media routes
        @Override
        public void onStart() {
            mediaRouter.addCallback(mSelector, mediaRouterCallback,
                    MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
            super.onStart();
        }

        // Remove the selector on stop to tell the media router that it no longer
        // needs to discover routes for your app.
        @Override
        public void onStop() {
            mediaRouter.removeCallback(mediaRouterCallback);
            super.onStop();
        }
        ...
    }
    

El marco de trabajo del router de contenido multimedia también proporciona una clase MediaRouteDiscoveryFragment, que se encarga de agregar y quitar la devolución de llamada de una actividad.

Nota: Si estás creando una app de reproducción de música y deseas que reproduzca música mientras está en segundo plano, debes compilar un Service para reproducción y llamar al marco de trabajo del router de contenido multimedia desde las devoluciones de llamada del ciclo de vida del servicio.

Cómo controlar una ruta de reproducción remota

Cuando seleccionas una ruta de reproducción remota, tu app funciona como un control remoto. El dispositivo del otro extremo de la ruta maneja todas las funciones de recuperación, decodificación y reproducción de datos de contenido. Los controles de la IU de tu app se comunican con el dispositivo receptor mediante un objeto RemotePlaybackClient.

La clase RemotePlaybackClient proporciona métodos adicionales para administrar la reproducción de contenido. A continuación, se incluyen algunos de los métodos de reproducción clave de la clase RemotePlaybackClient:

  • play(): Reproduce un archivo multimedia específico, determinado por un Uri.
  • pause(): Pausa la pista de contenido multimedia que se está reproduciendo.
  • resume(): Continúa reproduciendo la pista actual después de un comando de pausa.
  • seek(): Se desplaza a una posición específica en la pista actual.
  • release(): Elimina la conexión entre la app y el dispositivo de reproducción remota.

Puedes usar estos métodos para adjuntar acciones a los controles de reproducción que proporcionas en tu app. La mayoría de estos métodos también te permiten incluir un objeto de devolución de llamada para poder supervisar el progreso de la tarea de reproducción o la solicitud de control.

Además, la clase RemotePlaybackClient admite colas de varios elementos multimedia para reproducción y administración de la cola de contenido multimedia.

Ejemplo de código

Los ejemplos de Android BasicMediaRouter y MediaRouter demuestran aún más el uso de la API de MediaRouter.