Présentation de MediaRouteProvider

Le framework de routeur multimédia Android permet aux fabricants d'activer la lecture sur leurs appareils via une interface standardisée appelée MediaRouteProvider. Un fournisseur d'itinéraires définit une interface commune pour lire des contenus multimédias sur un appareil récepteur, ce qui permet de lire des contenus multimédias sur votre équipement à partir de n'importe quelle application Android compatible avec les routes multimédias.

Ce guide explique comment créer un fournisseur de routage multimédia pour un appareil récepteur et le rendre disponible pour d'autres applications de lecture de contenus multimédias exécutées sur Android. Pour utiliser cette API, vous devez connaître les classes de clés MediaRouteProvider, MediaRouteProviderDescriptor et RouteController.

Présentation

Le framework de routeur multimédia Android permet aux développeurs d'applications multimédias et aux fabricants d'appareils de lecture de contenus multimédias de se connecter via une API et une interface utilisateur communes. Les développeurs d'applications qui implémentent une interface MediaRouter peuvent ensuite se connecter au framework et lire du contenu sur les appareils qui participent au framework du routeur multimédia. Les fabricants d'appareils de lecture de contenus multimédias peuvent participer au framework en publiant un MediaRouteProvider qui permet à d'autres applications de se connecter aux appareils récepteurs et de les lire. La figure 1 montre comment une application se connecte à un appareil récepteur via le framework de routeur multimédia.

Figure 1 : Présentation de la manière dont les classes de fournisseur de routages multimédias fournissent la communication entre une application multimédia et un appareil récepteur.

Lorsque vous créez un fournisseur de routage multimédia pour votre appareil récepteur, il assure les fonctions suivantes:

  • Décrivez et publiez les fonctionnalités de l'appareil récepteur afin que d'autres applications puissent le découvrir et utiliser ses fonctionnalités de lecture.
  • Encapsulez l'interface de programmation du dispositif récepteur et ses mécanismes de transport de communication pour rendre l'appareil compatible avec le framework du routeur multimédia.

Répartition des fournisseurs d'itinéraires

Un fournisseur de routage multimédia est distribué dans une application Android. Il peut être mis à la disposition d'autres applications en étendant MediaRouteProviderService ou en encapsulant votre implémentation de MediaRouteProvider avec votre propre service et en déclarant un filtre d'intent pour le fournisseur de routage multimédia. Ces étapes permettent à d'autres applications de découvrir et d'utiliser votre route multimédia.

Remarque:L'application contenant le fournisseur de routage multimédia peut également inclure une interface MediaRouter au fournisseur de routage, mais ce n'est pas obligatoire.

Bibliothèque Support MediaRouter

Les API de routeur multimédia sont définies dans la bibliothèque MediaRouter AndroidX. Vous devez ajouter cette bibliothèque à votre projet de développement d'application. Pour en savoir plus sur l'ajout de bibliothèques Support à votre projet, consultez Configuration de la bibliothèque Support.

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

Créer un service de fournisseur

Le framework du routeur multimédia doit pouvoir détecter votre fournisseur de routage multimédia et s'y connecter pour permettre à d'autres applications d'utiliser votre itinéraire. Pour ce faire, le framework du routeur multimédia recherche les applications qui déclarent une action d'intent du fournisseur de routages multimédias. Lorsqu'une autre application souhaite se connecter à votre fournisseur, le framework doit pouvoir l'appeler et s'y connecter. Votre fournisseur doit donc être encapsulé dans un Service.

L'exemple de code suivant montre la déclaration d'un service de fournisseur de routage multimédia et le filtre d'intent dans un fichier manifeste, ce qui permet au framework du routeur multimédia de le découvrir et de l'utiliser:

<service android:name=".provider.SampleMediaRouteProviderService"
    android:label="@string/sample_media_route_provider_service"
    android:process=":mrp">
    <intent-filter>
        <action android:name="android.media.MediaRouteProviderService" />
    </intent-filter>
</service>

Cet exemple de fichier manifeste déclare un service qui encapsule les classes réelles du fournisseur de routages multimédias. Le framework du routeur multimédia Android fournit la classe MediaRouteProviderService à utiliser comme wrapper de service pour les fournisseurs de routages multimédias. L'exemple de code suivant montre comment utiliser cette classe wrapper:

Kotlin

class SampleMediaRouteProviderService : MediaRouteProviderService() {

    override fun onCreateMediaRouteProvider(): MediaRouteProvider {
        return SampleMediaRouteProvider(this)
    }
}

Java

public class SampleMediaRouteProviderService extends MediaRouteProviderService {

