Tổng quan về MediaRouteProvider

Khung của bộ định tuyến nội dung đa phương tiện Android cho phép nhà sản xuất bật tính năng phát trên thiết bị của họ thông qua một giao diện được chuẩn hoá có tên là MediaRouteProvider. Trình cung cấp tuyến xác định một giao diện chung để phát nội dung nghe nhìn trên thiết bị nhận, giúp bạn có thể phát nội dung nghe nhìn trên thiết bị của mình từ bất kỳ ứng dụng Android nào hỗ trợ tuyến nội dung nghe nhìn.

Hướng dẫn này thảo luận cách tạo trình cung cấp định tuyến nội dung nghe nhìn cho thiết bị nhận và cung cấp trình cung cấp đó cho các ứng dụng phát nội dung nghe nhìn khác chạy trên Android. Để sử dụng API này, bạn nên làm quen với các lớp chính MediaRouteProvider, MediaRouteProviderDescriptorRouteController.

Tổng quan

Khung bộ định tuyến nội dung đa phương tiện trên Android cho phép các nhà phát triển ứng dụng đa phương tiện và nhà sản xuất thiết bị phát nội dung đa phương tiện kết nối thông qua một API chung và giao diện người dùng phổ biến. Sau đó, các nhà phát triển ứng dụng triển khai giao diện MediaRouter có thể kết nối với khung này và phát nội dung đến các thiết bị tham gia vào khung bộ định tuyến nội dung nghe nhìn. Các nhà sản xuất thiết bị phát nội dung đa phương tiện có thể tham gia vào khung này bằng cách phát hành một MediaRouteProvider cho phép các ứng dụng khác kết nối và phát nội dung đa phương tiện trên thiết bị nhận. Hình 1 minh hoạ cách một ứng dụng kết nối với thiết bị nhận thông qua khung bộ định tuyến nội dung nghe nhìn.

Hình 1. Tổng quan về cách các lớp nhà cung cấp định tuyến nội dung nghe nhìn cung cấp hoạt động giao tiếp từ một ứng dụng đa phương tiện đến thiết bị nhận.

Khi bạn xây dựng trình cung cấp tuyến nội dung nghe nhìn cho thiết bị nhận của mình, trình cung cấp này sẽ phục vụ các mục đích sau đây:

  • Mô tả và phát hành các tính năng của thiết bị nhận để các ứng dụng khác có thể khám phá và sử dụng các tính năng phát của thiết bị đó.
  • Gói giao diện lập trình của thiết bị nhận và các cơ chế truyền tải giao tiếp để giúp thiết bị tương thích với khung bộ định tuyến nội dung nghe nhìn.

Phân phối nhà cung cấp tuyến đường

Trình cung cấp định tuyến nội dung nghe nhìn được phân phối như một phần của ứng dụng Android. Bạn có thể cung cấp trình cung cấp tuyến phương tiện cho các ứng dụng khác bằng cách mở rộng MediaRouteProviderService hoặc gói cách bạn triển khai MediaRouteProvider bằng dịch vụ của riêng bạn và khai báo bộ lọc ý định cho trình cung cấp tuyến nội dung nghe nhìn. Các bước này cho phép các ứng dụng khác khám phá và sử dụng tuyến nội dung nghe nhìn của bạn.

Lưu ý: Ứng dụng chứa trình cung cấp tuyến nội dung đa phương tiện cũng có thể dùng giao diện MediaRouter đối với trình cung cấp tuyến, nhưng điều này là không bắt buộc.

Thư viện hỗ trợ MediaRouter

API bộ định tuyến đa phương tiện được xác định trong thư viện AndroidX MediaRouter Bạn phải thêm thư viện này vào dự án phát triển ứng dụng của mình. Để biết thêm thông tin về cách thêm thư viện hỗ trợ vào dự án của bạn, hãy xem bài viết Thiết lập thư viện hỗ trợ.

Thận trọng: Hãy nhớ sử dụng phương thức triển khai AndroidX cho khung bộ định tuyến nội dung đa phương tiện. Không sử dụng gói android.media cũ.

Tạo Dịch vụ của nhà cung cấp

Khung bộ định tuyến đa phương tiện phải tìm được và kết nối với nhà cung cấp định tuyến nội dung nghe nhìn của bạn để cho phép các ứng dụng khác dùng tuyến của bạn. Để làm điều này, khung bộ định tuyến nội dung đa phương tiện sẽ tìm kiếm các ứng dụng khai báo thao tác theo ý định của trình cung cấp tuyến truyền thông. Khi một ứng dụng khác muốn kết nối với ứng dụng của bạn, khung này phải có khả năng gọi và kết nối với ứng dụng đó. Vì vậy, ứng dụng đó phải được đóng gói trong một Service.

