O framework do roteador de mídia do Android permite que os fabricantes ativem a reprodução nos dispositivos
usando uma interface padronizada chamada MediaRouteProvider
.
Um provedor de rotas define uma interface comum para reproduzir mídia em um dispositivo receptor, tornando
tocar mídia em seu equipamento a partir de qualquer aplicativo Android compatível com mídia
rotas de prioridade mais alta.
Este guia discute como criar um provedor de roteamento de mídia para um dispositivo receptor e torná-lo
a outros aplicativos de reprodução de mídia executados no Android. Para usar essa API, você precisa:
você precisa conhecer as classes principais
MediaRouteProvider
,
MediaRouteProviderDescriptor
e
RouteController
.
Visão geral
O framework do roteador de mídia do Android permite que desenvolvedores de apps de mídia e dispositivos de reprodução de mídia
que os fabricantes se conectem usando uma API e uma interface de usuário comuns. Desenvolvedores de apps que
implementar uma interface MediaRouter
pode se conectar
e reproduzir conteúdo nos dispositivos que participam do framework do roteador de mídia. Mídia
os fabricantes de dispositivos de reprodução podem participar do framework publicando um MediaRouteProvider
que permita que outros aplicativos se conectem e
reproduzir mídia nos dispositivos receptores A Figura 1 ilustra como um app se conecta a um servidor
usando o framework do roteador de mídia.
Quando você cria um provedor de roteamento de mídia para seu dispositivo receptor, ele fornece o seguintes finalidades:
- Descrever e publicar os recursos do dispositivo receptor para que outros apps possam descobri-lo e usar seus recursos de reprodução.
- Unir a interface de programação do dispositivo receptor e a comunicação dele mecanismos de transporte para tornar o dispositivo compatível com o framework do roteador de mídia.
Distribuição de provedores de roteamento
Um provedor de roteamento de mídia é distribuído como parte de um app Android. Seu provedor de trajetos pode ser
disponibilizados para outros aplicativos estendendo
MediaRouteProviderService
ou agrupando sua implementação de
MediaRouteProvider
pelo próprio serviço e declarar uma intent
filtro para o provedor de roteamento de mídia. Essas etapas permitem que outros apps descubram e usem
seu roteamento de mídia.
Observação: o app que contém o provedor de roteamento de mídia também pode incluir um interface MediaRouter do provedor de rota, mas isso não é obrigatório.
Biblioteca de Suporte MediaRouter
As APIs do roteador de mídia são definidas na Biblioteca AndroidX MediaRouter É preciso adicionar essa biblioteca ao projeto de desenvolvimento de apps. Para mais informações sobre como adicionar bibliotecas de suporte aos seus projeto, consulte Configuração da Biblioteca de Suporte.
Cuidado:use o AndroidX
implementação do framework do roteador de mídia.
Não use o pacote android.media
mais antigo.
Criar um serviço de provedor
O framework do roteador de mídia precisa ser capaz de descobrir e se conectar ao seu provedor de roteamento de mídia.
para permitir que outros aplicativos usem sua rota. Para fazer isso, o framework do roteador de mídia
procura aplicativos que declarem uma ação de intent do provedor de roteamento de mídia. Quando outro app quiser
conectar ao provedor, o framework precisa ser capaz de invocá-lo e se conectar a ele. Assim, o provedor
precisam ser encapsulados em um Service
.
O código de exemplo a seguir mostra a declaração de um serviço de provedor de roteamento de mídia e as filtro de intent em um manifesto, permitindo que ele seja descoberto e usado pelo roteador de mídia estrutural:
<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>
Este exemplo de manifesto declara um serviço que une as classes reais do provedor de roteamento de mídia.
O framework do roteador de mídia do Android oferece a
Classe MediaRouteProviderService
para uso como um wrapper de serviço para
provedores de roteamento de mídia. O código de exemplo a seguir demonstra como usar esse wrapper
classe:
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); } }
Especificar recursos de roteamento
Os apps que se conectam ao framework do roteador de mídia podem descobrir o roteamento de mídia pelo seu declarações de manifesto do app, mas também precisam conhecer os recursos dos roteamentos de mídia estão fornecendo. Os roteamentos de mídia podem ser de diferentes tipos e ter recursos distintos, e outros apps precisa ser capaz de descobrir esses detalhes para determinar se são compatíveis com o trajeto.
O framework do roteador de mídia permite que você defina e publique os recursos dos seus
trajeto por objetos IntentFilter
, MediaRouteDescriptor
e MediaRouteProviderDescriptor
. Nesta seção, explicamos como usar essas
para publicar os detalhes do seu roteamento de mídia para outros aplicativos.
Categorias de roteamento
Como parte da descrição programática de seu provedor de roteamento de mídia, você precisa especificar se o provedor oferece suporte a reprodução remota, saída secundária ou ambas. Estes são os trajetos Categorias fornecidas pelo framework do roteador de mídia:
CATEGORY_LIVE_AUDIO
— Saída de áudio para um dispositivo de saída secundário, como um sistema de música sem fio.CATEGORY_LIVE_VIDEO
— Saída de vídeo para um dispositivo de saída secundário, como dispositivos de Display sem fio.CATEGORY_REMOTE_PLAYBACK
: reproduza vídeo ou áudio em um dispositivo separado que gerencia mídia recuperação, decodificação e reprodução, como Dispositivos Chromecast.
Para incluir essas configurações em uma descrição do seu roteamento de mídia, é necessário inseri-las no
um objeto IntentFilter
, que você vai adicionar depois a um
Objeto 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); } }
Se você especificar a intent CATEGORY_REMOTE_PLAYBACK
, também vai precisar definir quais tipos de mídia e
são compatíveis com seu provedor de roteamento de mídia. A próxima seção descreve como
especifique essas configurações para seu dispositivo.
Tipos de mídia e protocolos
Um provedor de roteamento de mídia para um dispositivo de reprodução remota precisa especificar os tipos de mídia e as transferências
os protocolos compatíveis. Você especifica essas configurações usando IntentFilter
.
a classe addDataScheme()
, e
addDataType()
desse objeto. A
snippet de código a seguir demonstra como definir um filtro de intent para oferecer suporte a vídeo remoto
reprodução usando http, https e 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); } } }
Controles de mídia
Um provedor de roteamento de mídia que oferece reprodução remota precisa especificar os tipos de controles de mídia com suporte. Estes são os tipos de controle gerais que o roteamento de mídia pode oferecer:
- Controles de reprodução, como tocar, pausar, voltar e avançar.
- Recursos de enfileiramento, que permitem ao app de envio adicionar e remover itens. de uma lista de reprodução mantida pelo dispositivo receptor.
- Recursos de sessão, que impedem o envio de apps de interferir nas outro fazendo com que o dispositivo receptor forneça um ID de sessão ao app solicitante e verificando esse ID a cada solicitação subsequente de controle de reprodução.
O exemplo de código a seguir demonstra como construir um filtro de intent para oferecer suporte controles básicos de reprodução do roteamento de mídia:
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); } ... }
Para mais informações sobre as intents de controle de reprodução disponíveis, consulte a
MediaControlIntent
.
MediaRouteProviderDescriptor
Depois de definir os recursos do seu roteamento de mídia usando objetos IntentFilter
, você poderá criar um objeto descritor para publicar no
o framework do roteador de mídia do Android. Esse objeto descritor contém as especificidades da sua mídia
e os recursos do roteamento, para que outros aplicativos possam determinar como interagir com seus
trajeto.
O código de exemplo a seguir demonstra como adicionar os filtros de intents criados anteriormente a um
MediaRouteProviderDescriptor
e definir o descritor para ser usado por
o framework do roteador de mídia:
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); }
Para mais informações sobre as configurações de descritor disponíveis, consulte a documentação de referência.
para MediaRouteDescriptor
e MediaRouteProviderDescriptor
.
Controlar roteamentos
Quando um aplicativo se conecta ao seu provedor de roteamento de mídia, o provedor recebe
pelo framework do roteador de mídia enviados ao seu trajeto por outros apps. Para lidar com isso
é necessário fornecer uma implementação de uma classe MediaRouteProvider.RouteController
, que processa os comandos
e lida com a comunicação real com o dispositivo receptor.
O framework do roteador de mídia chama o método onCreateRouteController()
do seu provedor de rotas para obter uma instância dessa classe e, em seguida, roteia solicitações para ela.
Esses são os principais métodos da classe MediaRouteProvider.RouteController
, que precisam ser implementados para
seu provedor de roteamento de mídia:
onSelect()
— Chamado quando um aplicativo seleciona seu trajeto para reprodução. Você usa esse método para fazer qualquer trabalho de preparação que possa ser necessário antes do início da reprodução da mídia.onControlRequest()
: envia comandos de reprodução específicos para o dispositivo receptor.onSetVolume()
: envia uma solicitação ao dispositivo receptor para definir o volume de reprodução como um um valor específico.onUpdateVolume()
: envia uma solicitação ao dispositivo receptor para modificar a reprodução. volume por um valor especificado.onUnselect()
: chamado quando um aplicativo cancela a seleção de um roteamento.onRelease()
: chamado quando o trajeto não é mais necessário para a estrutura, permitindo que ele libere sua do Google Cloud.
Todas as solicitações de controle de mídia, exceto mudanças de volume, são direcionadas para o onControlRequest()
. Sua implementação deste método deve analisar as solicitações de controle e respondê-las
adequadamente. Aqui está um exemplo de implementação deste método que processa comandos para uma
rota de mídia de reprodução remota:
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; } ... }
É importante entender que o objetivo da classe MediaRouteProvider.RouteController
é atuar como um wrapper
da API ao seu equipamento de reprodução de mídia. A implementação dos métodos nessa classe é
totalmente dependente da interface programática fornecida pelo dispositivo receptor.
Exemplo de código
O MediaRouter exemplo mostra como criar um provedor de roteamento de mídia personalizado.