    @Override
    public MediaRouteProvider onCreateMediaRouteProvider() {
        return new SampleMediaRouteProvider(this);
    }
}

Spécifier des fonctionnalités de route

Les applications qui se connectent au framework de routeur multimédia peuvent découvrir votre route multimédia via les déclarations de fichier manifeste de votre application, mais ont également besoin de connaître les fonctionnalités des routes multimédias que vous fournissez. Les routes multimédias peuvent être de différents types et avoir des fonctionnalités différentes. D'autres applications doivent être en mesure de découvrir ces détails pour déterminer si elles sont compatibles avec votre itinéraire.

Le framework du routeur multimédia vous permet de définir et de publier les fonctionnalités de votre routage multimédia via des objets IntentFilter, MediaRouteDescriptor et MediaRouteProviderDescriptor. Cette section explique comment utiliser ces classes afin de publier les détails de votre routage multimédia pour d'autres applications.

Catégories d'itinéraires

Dans la description programmatique de votre fournisseur de routage média, vous devez spécifier s'il accepte la lecture à distance, la sortie secondaire ou les deux. Voici les catégories de routes fournies par le framework de routeur multimédia:

  • CATEGORY_LIVE_AUDIO : sortie audio sur un périphérique de sortie secondaire, tel qu'un système musical sans fil.
  • CATEGORY_LIVE_VIDEO : sortie vidéo sur un périphérique de sortie secondaire, tel que des périphériques d'affichage sans fil.
  • CATEGORY_REMOTE_PLAYBACK : lit des vidéos ou des fichiers audio sur un appareil distinct qui gère la récupération, le décodage et la lecture de contenus multimédias, comme les appareils Chromecast.

Pour inclure ces paramètres dans une description de votre routage multimédia, insérez-les dans un objet IntentFilter, que vous ajouterez ultérieurement à un objet MediaRouteDescriptor:

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    companion object {
        private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = IntentFilter().run {
            addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
            arrayListOf(this)
        }
    }
}

Java

public final class SampleMediaRouteProvider extends MediaRouteProvider {
    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
    static {
        IntentFilter videoPlayback = new IntentFilter();
        videoPlayback.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
        CONTROL_FILTERS_BASIC.add(videoPlayback);
    }
}

Si vous spécifiez l'intent CATEGORY_REMOTE_PLAYBACK, vous devez également définir les types de contenus multimédias et les commandes de lecture compatibles avec votre fournisseur de routage multimédia. La section suivante explique comment spécifier ces paramètres pour votre appareil.

Types de contenus multimédias et protocoles

Le fournisseur de routages multimédias pour un appareil de lecture à distance doit spécifier les types de contenus multimédias et les protocoles de transfert compatibles. Vous spécifiez ces paramètres à l'aide de la classe IntentFilter et des méthodes addDataScheme() et addDataType() de cet objet. L'extrait de code suivant montre comment définir un filtre d'intent pour permettre la lecture de vidéos à distance à l'aide des protocoles http, https et RTSP (Real Time Streaming Protocol):

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    companion object {

        private fun IntentFilter.addDataTypeUnchecked(type: String) {
            try {
                addDataType(type)
            } catch (ex: IntentFilter.MalformedMimeTypeException) {
                throw RuntimeException(ex)
            }
        }

        private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = IntentFilter().run {
            addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
            addAction(MediaControlIntent.ACTION_PLAY)
            addDataScheme("http")
            addDataScheme("https")
            addDataScheme("rtsp")
            addDataTypeUnchecked("video/*")
            arrayListOf(this)
        }
    }
    ...
}

Java

public final class SampleMediaRouteProvider extends MediaRouteProvider {

    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;

    static {
        IntentFilter videoPlayback = new IntentFilter();
        videoPlayback.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
        videoPlayback.addAction(MediaControlIntent.ACTION_PLAY);
        videoPlayback.addDataScheme("http");
        videoPlayback.addDataScheme("https");
        videoPlayback.addDataScheme("rtsp");
        addDataTypeUnchecked(videoPlayback, "video/*");
        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
        CONTROL_FILTERS_BASIC.add(videoPlayback);
    }
    ...

    private static void addDataTypeUnchecked(IntentFilter filter, String type) {
        try {
            filter.addDataType(type);
        } catch (MalformedMimeTypeException ex) {
            throw new RuntimeException(ex);
        }
    }
}

Commandes de lecture

