Visão geral do MediaRouter

Para usar o framework do MediaRouter no seu app, é preciso ter uma instância do objeto MediaRouter e anexar um MediaRouter.Callback para detectar eventos de roteamento. O conteúdo enviado por um roteamento de mídia passa pelo associado MediaRouteProvider (exceto em alguns casos especiais, como um dispositivo de saída Bluetooth). A Figura 1 mostra uma visualização de alto nível da classes usadas para rotear conteúdo entre dispositivos.

Figura 1. Visão geral das principais classes de roteador de mídia usadas por apps.

Observação:se você quiser que seu app seja compatível Dispositivos com Google Cast, use o SDK do Cast e crie seu app como um remetente do Cast. Siga as instruções no Documentação do Google Cast em vez de usar o framework do MediaRouter diretamente.

Botão de roteamento de mídia

Apps para Android devem usar um botão de roteamento de mídia para fins de controle. O framework do MediaRouter fornece uma interface padrão para o botão, o que ajuda os usuários a reconhecer e usar o roteamento quando estão disponíveis. O botão de roteamento de mídia geralmente é colocado no lado direito a barra de ações do app, como mostrado na Figura 2.

Figura 2. Botão "Rota de mídia" na barra de ações.

Quando o usuário pressiona o botão de roteamento de mídia, os roteamentos disponíveis aparecem em uma lista, como mostrado na Figura 3.

Figura 3. Uma lista de roteamentos de mídia disponíveis, exibida depois de pressionar o botão correspondente.

Siga estas etapas para criar um botão de roteamento de mídia:

  1. Use uma AppCompatActivity.
  2. Defina o item de menu do botão de roteamento de mídia.
  3. Crie um MediaRouteSelector.
  4. Adicione o botão de roteamento de mídia à barra de ações.
  5. Crie e processe os métodos MediaRouter.Callback no ciclo de vida da sua atividade.

Essa seção descreve as quatro primeiras etapas. A seção seguinte descreve os métodos de callback.

Use uma AppCompatActivity.

Ao usar o framework do roteador de mídia em uma atividade, estenda a atividade do AppCompatActivity e importar pacote androidx.appcompat.app. Você deve adicionar o parâmetro androidx.appCompat:appcompatibilidade (link em inglês) e androidx.mediarouter:mediarouter. bibliotecas de suporte ao seu projeto de desenvolvimento de aplicativos. Para mais informações sobre como adicionar bibliotecas de suporte, ao projeto, consulte Introdução ao Android Jetpack.

Cuidado:use o androidx implementação do framework do roteador de mídia. Não use o pacote android.media mais antigo.

Crie um arquivo XML que defina um item menu para o botão de rota de mídia. A ação do item deve ser a classe MediaRouteActionProvider. Veja um exemplo de arquivo:

// myMediaRouteButtonMenuItem.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      >

    <item android:id="@+id/media_route_menu_item"
        android:title="@string/media_route_menu_title"
        app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
        app:showAsAction="always"
    />
</menu>

Criar um MediaRouteSelector

Os roteamentos que aparecem no menu do botão de roteamento de mídia são determinados por um MediaRouteSelector. Ampliar a atividade do app AppCompatActivity e criar o seletor quando a atividade for criada, chamando MediaRouteSelector.Builder do método onCreate(), como mostrado abaixo. no exemplo de código a seguir. O seletor é salvo em uma variável de classe, e os tipos de trajeto permitidos são especificados adicionando objetos MediaControlIntent:

Kotlin

class MediaRouterPlaybackActivity : AppCompatActivity() {

    private var mSelector: MediaRouteSelector? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Create a route selector for the type of routes your app supports.
        mSelector = MediaRouteSelector.Builder()
                // These are the framework-supported intents
                .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                .build()
    }
}

Java

public class MediaRouterPlaybackActivity extends AppCompatActivity {
    private MediaRouteSelector mSelector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Create a route selector for the type of routes your app supports.
        mSelector = new MediaRouteSelector.Builder()
                // These are the framework-supported intents
                .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                .build();
    }
}

Para a maioria dos aplicativos, o único o tipo de trajeto necessário é CATEGORY_REMOTE_PLAYBACK. Esse tipo de roteamento trata o dispositivo que está executando seu app como um controle remoto. O dispositivo receptor conectado processa toda a recuperação, decodificação e reprodução dos dados de conteúdo. É assim que os apps compatíveis com o Google Cast, como Chromecast, trabalho.

