Платформа медиамаршрутизатора Android позволяет производителям включать воспроизведение на своих устройствах через стандартизированный интерфейс, называемый MediaRouteProvider
. Поставщик маршрутов определяет общий интерфейс для воспроизведения мультимедиа на устройстве-приемнике, что позволяет воспроизводить мультимедиа на вашем оборудовании из любого приложения Android, поддерживающего медиамаршруты.
В этом руководстве обсуждается, как создать поставщика маршрутов мультимедиа для устройства-приемника и сделать его доступным для других приложений воспроизведения мультимедиа, работающих на Android. Чтобы использовать этот API, вы должны быть знакомы с ключевыми классами MediaRouteProvider
, MediaRouteProviderDescriptor
и RouteController
.
Обзор
Платформа медиамаршрутизатора Android позволяет разработчикам мультимедийных приложений и производителям устройств воспроизведения мультимедиа подключаться через общий API и общий пользовательский интерфейс. Разработчики приложений, реализующие интерфейс MediaRouter
, могут затем подключиться к платформе и воспроизводить контент на устройствах, которые участвуют в платформе медиамаршрутизатора. Производители устройств воспроизведения мультимедиа могут участвовать в этой платформе, опубликовав MediaRouteProvider
, который позволяет другим приложениям подключаться и воспроизводить мультимедиа на устройствах-приемниках. На рис. 1 показано, как приложение подключается к принимающему устройству через структуру медиамаршрутизатора.