Un fournisseur de routages multimédias proposant la lecture à distance doit spécifier les types de commandes multimédias compatibles. Voici les types généraux de commandes que les routes multimédias peuvent fournir:

  • Commandes de lecture, comme la lecture, la mise en pause, le retour arrière et l'avance rapide.
  • Les fonctionnalités de mise en file d'attente, qui permettent à l'application émettrice d'ajouter et de supprimer des éléments dans une playlist gérée par l'appareil récepteur.
  • Les fonctionnalités de session, qui empêchent les applications d'envoi d'interférer les unes avec les autres. En effet, l'appareil récepteur fournit un ID de session à l'application à l'origine de la demande, puis vérifie cet ID à chaque requête de commande de lecture ultérieure.

L'exemple de code suivant montre comment créer un filtre d'intent compatible avec les commandes de lecture de base des itinéraires multimédias:

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    companion object {
        ...
        private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = run {
            val videoPlayback: IntentFilter = ...
            ...
            val playControls = IntentFilter().apply {
                addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                addAction(MediaControlIntent.ACTION_SEEK)
                addAction(MediaControlIntent.ACTION_GET_STATUS)
                addAction(MediaControlIntent.ACTION_PAUSE)
                addAction(MediaControlIntent.ACTION_RESUME)
                addAction(MediaControlIntent.ACTION_STOP)
            }
            arrayListOf(videoPlayback, playControls)
        }
    }
    ...
}

Java

public final class SampleMediaRouteProvider extends MediaRouteProvider {
    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
    static {
        ...
        IntentFilter playControls = new IntentFilter();
        playControls.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
        playControls.addAction(MediaControlIntent.ACTION_SEEK);
        playControls.addAction(MediaControlIntent.ACTION_GET_STATUS);
        playControls.addAction(MediaControlIntent.ACTION_PAUSE);
        playControls.addAction(MediaControlIntent.ACTION_RESUME);
        playControls.addAction(MediaControlIntent.ACTION_STOP);
        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
        CONTROL_FILTERS_BASIC.add(videoPlayback);
        CONTROL_FILTERS_BASIC.add(playControls);
    }
    ...
}

Pour en savoir plus sur les intents de commande de lecture disponibles, consultez la classe MediaControlIntent.

MediaRouteProviderDescriptor

Après avoir défini les capacités de votre route multimédia à l'aide d'objets IntentFilter, vous pouvez créer un objet descripteur pour la publication dans le framework du routeur multimédia Android. Cet objet descripteur contient les spécificités de votre routage multimédia afin que d'autres applications puissent déterminer comment interagir avec votre route multimédia.

L'exemple de code suivant montre comment ajouter les filtres d'intent créés précédemment à une MediaRouteProviderDescriptor et définir le descripteur destiné au framework du routeur multimédia:

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    init {
        publishRoutes()
    }

    private fun publishRoutes() {
        val resources = context.resources
        val routeName: String = resources.getString(R.string.variable_volume_basic_route_name)
        val routeDescription: String = resources.getString(R.string.sample_route_description)
        // Create a route descriptor using previously created IntentFilters
        val routeDescriptor: MediaRouteDescriptor =
                MediaRouteDescriptor.Builder(VARIABLE_VOLUME_BASIC_ROUTE_ID, routeName)
                        .setDescription(routeDescription)
                        .addControlFilters(CONTROL_FILTERS_BASIC)
                        .setPlaybackStream(AudioManager.STREAM_MUSIC)
                        .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
                        .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
                        .setVolumeMax(VOLUME_MAX)
                        .setVolume(mVolume)
                        .build()
        // Add the route descriptor to the provider descriptor
        val providerDescriptor: MediaRouteProviderDescriptor =
                MediaRouteProviderDescriptor.Builder()
                        .addRoute(routeDescriptor)
                        .build()

        // Publish the descriptor to the framework
        descriptor = providerDescriptor
    }
    ...
}

Java

public SampleMediaRouteProvider(Context context) {
    super(context);
    publishRoutes();
}

private void publishRoutes() {
    Resources r = getContext().getResources();
    // Create a route descriptor using previously created IntentFilters
    MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
            VARIABLE_VOLUME_BASIC_ROUTE_ID,
            r.getString(R.string.variable_volume_basic_route_name))
            .setDescription(r.getString(R.string.sample_route_description))
            .addControlFilters(CONTROL_FILTERS_BASIC)
            .setPlaybackStream(AudioManager.STREAM_MUSIC)
            .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
            .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
            .setVolumeMax(VOLUME_MAX)
            .setVolume(mVolume)
            .build();
    // Add the route descriptor to the provider descriptor
    MediaRouteProviderDescriptor providerDescriptor =
            new MediaRouteProviderDescriptor.Builder()
            .addRoute(routeDescriptor)
            .build();

    // Publish the descriptor to the framework
    setDescriptor(providerDescriptor);
}

