Tổng quan về MediaRouteProvider

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

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 đa phương tiện cho thiết bị nhận và cách tạo trình cung cấp đó có sẵn 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 của Android cho phép các nhà phát triển ứng dụng đa phương tiện và 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 chung. Nhà phát triển ứng dụng triển khai giao diện MediaRouter, sau đó có thể kết nối với khung và phát nội dung tới các thiết bị tham gia vào khung bộ định tuyến đa phương tiện. Nội dung đa phương tiện nhà sản xuất thiết bị phát có thể tham gia 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 nghe nhì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 một dịch vụ nhận thông qua khung bộ định tuyến phương tiện.

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

Khi bạn tạo nhà cung cấp tuyến nội dung đa phương tiện cho thiết bị nhận, nhà cung cấp đó sẽ phân phát mục đích sau đây:

  • Mô tả và công bố chức 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 YouTube.
  • Gói giao diện lập trình của thiết bị nhận và hoạt động giao tiếp của thiết bị nhận cơ chế truyền tải để thiết bị tương thích với khung bộ định tuyến đa phương tiện.

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

Nhà cung cấp tuyến nội dung nghe nhìn được phân phối như một phần của ứng dụng Android. Nhà cung cấp tuyến đường của bạn có thể là cung cấp cho các ứng dụng khác bằng cách mở rộng MediaRouteProviderService hoặc gói việc triển khai của bạn MediaRouteProvider bằng dịch vụ của riêng bạn và khai báo ý định cho nhà cung cấp định tuyến nội dung đa phương tiện. Các bước này giúp các ứng dụng khác khám phá và sử dụng tuyến nội dung đa phương tiệ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ể bao gồm Giao diện MediaRouter sang nhà cung cấp tuyến đường, nhưng điều này là không bắt buộc.

Thư viện hỗ trợ MediaRouter

API bộ định tuyến 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 hãy xem phần Thiết lập Thư viện hỗ trợ.

Thận trọng: Hãy nhớ sử dụng AndroidX khung bộ định tuyến 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 nội dung đa phương tiện phải có khả năng tìm thấy và kết nối với nhà cung cấp định tuyến nội dung đa phương tiện của bạn để cho phép các ứng dụng khác sử dụng tuyến đường của bạn. Để thực hiện việc này, khung bộ định tuyến phương tiện tìm các ứng dụng khai báo thao tác theo ý định của trình cung cấp tuyến nội dung đa phương tiện. Khi một ứng dụng khác muốn kết nối với nhà cung cấp của bạn, khung phải có thể gọi và kết nối với nó, để nhà cung cấp của bạn phải được gói gọn trong Service.

Đoạn mã ví dụ sau đây cho thấy phần khai báo dịch vụ nhà cung cấp định tuyến nội dung đa phương tiện và bộ lọc ý định trong tệp kê khai, qua đó bộ định tuyến nội dung đa phương tiện có thể phát hiện và sử dụng bộ lọc này khung:

<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 thực tế của trình cung cấp định tuyến phương tiện. 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 nhà cung cấp định tuyến nội dung đa phương tiện. Mã ví dụ sau đây minh hoạ cách sử dụng trình bao bọc này lớp:

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 phương tiện có thể khám phá tuyến phương tiện của bạn thông qua nội dung khai báo tệp kê khai của ứng dụng, nhưng chúng cũng cần biết khả năng của các tuyến nội dung đa phương tiện mà bạn đang cung cấp. Các tuyến nội dung đa phương tiện có thể thuộc nhiều loại và có các tính năng khác nhau cũng như các ứng dụng khác cần phải khám phá những chi tiết này để xác định xem chúng có tương thích với tuyến đường 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à xuất bản khả năng của nội dung nghe nhìn định tuyến qua đối tượng IntentFilter, đối tượng MediaRouteDescriptorMediaRouteProviderDescriptor. Phần này giải thích cách sử dụng để xuất bản chi tiết về tuyến nội dung đa phương tiệ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 của nhà cung cấp định tuyến phương tiện, bạn phải chỉ định nhà cung cấp của bạn hỗ trợ phát từ xa, đầu ra phụ hay cả hai. Đây là tuyến đường có các danh mục do khung bộ định tuyến 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ư hệ thống phát nhạc hỗ trợ không dây.
  • CATEGORY_LIVE_VIDEO — Đầu ra video sang 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 có chức năng xử lý nội dung nghe nhìn truy xuất, giải mã và phát lại, chẳng hạn như Thiết bị Chromecast.