Рис. 1. Обзор того, как классы поставщиков медиа-маршрутов обеспечивают связь между мультимедийным приложением и устройством-получателем.
Когда вы создаете поставщика медиа-маршрутов для вашего приемного устройства, этот поставщик служит следующим целям:
- Опишите и опубликуйте возможности устройства-приемника, чтобы другие приложения могли его обнаружить и использовать функции воспроизведения.
- Оберните программный интерфейс приемного устройства и его механизмы передачи данных, чтобы сделать устройство совместимым с платформой медиамаршрутизатора.
Распределение провайдеров маршрутов
Поставщик медиамаршрутов распространяется как часть приложения Android. Ваш поставщик маршрутов можно сделать доступным для других приложений, расширив MediaRouteProviderService
или обернув вашу реализацию MediaRouteProvider
собственной службой и объявив фильтр намерений для поставщика медиамаршрутов. Эти шаги позволят другим приложениям обнаружить и использовать ваш медиа-маршрут.
Примечание. Приложение, содержащее поставщика маршрутов мультимедиа, также может включать в себя интерфейс MediaRouter для поставщика маршрутов, но это не обязательно.
Библиотека поддержки MediaRouter
API-интерфейсы медиамаршрутизатора определены в библиотеке AndroidX MediaRouter. Эту библиотеку необходимо добавить в проект разработки приложения. Дополнительную информацию о добавлении библиотек поддержки в проект см. в разделе Настройка библиотеки поддержки . Внимание: обязательно используйте реализацию платформы медиамаршрутизатора AndroidX
. Не используйте старый пакет android.media
.
Создание службы провайдера
Платформа медиамаршрутизатора должна иметь возможность обнаруживать вашего поставщика медиамаршрутов и подключаться к нему, чтобы другие приложения могли использовать ваш маршрут. Для этого платформа медиамаршрутизатора ищет приложения, которые объявляют действие намерения поставщика медиамаршрута. Когда другое приложение хочет подключиться к вашему провайдеру, платформа должна иметь возможность вызывать его и подключаться к нему, поэтому ваш провайдер должен быть инкапсулирован в Service
.
В следующем примере кода показано объявление службы поставщика маршрутов мультимедиа и фильтра намерений в манифесте, который позволяет обнаруживать и использовать платформу медиамаршрутизатора:
<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>
В этом примере манифеста объявляется служба, которая обертывает фактические классы поставщиков маршрутов мультимедиа. Платформа медиамаршрутизатора Android предоставляет класс MediaRouteProviderService
для использования в качестве оболочки службы для поставщиков медиамаршрутов. В следующем примере кода показано, как использовать этот класс-оболочку:
class SampleMediaRouteProviderService : MediaRouteProviderService() {
override fun onCreateMediaRouteProvider(): MediaRouteProvider {
return SampleMediaRouteProvider(this)
}
}
public class SampleMediaRouteProviderService extends MediaRouteProviderService {
@Override
public MediaRouteProvider onCreateMediaRouteProvider() {
return new SampleMediaRouteProvider(this);
}
}
Определение возможностей маршрута
Приложения, подключающиеся к платформе медиа-маршрутизатора, могут обнаружить ваш медиа-маршрут через декларации манифеста вашего приложения, но им также необходимо знать возможности предоставляемых вами медиа-маршрутов. Медиа-маршруты могут быть разных типов и иметь разные функции, и другие приложения должны иметь возможность обнаруживать эти детали, чтобы определить, совместимы ли они с вашим маршрутом.
Платформа медиамаршрутизатора позволяет вам определять и публиковать возможности вашего медиамаршрута через объекты IntentFilter
, объекты MediaRouteDescriptor
и MediaRouteProviderDescriptor
. В этом разделе объясняется, как использовать эти классы для публикации сведений о вашем медиа-маршруте для других приложений.
Категории маршрутов
В программном описании вашего поставщика маршрутов мультимедиа вы должны указать, поддерживает ли ваш поставщик удаленное воспроизведение, вторичный вывод или и то, и другое. Это категории маршрутов, предоставляемые платформой медиамаршрутизатора:
-
CATEGORY_LIVE_AUDIO
— вывод звука на вторичное устройство вывода, например музыкальную систему с поддержкой беспроводной связи. -
CATEGORY_LIVE_VIDEO
— вывод видео на вторичное устройство вывода, например устройства беспроводного отображения. -
CATEGORY_REMOTE_PLAYBACK
— воспроизведение видео или аудио на отдельном устройстве, которое занимается поиском, декодированием и воспроизведением мультимедиа, например устройствах Chromecast .
Чтобы включить эти настройки в описание вашего медиа-маршрута, вы вставляете их в объект IntentFilter
, который позже добавляете в объект MediaRouteDescriptor
:
class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {
companion object {
private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = IntentFilter().run {
addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
arrayListOf(this)
}
}
}
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);
}
}
Если вы укажете намерение CATEGORY_REMOTE_PLAYBACK
, вы также должны определить, какие типы мультимедиа и элементы управления воспроизведением поддерживаются вашим поставщиком маршрутов мультимедиа. В следующем разделе описывается, как указать эти параметры для вашего устройства.
Типы носителей и протоколы
Поставщик маршрута мультимедиа для удаленного устройства воспроизведения должен указать типы мультимедиа и протоколы передачи, которые он поддерживает. Эти параметры указываются с помощью класса IntentFilter
и методов addDataScheme()
и addDataType()
этого объекта. В следующем фрагменте кода показано, как определить фильтр намерений для поддержки удаленного воспроизведения видео с использованием http, https и протокола потоковой передачи в реальном времени (RTSP):
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)
}
}
...
}
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);
}
}
}
Элементы управления воспроизведением
Поставщик маршрутов мультимедиа, предлагающий удаленное воспроизведение, должен указать типы элементов управления мультимедиа, которые он поддерживает. Ниже приведены общие типы управления, которые могут обеспечивать медиамаршруты:
- Элементы управления воспроизведением , такие как воспроизведение, пауза, перемотка назад и вперед.
- Функции организации очередей , которые позволяют приложению-отправителю добавлять и удалять элементы из списка воспроизведения, который поддерживается устройством-получателем.
- Функции сеанса , которые предотвращают взаимодействие отправляющих приложений друг с другом за счет того, что устройство-получатель предоставляет идентификатор сеанса запрашивающему приложению, а затем проверяет этот идентификатор при каждом последующем запросе на управление воспроизведением.
В следующем примере кода показано, как создать фильтр намерений для поддержки основных элементов управления воспроизведением маршрута мультимедиа:
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)
}
}
...
}
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);
}
...
}
Дополнительные сведения о доступных намерениях управления воспроизведением см. в классе MediaControlIntent
.
Медиарутепровидердескриптор
После определения возможностей вашего медиа-маршрута с помощью объектов IntentFilter
вы можете создать объект дескриптора для публикации в платформе медиа-маршрутизатора Android. Этот объект дескриптора содержит сведения о возможностях вашего медиа-маршрута, чтобы другие приложения могли определить, как взаимодействовать с вашим медиа-маршрутом.
В следующем примере кода показано, как добавить ранее созданные фильтры намерений в MediaRouteProviderDescriptor
и установить дескриптор для использования платформой медиамаршрутизатора:
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
}
...
}
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);
}
Дополнительные сведения о доступных настройках дескриптора см. в справочной документации для MediaRouteDescriptor
и MediaRouteProviderDescriptor
.
Управление маршрутами
Когда приложение подключается к вашему провайдеру медиа-маршрутизации, поставщик получает команды воспроизведения через структуру медиа-маршрутизатора, отправленные на ваш маршрут другими приложениями. Чтобы обрабатывать эти запросы, вы должны предоставить реализацию класса MediaRouteProvider.RouteController
, который обрабатывает команды и обеспечивает фактическую связь с вашим устройством-получателем.
Платформа медиамаршрутизатора вызывает метод onCreateRouteController()
вашего провайдера маршрутов, чтобы получить экземпляр этого класса, а затем направляет к нему запросы. Это ключевые методы класса MediaRouteProvider.RouteController
, которые вы должны реализовать для своего поставщика медиамаршрутов:
-
onSelect()
— вызывается, когда приложение выбирает маршрут для воспроизведения. Этот метод используется для выполнения любых подготовительных работ, которые могут потребоваться перед началом воспроизведения мультимедиа. -
onControlRequest()
— отправляет определенные команды воспроизведения на принимающее устройство. -
onSetVolume()
— отправляет запрос принимающему устройству на установку определенного значения громкости воспроизведения. -
onUpdateVolume()
— отправляет запрос принимающему устройству на изменение громкости воспроизведения на указанную величину. -
onUnselect()
— вызывается, когда приложение отменяет выбор маршрута. -
onRelease()
— вызывается, когда маршрут больше не нужен платформе, что позволяет ей освободить свои ресурсы.
Все запросы на управление воспроизведением, кроме изменения громкости, направляются в метод onControlRequest()
. Ваша реализация этого метода должна анализировать запросы управления и отвечать на них соответствующим образом. Вот пример реализации этого метода, который обрабатывает команды для маршрута мультимедиа удаленного воспроизведения:
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
}
}
...
}
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;
}
...
}
Важно понимать, что класс MediaRouteProvider.RouteController
предназначен для работы в качестве оболочки API для вашего оборудования воспроизведения мультимедиа. Реализация методов этого класса полностью зависит от программного интерфейса вашего принимающего устройства.
Пример кода
В примере MediaRouter показано, как создать собственный поставщик маршрутов мультимедиа.