Pour en savoir plus sur les paramètres de descripteur disponibles, consultez la documentation de référence sur MediaRouteDescriptor et MediaRouteProviderDescriptor.

Contrôler les routes

Lorsqu'une application se connecte à votre fournisseur de routage multimédia, celui-ci reçoit des commandes de lecture via le framework du routeur multimédia qui sont envoyées à votre itinéraire par d'autres applications. Pour gérer ces requêtes, vous devez fournir une implémentation d'une classe MediaRouteProvider.RouteController, qui traite les commandes et gère la communication réelle avec votre appareil récepteur.

Le framework du routeur multimédia appelle la méthode onCreateRouteController() de votre fournisseur d'itinéraires pour obtenir une instance de cette classe, puis y achemine les requêtes. Voici les principales méthodes de la classe MediaRouteProvider.RouteController, que vous devez implémenter pour votre fournisseur de routage média:

  • onSelect() : appelé lorsqu'une application sélectionne votre route pour la lecture. Elle permet d'effectuer toutes les tâches de préparation pouvant être nécessaires avant le lancement de la lecture du contenu multimédia.
  • onControlRequest() : envoie des commandes de lecture spécifiques à l'appareil récepteur.
  • onSetVolume() : envoie une requête à l'appareil récepteur pour régler le volume de lecture sur une valeur spécifique.
  • onUpdateVolume() : envoie une requête à l'appareil récepteur pour modifier le volume de lecture d'un certain montant.
  • onUnselect() : appelé lorsqu'une application désélectionne une route.
  • onRelease() : appelée lorsque la route n'est plus nécessaire au framework, ce qui lui permet de libérer ses ressources.

Toutes les requêtes de commandes de lecture, à l'exception des changements de volume, sont dirigées vers la méthode onControlRequest(). La mise en œuvre de cette méthode doit analyser les requêtes de contrôle et y répondre de manière appropriée. Voici un exemple d'implémentation de cette méthode qui traite les commandes pour une route multimédia de lecture à distance:

Kotlin

private class SampleRouteController : MediaRouteProvider.RouteController() {
    ...

    override fun onControlRequest(
            intent: Intent,
            callback: MediaRouter.ControlRequestCallback?
    ): Boolean {
        return if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
            val action = intent.action
            when (action) {
                MediaControlIntent.ACTION_PLAY -> handlePlay(intent, callback)
                MediaControlIntent.ACTION_ENQUEUE -> handleEnqueue(intent, callback)
                MediaControlIntent.ACTION_REMOVE -> handleRemove(intent, callback)
                MediaControlIntent.ACTION_SEEK -> handleSeek(intent, callback)
                MediaControlIntent.ACTION_GET_STATUS -> handleGetStatus(intent, callback)
                MediaControlIntent.ACTION_PAUSE -> handlePause(intent, callback)
                MediaControlIntent.ACTION_RESUME -> handleResume(intent, callback)
                MediaControlIntent.ACTION_STOP -> handleStop(intent, callback)
                MediaControlIntent.ACTION_START_SESSION -> handleStartSession(intent, callback)
                MediaControlIntent.ACTION_GET_SESSION_STATUS ->
                    handleGetSessionStatus(intent, callback)
                MediaControlIntent.ACTION_END_SESSION -> handleEndSession(intent, callback)
                else -> false
            }.also {
                Log.d(TAG, sessionManager.toString())
            }
        } else {
            false
        }
    }
    ...
}

Java

private final class SampleRouteController extends
        MediaRouteProvider.RouteController {
    ...

    @Override
    public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {

        String action = intent.getAction();

        if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
            boolean success = false;
            if (action.equals(MediaControlIntent.ACTION_PLAY)) {
                success = handlePlay(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
                success = handleEnqueue(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_REMOVE)) {
                success = handleRemove(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_SEEK)) {
                success = handleSeek(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_GET_STATUS)) {
                success = handleGetStatus(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_PAUSE)) {
                success = handlePause(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_RESUME)) {
                success = handleResume(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_STOP)) {
                success = handleStop(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_START_SESSION)) {
                success = handleStartSession(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_GET_SESSION_STATUS)) {
                success = handleGetSessionStatus(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_END_SESSION)) {
                success = handleEndSession(intent, callback);
            }

            Log.d(TAG, sessionManager.toString());
            return success;
        }
        return false;
    }
    ...
}

Il est important de comprendre que la classe MediaRouteProvider.RouteController est destinée à servir de wrapper à l'API pour votre équipement de lecture multimédia. L'implémentation des méthodes de cette classe dépend entièrement de l'interface de programmation fournie par votre appareil récepteur.

Exemple de code

L'exemple MediaRouter montre comment créer un fournisseur de routage multimédia personnalisé.