Omówienie MediaRouteProvider

Platforma routera multimediów z Androidem umożliwia producentom odtwarzanie treści na urządzeniach za pomocą ujednoliconego interfejsu o nazwie MediaRouteProvider. Dostawca trasy definiuje wspólny interfejs do odtwarzania multimediów na urządzeniu odbierającym, co umożliwia odtwarzanie multimediów na sprzęcie z dowolnej aplikacji na Androida, która obsługuje trasy multimediów.

W tym przewodniku omawiamy, jak utworzyć dostawcę trasy multimediów dla urządzenia odbierającego i udostępnić go innym aplikacjom do odtwarzania multimediów, które działają na Androidzie. Aby korzystać z tego interfejsu API, musisz znać klasy kluczy MediaRouteProvider, MediaRouteProviderDescriptor i RouteController.

Przegląd

Platforma routera multimediów na Androidzie umożliwia deweloperom aplikacji multimedialnych i producentom urządzeń do odtwarzania multimediów połączenie się za pomocą wspólnego interfejsu API i wspólnego interfejsu. Deweloperzy aplikacji, którzy implementują interfejs MediaRouter, mogą następnie połączyć się z tą platformą i odtwarzać treści na urządzeniach należących do platformy routera multimediów. Producenci urządzeń do odtwarzania multimediów mogą uczestniczyć w platformie, publikując MediaRouteProvider, który umożliwia innym aplikacjom łączenie się z urządzeniami odbiorczymi i ich odtwarzanie. Rysunek 1 pokazuje, jak aplikacja łączy się z urządzeniem odbierającym za pomocą platformy routera multimediów.

Rysunek 1. Omówienie sposobu, w jaki klasy dostawcy trasy multimediów zapewniają komunikację między aplikacją multimedialną a urządzeniem odbierającym.

Gdy tworzysz dostawcę trasy multimediów dla urządzenia odbierającego, dostawca służy do tych celów:

  • Opisz i opublikuj możliwości urządzenia odbierającego, aby inne aplikacje mogły je wykryć i korzystać z funkcji odtwarzania.
  • Opakuj interfejs programowania urządzenia odbierającego i jego mechanizmy transportu komunikacyjnego, aby urządzenie było zgodne ze strukturą routera multimediów.

Rozmieszczenie dostawców tras

Dostawca trasy multimediów jest rozpowszechniany w ramach aplikacji na Androida. Możesz udostępnić dostawcę trasy innym aplikacjom, rozszerzając MediaRouteProviderService lub dodając implementację MediaRouteProvider do własnej usługi i deklarując filtr intencji dla dostawcy trasy multimediów. Te czynności umożliwiają innym aplikacjom wykrywanie Twoich tras multimedialnych i korzystanie z nich.

Uwaga: aplikacja zawierająca dostawcę trasy multimediów może też udostępniać dostawcy trasy interfejs MediaRouter, ale nie jest to wymagane.

Biblioteka obsługi MediaRouter

Interfejsy API routera multimediów są zdefiniowane w bibliotece MediaRouter w AndroidzieX Musisz dodać tę bibliotekę do projektu, w którym tworzysz aplikację. Więcej informacji o dodawaniu bibliotek pomocniczych do projektu znajdziesz w artykule o konfigurowaniu bibliotek pomocy.

Uwaga: pamiętaj, aby użyć implementacji platformy routera multimediów AndroidX. Nie używaj starszego pakietu android.media.

Tworzenie usługi dostawcy

Platforma routera multimediów musi mieć możliwość wykrywania Twojego dostawcy tras multimediów i łączenia się z nim, aby umożliwić innym aplikacjom użycie Twojej trasy. W tym celu platforma routera multimediów szuka aplikacji, które deklarują działanie intencji dostawcy trasy multimediów. Gdy inna aplikacja chce połączyć się z Twoim dostawcą, platforma musi mieć możliwość jej wywołania i połączenia, dlatego dostawca musi być ujęty w Service.

Ten przykładowy kod zawiera deklarację usługi dostawcy trasy multimediów i filtr intencji w pliku manifestu, który umożliwia wykrywanie i używanie tej usługi przez platformę routera multimediów:

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

Ten przykładowy plik manifestu deklaruje usługę, która pakuje rzeczywiste klasy dostawcy trasy multimediów. Platforma routera multimediów na Androidzie udostępnia klasę MediaRouteProviderService, która może być używana jako kod usługi dla dostawców tras multimediów. Poniższy przykładowy kod pokazuje, jak używać tej klasy otoki:

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

Określanie możliwości tras

Aplikacje łączące się z platformą routera multimediów mogą wykrywać trasę multimediów za pomocą deklaracji w pliku manifestu aplikacji, ale muszą też znać możliwości zapewnianych tras multimediów. Trasy multimediów mogą być różnego typu i mieć różne funkcje. Inne aplikacje muszą mieć możliwość wykrycia tych szczegółów, aby określić, czy są zgodne z Twoją trasą.

Platforma routera multimediów umożliwia definiowanie i publikowanie możliwości trasy multimediów przez obiekty IntentFilter, obiekty MediaRouteDescriptor i MediaRouteProviderDescriptor. Z tej sekcji dowiesz się, jak używać tych klas do publikowania szczegółów trasy multimediów dla innych aplikacji.

Kategorie trasy

