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
, MediaRouteProviderDescriptor
và RouteController
.
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.
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 MediaRouteDescriptor
và MediaRouteProviderDescriptor
. 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()
và 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 MediaRouteDescriptor
và MediaRouteProviderDescriptor
.
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.