Alguns fabricantes oferecem compatibilidade com uma opção de roteamento especial conhecida como "saída secundária". Com esse roteamento, O app de mídia recupera, renderiza e faz streaming de vídeo ou música diretamente na tela e/ou nos alto-falantes do dispositivo receptor remoto selecionado. Use a saída secundária para enviar conteúdo para sistemas de música ou telas de vídeo sem fio. Para ativar a descoberta e uma seleção desses dispositivos, você precisa adicionar o CATEGORY_LIVE_AUDIO ou CATEGORY_LIVE_VIDEO categorias de controle de acesso ao MediaRouteSelector. Também é necessário criar e processar a própria caixa de diálogo Presentation.

Adicionar o botão de roteamento de mídia à barra de ações

Com o menu de roteamento de mídia e o MediaRouteSelector definidos, é possível adicionar o botão de roteamento de mídia a uma atividade. Substitua o método onCreateOptionsMenu() em cada uma das atividades para adicionar opções. .

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Inflate the menu and configure the media router action provider.
    menuInflater.inflate(R.menu.sample_media_router_menu, menu)

    // Attach the MediaRouteSelector to the menu item
    val mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item)
    val mediaRouteActionProvider =
            MenuItemCompat.getActionProvider(mediaRouteMenuItem) as MediaRouteActionProvider

    // Attach the MediaRouteSelector that you built in onCreate()
    selector?.also(mediaRouteActionProvider::setRouteSelector)

    // Return true to show the menu.
    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    // Inflate the menu and configure the media router action provider.
    getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);

    // Attach the MediaRouteSelector to the menu item
    MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
    MediaRouteActionProvider mediaRouteActionProvider =
            (MediaRouteActionProvider)MenuItemCompat.getActionProvider(
            mediaRouteMenuItem);
    // Attach the MediaRouteSelector that you built in onCreate()
    mediaRouteActionProvider.setRouteSelector(selector);

    // Return true to show the menu.
    return true;
}

Para mais informações sobre como implementar a barra de ações no seu app, consulte a barra de ações guia para desenvolvedores.

Também é possível adicionar um botão de roteamento de mídia como um MediaRouteButton em qualquer visualização. Você precisa anexar um MediaRouteSelector ao botão por meio do método setRouteSelector(). Consulte a Lista de verificação de design do Google Cast para orientações sobre como incorporar o botão de roteamento de mídia em seu aplicativo.

Callbacks do MediaRouter

Todos os apps em execução no mesmo dispositivo compartilham uma única instância do MediaRouter e as rotas dela. (filtrados por app pelo MediaRouteSeletor do app). Cada atividade se comunica com o MediaRouter. usando a própria implementação de MediaRouter.Callback métodos. O MediaRouter chama os métodos de callback sempre que o usuário seleciona, muda ou desconecta um roteamento.

Há vários métodos no callback que você pode modificar para receber informações sobre eventos de roteamento. No mínimo, sua implementação da classe MediaRouter.Callback precisa substituir onRouteSelected() e onRouteUnselected()

Como o MediaRouter é um recurso compartilhado, o app precisa gerenciar os callbacks do MediaRouter. em resposta aos callbacks habituais do ciclo de vida da atividade:

  • Quando a atividade for criada (onCreate(Bundle)), pegue um ponteiro para MediaRouter e segure-o por todo o ciclo de vida do app.
  • Anexar callbacks ao MediaRouter quando a atividade se tornar visível (onStart()) e desanexá-los quando ela estiver oculta onStop()).

O exemplo de código a seguir demonstra como criar e salvar o objeto de callback, como ver uma instância de MediaRouter e como gerenciar callbacks. Observe o uso da sinalização CALLBACK_FLAG_REQUEST_DISCOVERY ao anexar os callbacks em onStart(). Isso permite que o MediaRouteSelector atualize o link do botão de roteamento de mídia lista de rotas disponíveis.

Kotlin

class MediaRouterPlaybackActivity : AppCompatActivity() {

    private var mediaRouter: MediaRouter? = null
    private var mSelector: MediaRouteSelector? = null

    // Variables to hold the currently selected route and its playback client
    private var mRoute: MediaRouter.RouteInfo? = null
    private var remotePlaybackClient: RemotePlaybackClient? = null

