Présentation de MediaRouter

Pour utiliser le framework MediaRouter dans votre application, vous devez obtenir une instance de l'objet MediaRouter et associer un objet MediaRouter.Callback pour écouter les événements de routage. Le contenu envoyé via un routage multimédia passe par le MediaRouteProvider associé à la route (sauf dans quelques cas particuliers, tels qu'un périphérique de sortie Bluetooth). La figure 1 présente une vue d'ensemble des classes utilisées pour acheminer du contenu entre des appareils.

Figure 1 : Présentation des principales classes de routeur multimédia utilisées par les applications

Remarque:Si vous souhaitez que votre application soit compatible avec les appareils Google Cast, vous devez utiliser le SDK Cast et compiler votre application en tant qu'expéditeur Cast. Suivez les instructions de la documentation Cast au lieu d'utiliser directement le framework MediaRouter.

Bouton de routage multimédia

Les applications Android doivent utiliser un bouton de routage multimédia pour contrôler le routage multimédia. Le framework MediaRouter fournit une interface standard pour le bouton, qui aide les utilisateurs à reconnaître et à utiliser le routage lorsqu'il est disponible. Le bouton de routage multimédia est généralement placé sur le côté droit de la barre d'action de votre application, comme illustré dans la figure 2.

Figure 2. Bouton de routage multimédia dans la barre d'action.

Lorsque l'utilisateur appuie sur le bouton de routage multimédia, les itinéraires multimédias disponibles s'affichent dans une liste, comme illustré dans la figure 3.

Figure 3. Liste des itinéraires multimédias disponibles, qui s'affiche lorsque vous appuyez sur le bouton de routage multimédia.

Pour créer un bouton de routage multimédia, procédez comme suit:

  1. Utiliser une AppCompatActivity
  2. Définir l'élément de menu du bouton de routage multimédia
  3. Créer un MediaRouteSélecteur
  4. Ajouter le bouton de routage multimédia à la barre d'action
  5. Créer et gérer les méthodes MediaRouter.Callback dans le cycle de vie de votre activité

Cette section décrit les quatre premières étapes. La section suivante décrit les méthodes de rappel.

Utiliser une AppCompatActivity

Lorsque vous utilisez le framework du routeur multimédia dans une activité, vous devez étendre l'activité à partir de AppCompatActivity et importer le package androidx.appcompat.app. Vous devez ajouter les bibliothèques d'assistance androidx.appcompat:appcompat et androidx.mediarouter:mediarouter à votre projet de développement d'applications. Pour en savoir plus sur l'ajout de bibliothèques Support à votre projet, consultez Premiers pas avec Android Jetpack.

Attention:Veillez à utiliser l'implémentation androidx du framework du routeur multimédia. N'utilisez pas l'ancien package android.media.

Créez un fichier XML qui définit un élément de menu pour le bouton de routage multimédia. L'action de l'élément doit être la classe MediaRouteActionProvider. Voici un exemple de fichier:

// 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="androidx.mediarouter.app.MediaRouteActionProvider"
        app:showAsAction="always"
    />
</menu>

Créer un MediaRouteSélecteur

Les itinéraires qui s'affichent dans le menu du bouton de routage multimédia sont déterminés par un MediaRouteSelector. Étendez votre activité à partir de AppCompatActivity et créez le sélecteur lorsque l'activité est créée, en appelant MediaRouteSelector.Builder à partir de la méthode onCreate(), comme indiqué dans l'exemple de code suivant. Notez que le sélecteur est enregistré dans une variable de classe et que les types de routes autorisés sont spécifiés en ajoutant des objets 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();
    }
}

Pour la plupart des applications, le seul type de route nécessaire est CATEGORY_REMOTE_PLAYBACK. Ce type de route traite l'appareil exécutant votre application comme une télécommande. Le récepteur connecté gère l'ensemble des opérations de récupération, de décodage et de lecture des données de contenu. C'est ainsi que fonctionnent les applications compatibles avec Google Cast, telles que Chromecast.

Certains fabricants proposent une option de routage spéciale appelée "sortie secondaire". Avec ce routage, votre application multimédia récupère, affiche et diffuse des vidéos ou de la musique en streaming directement sur l'écran et/ou les haut-parleurs du récepteur distant sélectionné. Utilisez une sortie secondaire pour envoyer du contenu à des systèmes musicaux ou des écrans vidéo compatibles sans fil. Pour activer la découverte et la sélection de ces appareils, vous devez ajouter les catégories de commandes CATEGORY_LIVE_AUDIO ou CATEGORY_LIVE_VIDEO à MediaRouteSélecteur. Vous devez également créer et gérer votre propre boîte de dialogue Presentation.