Để đưa những chế độ cài đặt này vào nội dung mô tả tuyến đường truyền thông của bạn, hãy chèn các chế độ cài đặt này vào đối tượng IntentFilter mà sau đó bạn thêm vào một đối tượng Đố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 loại nội dung đa phương tiện và bộ điều khiển chế độ phát được nhà cung cấp định tuyến nội dung nghe nhìn của bạ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 phương tiện và giao thức

Nhà cung cấp tuyến nội dung đa phương tiện cho thiết bị phát từ xa phải chỉ định loại nội dung nghe nhìn và quá trình chuyển các giao thức mà nó hỗ trợ. Bạn chỉ định các chế độ cài đặt này bằng IntentFilter lớp này cũng như addDataScheme() và Phương thức addDataType() của đối tượng đó. Chiến lược phát hành đĩa đơn đoạn mã sau đây minh hoạ cách xác định bộ lọc ý định để hỗ trợ video từ xa phát lại 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

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

  • Các nút đ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 đợi, cho phép ứng dụng gửi thêm và xoá các mục từ một danh sách phát do thiết bị nhận duy trì.
  • Tính năng phiên, giúp ngăn chặn việc gửi các ứng dụng can thiệp vào từng tính năng khác bằng cách yêu cầu thiết bị nhận cung cấp id phiên cho ứng dụng yêu cầu, sau đó kiểm tra mã đó với mỗi yêu cầu điều khiển phát tiếp theo.

Ví dụ về mã sau đây minh hoạ cách tạo bộ lọc ý định để hỗ trợ bộ điều khiển chế độ phát định 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 điều khiển chế độ phát hiện có, hãy xem Lớp MediaControlIntent.

MediaRouteProviderDescriptor

Sau khi xác định khả 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 mô tả để xuất bản lên khung bộ định tuyến nội dung đa phương tiện của Android. Đối tượng mô tả này chứa các thông tin cụ thể về nội dung nghe nhìn của bạn định tuyến để các ứng dụng khác có thể xác định cách tương tác với nội dung nghe nhìn của bạn tuyến đường.

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

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 mã mô tả có sẵn, 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 đa phương tiện của bạn, ứng dụng sẽ nhận được chế độ phát thông qua khung bộ định tuyến phương tiện do ứng dụng khác gửi đến tuyến đường của bạn. Để xử lý những vấn đề này các yêu cầu, bạn phải cung cấp phương thức triển khai 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ị thu của bạn.

Khung bộ định tuyến nội dung đa phương tiện sẽ gọi onCreateRouteController() của trình cung cấp định tuyến của bạn để lấy một thực thể của lớp này rồi định tuyến các yêu cầu đến đó. 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 nhà cung cấp tuyến nội dung đa phương tiện của bạn:

  • onSelect() – Được gọi khi một ứng dụng chọn tuyến đường để phát. Bạn 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 nghe nhì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 giá trị cụ thể.
  • onUpdateVolume() – Gửi yêu cầu đến thiết bị nhận để sửa đổi chế độ phát theo số lượng cụ thể.
  • 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 tuyến đường nữa, cho phép khung giải phóng của chúng tôi.

Tất cả yêu cầu về bộ điều khiển chế độ phát, ngoại trừ yêu cầu thay đổi âm lượng, đều được chuyển hướng đến 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 phù hợp. Dưới đây là một ví dụ về cách triển khai phương thức này, phương thức này sẽ xử lý các lệnh cho một tuyến phương tiện phát lại 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 là bạn phải hiểu rằng lớp MediaRouteProvider.RouteController được dùng để đóng vai trò là trình bao bọc cho API đến thiết bị phát nội dung đa phương tiện của bạn. Cách 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

MediaRouter mẫu cho biết cách tạo trình cung cấp tuyến nội dung đa phương tiện tuỳ chỉnh.