W automatycznym opisie dostawcy trasy multimediów musisz określić, czy obsługuje on odtwarzanie zdalne, dodatkowe urządzenie czy obie te funkcje. Oto kategorie tras udostępniane przez platformę routera multimediów:

  • CATEGORY_LIVE_AUDIO – Wyjście dźwięku na dodatkowe urządzenie wyjściowe, np. na bezprzewodowy system muzyczny.
  • CATEGORY_LIVE_VIDEO – Dane wyjściowe wideo na dodatkowe urządzenie wyjściowe, takie jak wyświetlacze bezprzewodowe.
  • CATEGORY_REMOTE_PLAYBACK – odtwarzaj filmy lub dźwięk na osobnym urządzeniu, które obsługuje pobieranie, dekodowanie i odtwarzanie multimediów, np. na urządzeniach Chromecast.

Aby uwzględnić te ustawienia w opisie trasy multimediów, musisz je wstawić do obiektu IntentFilter, który możesz później dodać do obiektu 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);
    }
}

Jeśli określisz intencję CATEGORY_REMOTE_PLAYBACK, musisz też określić, jakie typy multimediów i elementy sterujące odtwarzaniem są obsługiwane przez dostawcę trasy multimediów. W następnej sekcji opisujemy, jak określić te ustawienia na swoim urządzeniu.

Typy multimediów i protokoły

Dostawca trasy multimediów dla zdalnego urządzenia odtwarzającego musi określać typy multimediów i protokoły przesyłania obsługiwane. Określasz te ustawienia za pomocą klasy IntentFilter oraz metod addDataScheme() i addDataType() tego obiektu. Ten fragment kodu pokazuje, jak zdefiniować filtr intencji na potrzeby zdalnego odtwarzania filmów przy użyciu protokołów http, https i protokołu Real Time Streaming Protocol (RTSP):

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

Sterowanie odtwarzaniem

Dostawca trasy multimediów, który oferuje zdalne odtwarzanie, musi określić typy elementów sterujących obsługiwanych przez niego. Oto ogólne typy ustawień, które zapewniają trasy multimediów:

  • Elementy sterujące odtwarzaniem, takie jak odtwarzanie, wstrzymywanie, przewijanie do tyłu i do przodu.
  • Funkcje kolejki, które umożliwiają aplikacji wysyłającej dodawanie i usuwanie elementów z playlisty zarządzanej przez urządzenie odbierające.
  • Funkcje sesji, które uniemożliwiają wysyłanie aplikacji do siebie nawzajem, ponieważ urządzenie odbierające przekazuje identyfikator sesji aplikacji żądającej i sprawdza ten identyfikator przy każdym kolejnym żądaniu sterowania odtwarzaniem.

Poniższy przykładowy kod pokazuje, jak utworzyć filtr intencji do obsługi podstawowych elementów sterujących odtwarzaniem trasy multimediów:

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

Więcej informacji o dostępnych intencjach sterowania odtwarzaniem znajdziesz w klasie MediaControlIntent.

MediaRouteProviderDescriptor

Po zdefiniowaniu możliwości trasy multimediów przy użyciu obiektów IntentFilter możesz utworzyć obiekt deskryptora do opublikowania w platformie routera multimediów Android. Ten obiekt deskryptora zawiera specyfikę funkcji trasy multimediów, tak aby inne aplikacje mogły określić sposób interakcji z tą trasą multimediów.

Ten przykładowy kod pokazuje, jak dodać wcześniej utworzone filtry intencji do obiektu MediaRouteProviderDescriptor i ustawić deskryptor do użycia przez platformę routera multimediów:

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

Więcej informacji o dostępnych ustawieniach deskryptorów znajdziesz w dokumentacji referencyjnej MediaRouteDescriptor i MediaRouteProviderDescriptor.

Kontrolowanie tras

Gdy aplikacja łączy się z dostawcą trasy multimediów, otrzymuje polecenia odtwarzania za pomocą platformy routera multimediów wysyłane na Twoją trasę przez inne aplikacje. Aby obsługiwać te żądania, musisz udostępnić implementację klasy MediaRouteProvider.RouteController, która przetwarza polecenia i obsługuje rzeczywistą komunikację z urządzeniem odbierającym.

Platforma routera multimediów wywołuje metodę onCreateRouteController() dostawcy tras, aby uzyskać instancję tej klasy, a następnie kieruje do niej żądania. Oto główne metody klasy MediaRouteProvider.RouteController, które musisz wdrożyć na potrzeby dostawcy trasy multimediów:

  • onSelect() – wywoływane, gdy aplikacja wybiera trasę do odtwarzania. Korzystasz z tej metody do wykonywania wszelkich czynności przygotowawczych, które mogą być wymagane przed rozpoczęciem odtwarzania multimediów.
  • onControlRequest() – wysyła określone polecenia odtwarzania do urządzenia odbierającego.
  • onSetVolume() – wysyła do urządzenia odbierającego żądanie ustawienia głośności odtwarzania na określoną wartość.
  • onUpdateVolume() – wysyła do urządzenia odbierającego żądanie zmiany głośności odtwarzania o określoną wartość.
  • onUnselect() – wywoływane, gdy aplikacja odznacza trasę.
  • onRelease() – wywoływane, gdy trasa nie jest już potrzebna przez platformę, co pozwala zwolnić jej zasoby.

Wszystkie żądania sterowania odtwarzaniem, z wyjątkiem zmiany głośności, są kierowane do metody onControlRequest(). Twoja implementacja tej metody musi analizować żądania kontrolne i odpowiednio na nie odpowiadać. Oto przykładowa implementacja tej metody, która przetwarza polecenia do zdalnego odtwarzania multimediów:

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

Pamiętaj, że klasa MediaRouteProvider.RouteController służy do działania jako kod interfejsu API dla sprzętu do odtwarzania multimediów. Implementacja metod z tej klasy zależy całkowicie od interfejsu programowego udostępnianego przez urządzenie odbierające.

Kod demonstracyjny

Przykład MediaRouter pokazuje, jak utworzyć dostawcę niestandardowej trasy multimediów.