Ringkasan MediaRouteProvider

Framework router media Android memungkinkan produsen mengaktifkan pemutaran di perangkat mereka melalui antarmuka standar yang disebut MediaRouteProvider. Penyedia rute menentukan antarmuka umum untuk memutar media di perangkat penerima, sehingga memungkinkan pemutaran media di peralatan Anda dari aplikasi Android apa pun yang mendukung rute media.

Panduan ini membahas cara membuat penyedia rute media untuk perangkat penerima dan menyediakannya untuk aplikasi pemutaran media lain yang berjalan di Android. Untuk menggunakan API ini, Anda harus memahami class utama MediaRouteProvider, MediaRouteProviderDescriptor, dan RouteController.

Ringkasan

Framework router media Android memungkinkan developer aplikasi media dan produsen perangkat pemutaran media untuk terhubung melalui API umum dan antarmuka pengguna umum. Developer aplikasi yang mengimplementasikan antarmuka MediaRouter kemudian dapat terhubung ke framework tersebut dan memutar konten ke perangkat yang berpartisipasi dalam framework router media. Produsen perangkat pemutaran media dapat berpartisipasi dalam framework dengan memublikasikan MediaRouteProvider yang memungkinkan aplikasi lain terhubung dan memutar media di perangkat penerima. Gambar 1 mengilustrasikan cara aplikasi terhubung ke perangkat penerima melalui framework router media.

Gambar 1. Ringkasan tentang cara class penyedia rute media menyediakan komunikasi dari aplikasi media ke perangkat penerima.

Saat Anda mem-build penyedia rute media untuk perangkat penerima, penyedia tersebut memiliki tujuan berikut:

  • Mendeskripsikan dan memublikasikan kemampuan perangkat penerima sehingga aplikasi lain dapat menemukannya dan menggunakan fitur pemutarannya.
  • Menggabungkan antarmuka pemrograman perangkat penerima dan mekanisme transpor komunikasinya agar perangkat tersebut kompatibel dengan framework router media.

Distribusi penyedia rute

Penyedia rute media didistribusikan sebagai bagian dari aplikasi Android. Penyedia rute Anda dapat tersedia untuk aplikasi lain dengan memperluas MediaRouteProviderService atau menggabungkan implementasi MediaRouteProvider dengan layanan Anda sendiri dan mendeklarasikan filter intent untuk penyedia rute media. Langkah-langkah ini memungkinkan aplikasi lain untuk menemukan dan memanfaatkan rute media Anda.

Catatan: Aplikasi yang berisi penyedia rute media juga dapat menyertakan antarmuka MediaRouter ke penyedia rute, tetapi hal ini tidak wajib.

Support library MediaRouter

API router media ditentukan dalam library AndroidX MediaRouter Anda harus menambahkan library ini ke project pengembangan aplikasi Anda. Untuk mengetahui informasi selengkapnya tentang cara menambahkan support library ke project, lihat Penyiapan Support Library.

Perhatian: Pastikan untuk menggunakan implementasi AndroidX framework router media. Jangan gunakan paket android.media yang lama.

Membuat Layanan Penyedia

Framework router media harus dapat menemukan dan terhubung ke penyedia rute media agar aplikasi lain dapat menggunakan rute Anda. Untuk melakukannya, framework router media akan mencari aplikasi yang mendeklarasikan tindakan intent penyedia rute media. Saat aplikasi lain ingin terhubung ke penyedia Anda, framework harus dapat memanggil dan terhubung ke penyedia tersebut, sehingga penyedia harus dienkapsulasi dalam Service.

Kode contoh berikut menunjukkan deklarasi layanan penyedia rute media dan filter intent dalam manifes, yang memungkinkannya ditemukan dan digunakan oleh framework router media:

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

Contoh manifes ini mendeklarasikan layanan yang menggabungkan class penyedia rute media yang sebenarnya. Framework router media Android menyediakan class MediaRouteProviderService untuk digunakan sebagai wrapper layanan bagi penyedia rute media. Kode contoh berikut menunjukkan cara menggunakan class wrapper ini:

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

Menentukan Kemampuan Rute

Aplikasi yang terhubung ke framework router media dapat menemukan rute media Anda melalui deklarasi manifes aplikasi, tetapi aplikasi juga perlu mengetahui kemampuan rute media yang Anda sediakan. Rute media dapat terdiri dari berbagai jenis dan memiliki fitur yang berbeda, dan aplikasi lain harus dapat menemukan detail ini untuk menentukan apakah rute tersebut kompatibel dengan rute Anda.

Framework router media memungkinkan Anda menentukan dan memublikasikan kemampuan rute media melalui objek IntentFilter, objek MediaRouteDescriptor, dan MediaRouteProviderDescriptor. Bagian ini menjelaskan cara menggunakan class ini untuk memublikasikan detail rute media Anda untuk aplikasi lain.

Kategori rute

Sebagai bagian dari deskripsi terprogram penyedia rute media, Anda harus menentukan apakah penyedia Anda mendukung pemutaran jarak jauh, output sekunder, atau keduanya. Berikut adalah kategori rute yang disediakan oleh framework router media:

  • CATEGORY_LIVE_AUDIO — Output audio ke perangkat output sekunder, seperti sistem musik yang didukung secara nirkabel.
  • CATEGORY_LIVE_VIDEO — Output video ke perangkat output sekunder, seperti perangkat Layar Nirkabel.
  • CATEGORY_REMOTE_PLAYBACK — Memutar video atau audio di perangkat terpisah yang menangani pengambilan, dekode, dan pemutaran media, seperti perangkat Chromecast.