Mã ví dụ sau đây minh hoạ phần khai báo dịch vụ của nhà cung cấp định tuyến nội dung nghe nhìn và bộ lọc ý định trong một tệp kê khai, cho phép khung bộ định tuyến nội dung nghe nhìn phát hiện và sử dụng dịch vụ đó:

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

Ví dụ về tệp kê khai này khai báo một dịch vụ bao bọc các lớp nhà cung cấp định tuyến nội dung nghe nhìn thực tế. Khung bộ định tuyến nội dung đa phương tiện của Android cung cấp lớp MediaRouteProviderService để dùng làm trình bao bọc dịch vụ cho các nhà cung cấp tuyến truyền thông. Mã ví dụ sau đây minh hoạ cách sử dụng lớp trình bao bọc này:

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

Chỉ định khả năng định tuyến

Các ứng dụng kết nối với khung bộ định tuyến nội dung đa phương tiện có thể khám phá tuyến nội dung nghe nhìn của bạn thông qua nội dung khai báo tệp kê khai của ứng dụng. Tuy nhiên, những ứng dụng này cũng cần biết chức năng của các tuyến nội dung nghe nhìn mà bạn đang cung cấp. Tuyến nội dung nghe nhìn có thể có nhiều loại và có những tính năng khác nhau. Các ứng dụng khác cần phát hiện được những thông tin chi tiết này để xác định xem chúng có tương thích với tuyến của bạn hay không.

Khung bộ định tuyến nội dung đa phương tiện cho phép bạn xác định và phát hành các tính năng của tuyến nội dung nghe nhìn thông qua các đối tượng IntentFilter, đối tượng MediaRouteDescriptorMediaRouteProviderDescriptor. Phần này giải thích cách dùng các lớp này để phát hành thông tin chi tiết về tuyến nội dung nghe nhìn của bạn cho các ứng dụng khác.

Danh mục tuyến đường

Trong phần mô tả có lập trình về nhà cung cấp định tuyến nội dung nghe nhìn, bạn phải chỉ định xem liệu nhà cung cấp có hỗ trợ tính năng phát từ xa, đầu ra phụ hay cả hai. Dưới đây là các danh mục tuyến do khung bộ định tuyến nội dung đa phương tiện cung cấp:

  • CATEGORY_LIVE_AUDIO – Đầu ra âm thanh sang thiết bị đầu ra phụ, chẳng hạn như một hệ thống phát nhạc có hỗ trợ không dây.
  • CATEGORY_LIVE_VIDEO – Đầu ra video sang một thiết bị đầu ra phụ, chẳng hạn như các thiết bị Hiển thị không dây.
  • CATEGORY_REMOTE_PLAYBACK – Phát video hoặc âm thanh trên một thiết bị riêng biệt xử lý việc truy xuất, giải mã và phát nội dung nghe nhìn, chẳng hạn như thiết bị Chromecast.

Để đưa các chế độ cài đặt này vào nội dung mô tả về tuyến nội dung nghe nhìn, bạn sẽ chèn các chế độ cài đặt đó vào đối tượng IntentFilter. Sau này, bạn sẽ thêm đối tượng này vào đối tượng 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);
    }
}

Nếu chỉ định ý định CATEGORY_REMOTE_PLAYBACK, bạn cũng phải xác định những loại nội dung nghe nhìn và bộ điều khiển chế độ phát được nhà cung cấp tuyến nội dung nghe nhìn hỗ trợ. Phần tiếp theo mô tả cách chỉ định các chế độ cài đặt này cho thiết bị của bạn.

Loại nội dung đa phương tiện và giao thức

Nhà cung cấp tuyến nội dung nghe nhìn cho thiết bị phát từ xa phải chỉ định loại nội dung nghe nhìn và giao thức truyền mà thiết bị hỗ trợ. Bạn chỉ định các chế độ cài đặt này bằng cách sử dụng lớp IntentFilter cũng như các phương thức addDataScheme()addDataType() của đối tượng đó. Đoạn mã sau đây minh hoạ cách xác định bộ lọc ý định để hỗ trợ tính năng phát video từ xa bằng http, https và Giao thức truyền trực tuyến theo thời gian thực (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);
        }
    }
}

Bộ điều khiển chế độ phát