Ajouter le bouton de routage multimédia à la barre d'action

Maintenant que le menu de routage multimédia et MediaRouteSelect sont définis, vous pouvez ajouter le bouton de routage multimédia à une activité. Remplacez la méthode onCreateOptionsMenu() pour chacune de vos activités afin d'ajouter un menu d'options.

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

Pour découvrir comment implémenter la barre d'action dans votre application, consultez le guide du développeur Barre d'action.

Vous pouvez également ajouter un bouton de routage multimédia en tant que MediaRouteButton dans n'importe quelle vue. Vous devez associer un MediaRouteViewer au bouton à l'aide de la méthode setRouteSelector(). Consultez la checklist de conception Google Cast pour savoir comment intégrer le bouton de routage multimédia dans votre application.

Rappels MediaRouter

Toutes les applications exécutées sur le même appareil partagent une seule instance MediaRouter et ses routes (filtrées par application à l'aide du MediaRouteSélecteur de l'application). Chaque activité communique avec MediaRouter à l'aide de sa propre implémentation de méthodes MediaRouter.Callback. MediaRouter appelle les méthodes de rappel chaque fois que l'utilisateur sélectionne, modifie ou déconnecte un itinéraire.

Vous pouvez ignorer plusieurs méthodes dans le rappel afin de recevoir des informations sur les événements de routage. L'implémentation de la classe MediaRouter.Callback doit au minimum remplacer onRouteSelected() et onRouteUnselected().

Comme MediaRouter est une ressource partagée, votre application doit gérer ses rappels MediaRouter en réponse aux rappels de cycle de vie d'une activité habituels:

  • Lorsque l'activité est créée (onCreate(Bundle)), récupérez un pointeur vers MediaRouter et conservez-le pendant toute la durée de vie de l'application.
  • Associez des rappels à MediaRouter lorsque l'activité devient visible (onStart()), puis dissociez-les lorsqu'elle est masquée (onStop()).

L'exemple de code suivant montre comment créer et enregistrer l'objet de rappel, comment obtenir une instance de MediaRouter et comment gérer les rappels. Notez l'utilisation de l'option CALLBACK_FLAG_REQUEST_DISCOVERY lorsque vous associez les rappels dans onStart(). Cela permet à MediaRouteViewer d'actualiser la liste des routes disponibles pour le bouton de routage multimédia.

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

Le framework du routeur multimédia fournit également une classe MediaRouteDiscoveryFragment, qui se charge d'ajouter et de supprimer le rappel d'une activité.

Remarque:Si vous écrivez une application de lecture de musique et que vous souhaitez qu'elle lise de la musique en arrière-plan, vous devez créer un Service pour la lecture et appeler le framework du routeur multimédia à partir des rappels de cycle de vie du service.

Contrôler un itinéraire de lecture à distance

Lorsque vous sélectionnez un itinéraire de lecture à distance, votre application fait office de télécommande. L'appareil situé à l'autre extrémité de l'itinéraire gère toutes les fonctions de récupération, de décodage et de lecture des données de contenu. Les commandes de l'interface utilisateur de votre application communiquent avec l'appareil récepteur à l'aide d'un objet RemotePlaybackClient.

La classe RemotePlaybackClient fournit des méthodes supplémentaires pour gérer la lecture de contenu. Voici quelques-unes des méthodes de lecture clés de la classe RemotePlaybackClient:

  • play() : lit un fichier multimédia spécifique, spécifié par un élément Uri.
  • pause() : suspend la piste multimédia en cours de lecture.
  • resume() : poursuit la lecture du titre en cours après une commande de pause.
  • seek() : passez à une position spécifique dans la piste actuelle.
  • release() : annule la connexion de votre application à l'appareil de lecture à distance.

Vous pouvez utiliser ces méthodes pour associer des actions aux commandes de lecture que vous fournissez dans votre application. La plupart de ces méthodes vous permettent également d'inclure un objet de rappel afin de surveiller la progression de la tâche de lecture ou de la requête de commande.

La classe RemotePlaybackClient prend également en charge la mise en file d'attente de plusieurs éléments multimédias pour la lecture et la gestion de la file d'attente.

Exemple de code

Les exemples Android BasicMediaRouter et MediaRouter illustrent davantage l'utilisation de l'API MediaRouter.