Untuk menyertakan setelan ini dalam deskripsi rute media Anda, sisipkan setelan ke dalam objek IntentFilter, yang nantinya Anda tambahkan ke objek 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);
    }
}

Jika menentukan intent CATEGORY_REMOTE_PLAYBACK, Anda juga harus menentukan jenis media dan kontrol pemutaran yang didukung oleh penyedia rute media Anda. Bagian berikutnya menjelaskan cara menentukan setelan ini untuk perangkat Anda.

Jenis media dan protokol

Penyedia rute media untuk perangkat pemutaran jarak jauh harus menentukan jenis media dan protokol transfer yang didukungnya. Anda menentukan setelan ini menggunakan class IntentFilter serta metode addDataScheme() dan addDataType() objek tersebut. Cuplikan kode berikut menunjukkan cara menentukan filter intent untuk mendukung pemutaran video jarak jauh menggunakan http, https, dan 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);
        }
    }
}

Kontrol pemutaran

Penyedia rute media yang menawarkan pemutaran jarak jauh harus menentukan jenis kontrol media yang didukungnya. Berikut adalah jenis kontrol umum yang dapat disediakan oleh rute media:

  • Kontrol pemutaran, seperti putar, jeda, putar ulang, dan maju cepat.
  • Fitur antrean, yang memungkinkan aplikasi pengirim menambahkan dan menghapus item dari playlist yang dikelola oleh perangkat penerima.
  • Fitur sesi, yang mencegah aplikasi pengirim mengganggu satu sama lain dengan meminta perangkat penerima memberikan ID sesi ke aplikasi yang meminta, lalu memeriksa ID tersebut dengan setiap permintaan kontrol pemutaran berikutnya.

Contoh kode berikut menunjukkan cara membuat filter intent untuk mendukung kontrol pemutaran rute media dasar:

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

Untuk informasi selengkapnya tentang intent kontrol pemutaran yang tersedia, lihat class MediaControlIntent.

MediaRouteProviderDescriptor

Setelah menentukan kemampuan rute media menggunakan objek IntentFilter, Anda dapat membuat objek deskriptor untuk dipublikasikan ke framework router media Android. Objek deskripsi ini berisi spesifikasi kemampuan rute media Anda agar aplikasi lain dapat menentukan cara berinteraksi dengan rute media Anda.

Kode contoh berikut menunjukkan cara menambahkan filter intent yang telah dibuat sebelumnya ke MediaRouteProviderDescriptor dan menetapkan deskripsi untuk digunakan oleh framework router media:

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

Untuk informasi selengkapnya tentang setelan deskripsi yang tersedia, lihat dokumentasi referensi untuk MediaRouteDescriptor dan MediaRouteProviderDescriptor.

Mengontrol Rute

Saat aplikasi terhubung ke penyedia rute media Anda, penyedia tersebut akan menerima perintah pemutaran melalui framework router media yang dikirim ke rute Anda oleh aplikasi lain. Untuk menangani permintaan ini, Anda harus menyediakan implementasi class MediaRouteProvider.RouteController, yang memproses perintah dan menangani komunikasi aktual ke perangkat penerima.

Framework router media memanggil metode onCreateRouteController() penyedia rute Anda untuk mendapatkan instance class ini, lalu merutekan permintaan ke instance tersebut. Metode ini adalah metode utama class MediaRouteProvider.RouteController, yang harus Anda implementasikan untuk penyedia rute media Anda:

  • onSelect() — Dipanggil saat aplikasi memilih rute Anda untuk pemutaran. Gunakan metode ini untuk melakukan tugas persiapan yang mungkin diperlukan sebelum pemutaran media dimulai.
  • onControlRequest() - Mengirimkan perintah pemutaran spesifik ke perangkat penerima.
  • onSetVolume() — Mengirim permintaan ke perangkat penerima untuk menetapkan volume pemutaran ke nilai tertentu.
  • onUpdateVolume() — Mengirim permintaan ke perangkat penerima untuk mengubah volume pemutaran dengan jumlah yang ditentukan.
  • onUnselect() - Dipanggil ketika aplikasi batal memilih rute.
  • onRelease() — Dipanggil saat rute tidak lagi diperlukan oleh framework, sehingga rute tersebut dapat membebaskan resource-nya.

Semua permintaan kontrol pemutaran, kecuali untuk perubahan volume, akan diarahkan ke metode onControlRequest(). Penerapan metode ini harus mengurai permintaan kontrol dan meresponsnya dengan tepat. Berikut adalah contoh implementasi metode ini yang memproses perintah untuk rute media pemutaran jarak jauh:

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

Penting untuk dipahami bahwa class MediaRouteProvider.RouteController dimaksudkan untuk bertindak sebagai wrapper bagi API ke peralatan pemutaran media Anda. Implementasi metode dalam class ini sepenuhnya bergantung pada antarmuka terprogram yang disediakan oleh perangkat penerima Anda.

Contoh Kode

Contoh MediaRouter menunjukkan cara membuat penyedia rute media kustom.