    // Define the Callback object and its methods, save the object in a class variable
    private val mediaRouterCallback = object : MediaRouter.Callback() {

        override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) {
            Log.d(TAG, "onRouteSelected: route=$route")
            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
                // Stop local playback (if necessary)
                // ...

                // Save the new route
                mRoute = route

                // Attach a new playback client
                remotePlaybackClient =
                    RemotePlaybackClient(this@MediaRouterPlaybackActivity, mRoute)

                // Start remote playback (if necessary)
                // ...
            }
        }

        override fun onRouteUnselected(
                router: MediaRouter,
                route: MediaRouter.RouteInfo,
                reason: Int
        ) {
            Log.d(TAG, "onRouteUnselected: route=$route")
            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {

                // Changed route: tear down previous client
                mRoute?.also {
                    remotePlaybackClient?.release()
                    remotePlaybackClient = null
                }

                // Save the new route
                mRoute = route

                when (reason) {
                    MediaRouter.UNSELECT_REASON_ROUTE_CHANGED -> {
                        // Resume local playback (if necessary)
                        // ...
                    }
                }
            }
        }
    }


    // Retain a pointer to the MediaRouter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Get the media router service.
        mediaRouter = MediaRouter.getInstance(this)
        ...
    }

    // Use this callback to run your MediaRouteSelector to generate the
    // list of available media routes
    override fun onStart() {
        mSelector?.also { selector ->
            mediaRouter?.addCallback(selector, mediaRouterCallback,
                    MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY)
        }
        super.onStart()
    }

    // Remove the selector on stop to tell the media router that it no longer
    // needs to discover routes for your app.
    override fun onStop() {
        mediaRouter?.removeCallback(mediaRouterCallback)
        super.onStop()
    }
    ...
}

Java

public class MediaRouterPlaybackActivity extends AppCompatActivity {
    private MediaRouter mediaRouter;
    private MediaRouteSelector mSelector;

    // Variables to hold the currently selected route and its playback client
    private MediaRouter.RouteInfo mRoute;
    private RemotePlaybackClient remotePlaybackClient;

    // Define the Callback object and its methods, save the object in a class variable
    private final MediaRouter.Callback mediaRouterCallback =
            new MediaRouter.Callback() {

        @Override
        public void onRouteSelected(MediaRouter router, RouteInfo route) {
            Log.d(TAG, "onRouteSelected: route=" + route);

            if (route.supportsControlCategory(
                MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){
                // Stop local playback (if necessary)
                // ...

                // Save the new route
                mRoute = route;

                // Attach a new playback client
                remotePlaybackClient = new RemotePlaybackClient(this, mRoute);

                // Start remote playback (if necessary)
                // ...
            }
        }

        @Override
        public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) {
            Log.d(TAG, "onRouteUnselected: route=" + route);

            if (route.supportsControlCategory(
                MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){

                // Changed route: tear down previous client
                if (mRoute != null && remotePlaybackClient != null) {
                    remotePlaybackClient.release();
                    remotePlaybackClient = null;
                }

                // Save the new route
                mRoute = route;

                if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
                    // Resume local playback  (if necessary)
                    // ...
                }
            }
        }
    }


    // Retain a pointer to the MediaRouter
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get the media router service.
        mediaRouter = MediaRouter.getInstance(this);
        ...
    }

    // Use this callback to run your MediaRouteSelector to generate the list of available media routes
    @Override
    public void onStart() {
        mediaRouter.addCallback(mSelector, mediaRouterCallback,
                MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
        super.onStart();
    }

    // Remove the selector on stop to tell the media router that it no longer
    // needs to discover routes for your app.
    @Override
    public void onStop() {
        mediaRouter.removeCallback(mediaRouterCallback);
        super.onStop();
    }
    ...
}

O framework do roteador de mídia também oferece classe MediaRouteDiscoveryFragment, que cuida de adicionar e removendo o callback de uma atividade.

Observação:se você estiver criando um app de reprodução de música e quiser que ele seja reproduzido música em segundo plano, você precisa criar uma Service para reprodução e chamar o framework do roteador de mídia nos callbacks do ciclo de vida do serviço.

Controlar um roteamento de reprodução remota

Quando você seleciona um roteamento de reprodução remota, seu app funciona como um controle remoto. O dispositivo na outra extremidade do trajeto lida com todas as funções de recuperação, decodificação e reprodução de dados de conteúdo. Os controles na IU do seu app se comunicam com o dispositivo receptor usando um RemotePlaybackClient.

A classe RemotePlaybackClient fornece outros métodos para gerenciar a reprodução de conteúdo. Veja alguns dos principais métodos de reprodução da classe RemotePlaybackClient:

  • play(): reproduz uma música específica arquivo de mídia, especificado por um Uri.
  • pause(): pause o faixa de mídia em reprodução no momento.
  • resume() — Continuar a reprodução da faixa atual após um comando de pausa.
  • seek(): mover para um local específico na faixa atual.
  • release(): elimine o conexão do seu app com o dispositivo de reprodução remota.

É possível usar esses métodos para anexar ações aos controles de mídia fornecidos no app. A maioria desses métodos também permite incluir um objeto de callback para que você possa monitorar o progresso da tarefa de reprodução ou solicitação de controle.

A classe RemotePlaybackClient também aceita o enfileiramento de vários itens de mídia para reprodução e gerenciamento da fila de mídia.

Exemplo de código

O BasicMediaRouter do Android e MediaRouter Os exemplos demonstram ainda mais o uso da API MediaRouter.