Trình cung cấp định tuyến nội dung nghe nhìn cung cấp tính năng phát từ xa phải chỉ định loại chế độ điều khiển nội dung nghe nhìn được hỗ trợ. Sau đây là các loại chế độ kiểm soát chung mà các tuyến nội dung đa phương tiện có thể cung cấp:

  • Bộ điều khiển chế độ phát, chẳng hạn như phát, tạm dừng, tua lại và tua đi.
  • Các tính năng xếp hàng, cho phép ứng dụng gửi thêm và xoá các mục khỏi danh sách phát do thiết bị nhận duy trì.
  • Các tính năng của phiên giúp ngăn việc gửi các ứng dụng can thiệp vào nhau bằng cách yêu cầu thiết bị nhận cung cấp mã phiên cho ứng dụng yêu cầu, sau đó kiểm tra mã phiên đó trong mỗi yêu cầu điều khiển chế độ phát tiếp theo.

Mã ví dụ sau đây minh hoạ cách tạo bộ lọc ý định để hỗ trợ các chế độ điều khiển chế độ phát tuyến nội dung nghe nhìn cơ bản:

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

Để biết thêm thông tin về các ý định hiện có liên quan đến bộ điều khiển chế độ phát, hãy xem lớp MediaControlIntent.

MediaRouteProviderDescriptor

Sau khi xác định chức năng của tuyến nội dung đa phương tiện bằng đối tượng IntentFilter, bạn có thể tạo đối tượng chỉ số mô tả để phát hành lên khung bộ định tuyến nội dung đa phương tiện Android. Đối tượng chỉ số mô tả này chứa thông tin cụ thể về khả năng của tuyến nội dung nghe nhìn để các ứng dụng khác có thể xác định cách tương tác với tuyến nội dung nghe nhìn của bạn.

Mã ví dụ sau đây minh hoạ cách thêm các bộ lọc ý định đã tạo trước đó vào MediaRouteProviderDescriptor và đặt chỉ số mô tả để khung bộ định tuyến nội dung đa phương tiện sử dụng:

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

Để biết thêm thông tin về các chế độ cài đặt có sẵn của phần mô tả, hãy xem tài liệu tham khảo cho MediaRouteDescriptorMediaRouteProviderDescriptor.

Kiểm soát tuyến đường

Khi một ứng dụng kết nối với nhà cung cấp định tuyến nội dung nghe nhìn của bạn, nhà cung cấp đó sẽ nhận được lệnh phát thông qua khung bộ định tuyến nội dung đa phương tiện mà các ứng dụng khác gửi đến tuyến của bạn. Để xử lý những yêu cầu này, bạn phải cung cấp phương thức triển khai của một lớp MediaRouteProvider.RouteController. Lớp này sẽ xử lý các lệnh và xử lý hoạt động giao tiếp thực tế đến thiết bị nhận.

Khung bộ định tuyến nội dung đa phương tiện gọi phương thức onCreateRouteController() của trình cung cấp định tuyến để lấy một thực thể của lớp này rồi chuyển các yêu cầu đến thực thể đó. Dưới đây là các phương thức chính của lớp MediaRouteProvider.RouteController mà bạn phải triển khai cho trình cung cấp định tuyến nội dung nghe nhìn của mình:

  • onSelect() – Được gọi khi một ứng dụng chọn tuyến của bạn để phát. Bạn sẽ sử dụng phương thức này để thực hiện mọi công việc chuẩn bị có thể cần thiết trước khi bắt đầu phát nội dung đa phương tiện.
  • onControlRequest() — Gửi các lệnh phát cụ thể đến thiết bị nhận.
  • onSetVolume() – Gửi yêu cầu đến thiết bị nhận để đặt âm lượng phát thành một giá trị cụ thể.
  • onUpdateVolume() – Gửi yêu cầu đến thiết bị nhận để sửa đổi âm lượng phát theo mức âm lượng đã chỉ định.
  • onUnselect() – Được gọi khi một ứng dụng bỏ chọn một tuyến.
  • onRelease() – Được gọi khi khung không còn cần đến tuyến này, cho phép khung giải phóng tài nguyên.

Tất cả yêu cầu điều khiển chế độ phát, ngoại trừ các thay đổi về âm lượng, đều được chuyển đến phương thức onControlRequest(). Khi triển khai phương thức này, bạn phải phân tích cú pháp các yêu cầu kiểm soát và phản hồi các yêu cầu đó một cách thích hợp. Dưới đây là một ví dụ về cách triển khai phương thức này, giúp xử lý các lệnh cho tuyến nội dung nghe nhìn phát từ xa:

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

Điều quan trọng bạn cần hiểu là lớp MediaRouteProvider.RouteController được dùng để đóng vai trò là trình bao bọc cho API cho thiết bị phát nội dung đa phương tiện. Việc triển khai các phương thức trong lớp này hoàn toàn phụ thuộc vào giao diện có lập trình do thiết bị nhận của bạn cung cấp.

Mã mẫu

Mẫu MediaRouter cho biết cách tạo nhà cung cấp định tuyến nội dung nghe nhìn tuỳ chỉnh.