Criar um receptor do Cast básico

googlecastnew500.png

Este codelab ensinará você a criar um app receptor compatível com Cast em um dispositivo com o Google Cast.

O que é o Google Cast?

O Google Cast permite que os usuários transmitam conteúdo de um dispositivo móvel para uma TV. Os usuários podem usar o dispositivo móvel ou o navegador Chrome do computador como um controle remoto para reprodução de mídia na TV.

O SDK do Google Cast permite que seu app controle dispositivos compatíveis com o Google Cast, como um sistema de som ou uma TV. Ele fornece os componentes de IU necessários de acordo com a Checklist de design do Google Cast.

Essa checklist é fornecida para facilitar a experiência do usuário no Google Cast e deixá-la mais previsível em todas as plataformas compatíveis. Clique aqui para saber mais.

O que vamos criar?

Ao concluir este codelab, você terá um app HTML5 que funciona como seu receptor personalizado capaz de exibir conteúdo de vídeo em dispositivos compatíveis com Cast.

O que você aprenderá

  • Como começar a configurar para o desenvolvimento de receptores
  • Informações básicas de um receptor compatível com Cast baseado no framework de aplicativos de transmissão
  • Como receber um vídeo por transmissão
  • Como integrar a Debug Logger
  • Como otimizar o receptor para smart displays

O que é necessário

  • A versão mais recente do navegador Google Chrome
  • O Node.js (link em inglês), o NPM, o http-server e o módulo ngrok
  • Um dispositivo com Google Cast, como um Chromecast ou Android TV, configurado com acesso à Internet
  • Uma TV ou um monitor com entrada HDMI

Experiência

  • Você precisa ter conhecimento prévio em desenvolvimento para a Web.
  • Também é necessário ter conhecimento prévio de como assistir TV :)

Como você usará este tutorial?

Apenas leitura Leitura e exercícios

Como você classificaria sua experiência com a criação de apps da Web?

Iniciante Intermediário Proficiente

Como você classificaria sua experiência com o ato de assistir TV?

Iniciante Intermediário Proficiente

Você pode fazer o download de todo o exemplo de código para seu computador…

Fazer o download do código-fonte

e descompactar o arquivo ZIP salvo.

Para poder usar seu receptor com um dispositivo de transmissão, ele precisa estar hospedado em algum lugar que o dispositivo possa acessar. Se você já tem um servidor compatível com HTTPS disponível, pule as instruções a seguir e anote o URL, porque ele será necessário na próxima seção.

Caso não haja um servidor disponível, não se preocupe. Você pode instalar o node.js (link em inglês), o http-server e o módulo de nó ngrok.

npm install -g http-server
npm install -g ngrok

Executar o servidor

Se você está usando o http-server, acesse seu console e faça o seguinte:

cd app-start
http-server

Você verá algo parecido com o exemplo a seguir:

Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://172.19.17.192:8080
Hit CTRL-C to stop the server

Observe a porta local usada e, em um novo terminal, faça o seguinte para expor seu receptor local por HTTPS usando o ngrok:

ngrok http 8080

Isso definirá um túnel ngrok para seu servidor HTTP local, atribuindo a você um endpoint protegido por HTTPS disponível globalmente que pode ser usado na próxima etapa (https://116ec943.eu.ngrok.io):

ngrok by @inconshreveable                                                                                                                                                                                                                                     (Ctrl+C to quit)

Session Status         online
Version                2.2.4
Web Interface          http://127.0.0.1:8080
Forwarding             http://116ec943.eu.ngrok.io -> localhost:8080
Forwarding             https://116ec943.eu.ngrok.io -> localhost:8080

É necessário manter o ngrok e o http-server em execução durante todo o codelab. Todas as mudanças feitas localmente serão disponibilizadas imediatamente.

É necessário registrar seu aplicativo para poder executar um receptor personalizado nos dispositivos Chromecast, como o que criaremos neste codelab. Após o registro, você receberá um ID de aplicativo que precisa ser usado pelo app remetente para realizar chamadas de API, como para inicializar um app receptor.

d8b39f5d33d33db4.png

Clique em "Add new application".

e8c19e57b85c7d.png

Selecione "Custom Receiver", que é o que estamos criando.

bf364a7d382e3c58.png

Insira os detalhes do novo receptor usando o URL recebido

na última seção. Anote o ID do aplicativo atribuído ao seu novo receptor.

Você também precisa registrar seu dispositivo com Google Cast para que ele possa acessar o aplicativo receptor antes de você publicá-lo. Após a publicação, o aplicativo ficará disponível para todos os dispositivos com Google Cast. Para os fins deste codelab, é recomendável trabalhar com um aplicativo receptor não publicado.

a446325da6ebd627.png

Clique em "Add new Device".

a21355793a3f4cd5.png

Insira o número de série impresso na parte traseira do seu dispositivo de transmissão e dê um nome descritivo para ele. Também é possível encontrar o número de série pela transmissão de tela no Chrome ao acessar o Play Console do SDK do Google Cast.

Leva de 5 a 15 minutos para que o receptor e o dispositivo estejam prontos para o teste. Após esse período, reinicie o dispositivo de transmissão.

Google_Chrome_logo_icon.png

Enquanto aguardamos o novo aplicativo receptor ficar pronto para o teste, vamos ver um exemplo desse aplicativo concluído. O receptor que criaremos será capaz de reproduzir mídia por streaming com taxa de bits adaptável. Usaremos um conteúdo de exemplo codificado para Dynamic Adaptive Streaming over HTTP (DASH).

No navegador, abra a ferramenta CaC (link em inglês).

7dbd91a75140c46f.png

  1. Você verá a nossa ferramenta CaC.
  2. Use o exemplo de ID padrão "CC1AD845" de receptor e clique no botão "Set app ID".
  3. Clique no botão Transmitir no canto superior esquerdo e selecione o dispositivo com Google Cast.

a02db8244a963fd6.png

  1. Navegue até a guia "Load Media" na parte superior.

acd63df355a0493.png

  1. Clique no botão "Load by Content" para iniciar um vídeo de exemplo.
  2. O vídeo será aberto no dispositivo com Google Cast para mostrar qual é a aparência da funcionalidade básica usando o receptor padrão.

Precisamos adicionar compatibilidade com o Google Cast ao app inicial que você transferiu por download. Aqui está parte da terminologia do Google Cast que será usada neste codelab:

  • Um app remetente é executado em um dispositivo móvel ou laptop.
  • Um app receptor é executado no dispositivo com Google Cast.

Agora você já pode criar com base no projeto inicial usando seu editor de texto favorito:

  1. Selecione o diretório android_studio_folder.pngapp-start no download do exemplo de código.
  2. Abra js/receiver.js e index.html.

Enquanto você trabalha neste codelab, o http-server precisa detectar as mudanças feitas. Se você perceber que ele não as está detectando, tente encerrar e reiniciar o http-server.

Design do app

O app receptor inicializa a sessão de transmissão e fica em espera até receber uma solicitação LOAD de um remetente, ou seja, o comando para iniciar uma mídia.

O app consiste em uma visualização principal, definida em index.html, e um arquivo JavaScript chamado js/receiver.js, contendo toda a lógica necessária para fazer nosso receptor funcionar.

index.html

Esse arquivo HTML conterá a IU do app receptor. Ele está vazio por enquanto, mas será modificado ao longo do codelab.

receiver.js

Esse script gerenciará toda a lógica do app receptor. Agora ele é apenas um arquivo vazio, mas nós o transformaremos, na próxima seção, em um receptor do Google Cast totalmente funcional com apenas algumas linhas de código.

Um receptor do Google Cast básico iniciará a sessão de transmissão durante a inicialização. Isso é necessário para informar a todos os aplicativos remetentes conectados que o receptor foi inicializado. Além disso, o novo SDK vem pré-configurado para processar mídias por streaming com taxa de bits adaptável (usando DASH, HLS e Smooth Streaming) e arquivos MP4 simples prontos para uso. Vamos fazer um teste.

Inicialização

Adicione o seguinte código ao index.html no cabeçalho:

<head>
  ...

  <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
</head>

Adicione o código a seguir ao index.html <body> antes de <footer> carregar o receiver.js, de modo a fornecer espaço ao SDK do receptor para exibir a IU padrão que é enviada com o script que você acabou de adicionar.

<cast-media-player></cast-media-player>

Agora, precisamos inicializar o SDK no js/receiver.js, que consiste em:

  • adquirir uma referência ao CastReceiverContext, seu ponto de entrada principal para todo o SDK do receptor;
  • armazenar uma referência ao PlayerManager, o objeto que processa a reprodução e fornece todos os hooks necessários para conectar sua própria lógica personalizada;
  • inicializar o SDK chamando start() no CastReceiverContext.

Adicione o seguinte ao js/receiver.js:

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

context.start();

Para os fins deste codelab, use a ferramenta CaC para testar o novo receptor.

Direcione seu navegador da Web para a ferramenta CaC (link em inglês).

72039b93a476e35e.png

Substitua o ID do app conforme registrado anteriormente no campo e clique em "Set App ID". Isso instrui a ferramenta a usar o receptor ao iniciar a sessão de transmissão.

Transmitir mídia

De modo geral, para abrir mídia em um dispositivo de transmissão, o seguinte precisa acontecer:

  1. O remetente cria um objeto MediaInfo JSON pelo SDK do Cast que modela um item de mídia.
  2. O remetente se conecta ao dispositivo de transmissão para iniciar o aplicativo receptor.
  3. O receptor carrega o objeto MediaInfo por uma solicitação LOAD para abrir o conteúdo.
  4. O receptor monitora e rastreia o status da mídia.
  5. O remetente envia comandos ao receptor para controlar a reprodução de acordo com as interações do usuário com o app remetente.

Nesta primeira tentativa básica, preencheremos o MediaInfo com um URL de recurso reproduzível, armazenado no MediaInfo.contentUrl.

Um remetente real usa um aplicativo identificador de mídia específico no MediaInfo.contentId. O receptor usa o contentId como um identificador para fazer chamadas adequadas à API de back-end de modo a resolver o URL real do recurso e defini-lo como o MediaInfo.contentUrl.. Além disso, o receptor também processa tarefas como aquisição de licenças de DRM ou injeção de informações sobre intervalos de anúncios.

Ampliaremos seu receptor para fazer algo parecido na próxima seção. Por enquanto, clique no ícone de transmissão e selecione seu dispositivo para abrir o receptor.

4954e949c3d3b232.png

Navegue até a guia "Load Media" e clique no botão "Load by Content". Seu receptor iniciará o conteúdo de exemplo.

ccd8cc258d0c1522.png

Então, o SDK do receptor já vem pronto para:

  • inicializar a sessão de transmissão;
  • processar solicitações LOAD recebidas de remetentes que contêm recursos reproduzíveis;
  • fornecer uma IU de player básica pronta para ser exibida na tela grande.

Fique à vontade para conhecer melhor a ferramenta CaC e o código dela antes de passar para a próxima seção. Vamos ampliar nosso receptor para comunicação com uma API de exemplo simples de modo a atender às solicitações LOAD recebidas dos remetentes.

Em concordância com a forma como a maioria dos desenvolvedores interage com os receptores do Google Cast em aplicativos reais, modificaremos nosso receptor para processar solicitações LOAD que fazem referência ao conteúdo de mídia pretendido pela própria chave de API, em vez de enviar por um URL de recurso reproduzível.

Os aplicativos normalmente fazem isso porque:

  • o remetente pode não saber o URL do conteúdo;
  • o aplicativo de transmissão foi projetado para processar a autenticação, outras lógicas de negócios ou chamadas de API diretamente no receptor.

Essa funcionalidade é implementada principalmente no método setMessageInterceptor() do PlayerManager. Isso permite que você intercepte as mensagens recebidas por tipo e as modifique antes que cheguem no gerenciador de mensagens interno do SDK. Nesta seção, estamos lidando com solicitações LOAD e faremos o seguinte:

  • Ler a solicitação LOAD recebida e o contentId personalizado dela
  • Fazer uma chamada GET para nossa API para pesquisar o recurso de streaming pelo contentId dela
  • Modificar a solicitação LOAD com o URL do stream
  • Modificar o objeto MediaInformation para definir os parâmetros de tipo do stream
  • Transmitir a solicitação ao SDK para reprodução ou rejeitar o comando se não for possível pesquisar a mídia solicitada

A API de exemplo fornecida demonstra os hooks do SDK para personalizar tarefas comuns do receptor e ainda conta com uma experiência, em sua maioria, pronta para uso.

API de exemplo

Acesse https://storage.googleapis.com/cpe-sample-media/content.json no navegador e analise nosso catálogo de vídeos de exemplo. O conteúdo inclui URLs para imagens de pôster no formato PNG, além dos streams DASH e HLS. Os streams DASH e HLS direcionam para fontes de áudio e vídeo com multiplexação revertida armazenadas em contêineres mp4 fragmentados.

{
  "bbb": {
    "author": "The Blender Project",
    "description": "Grumpy Bunny is grumpy",
    "poster": "https://[...]/[...]/BigBuckBunny/images/screenshot1.png",
    "stream": {
      "dash": "https://[...]/[...]/BigBuckBunny/BigBuckBunny_master.mpd",
      "hls": "https://[...]/[...]/BigBuckBunny/BigBuckBunny_master.m3u8",
    "title": "Big Buck Bunny"
  },
  "fbb_ad": {
    "author": "Google Inc.",
    "description": "Introducing Chromecast. The easiest way to enjoy [...]",
    "poster": "https://[...]/[...]/ForBiggerBlazes/images/screenshot8.png",
    "stream": {
      "dash": "https://[...]/[...]/ForBiggerBlazes/ForBiggerBlazes.mpd",
      "hls": "https://[...]/[...]/ForBiggerBlazes/ForBiggerBlazes.m3u8",
    "title": "For Bigger Blazes"
  },

  [...]

}

Na próxima etapa, mapearemos a chave de cada entrada (por exemplo, bbb, fbb_ad) para o URL do stream após o receptor ser chamado com uma solicitação LOAD.

Interceptar a solicitação LOAD

Nesta etapa, criaremos um interceptador de carregamento com uma função que faz uma solicitação XHR ao arquivo JSON hospedado. Quando o arquivo JSON for recebido, analisaremos o conteúdo e definiremos os metadados. Nas seções a seguir, personalizaremos os parâmetros MediaInformation para especificar o tipo de conteúdo.

Adicione o seguinte código ao seu arquivo js/receiver.js antes de chamar context.start().

function makeRequest (method, url) {
  return new Promise(function (resolve, reject) {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    xhr.send();
  });
}

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
        // Fetch content repository by requested contentId
        makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json').then(function (data) {
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            reject();
          } else {
            // Add metadata
            let metadata = new
               cast.framework.messages.GenericMediaMetadata();
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
        });
      });
    });

A próxima seção descreve como configurar a propriedade media da solicitação de carregamento para o conteúdo DASH.

Usar o conteúdo DASH da API de exemplo

Agora que preparamos o interceptador de carregamento, especificaremos o tipo de conteúdo para o receptor. Essas informações fornecerão ao receptor o URL da playlist principal e o Tipo MIME do stream. Adicione o seguinte código ao arquivo js/receiver.js no Promise() do interceptador LOAD:

...
playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
          ...
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.dash;
            request.media.contentType = 'application/dash+xml';
            ...
          }
        });
      });
    });

Depois de concluir essa etapa, avance para a seção "Testar" para tentar carregar com conteúdo DASH. Se você quiser testar o carregamento com o conteúdo HLS, confira a próxima etapa.

Usar o conteúdo HLS da API de exemplo

A API de exemplo inclui conteúdo HLS e DASH. Além de definir o contentType como fizemos na etapa anterior, a solicitação de carregamento precisará de algumas outras propriedades para usar os URLs HLS da API de exemplo. Quando o receptor está configurado para abrir streams HLS, o tipo de contêiner padrão esperado é o stream de transporte (TS, na sigla em inglês). Como resultado, o receptor tentará abrir os streams de MP4 de exemplo no formato TS apenas se a propriedade contentUrl for modificada. Na solicitação de carregamento, o objeto MediaInformation precisa ser modificado com mais propriedades para que o receptor saiba que o conteúdo é do tipo MP4, e não TS. Adicione o seguinte código ao seu arquivo js/receiver.js no interceptador para modificar as propriedades contentUrl e contentType. Adicione também as propriedades HlsSegmentFormat e HlsVideoSegmentFormat.

...
playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
          ...
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.hls;
            request.media.contentType = 'application/x-mpegurl';
            request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
            request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
            ...
          }
        });
      });
    });

Testar

Novamente, abra a ferramenta CaC (link em inglês) e defina o ID do app como o ID do app receptor. Selecione seu dispositivo usando o botão Transmitir.

Navegue até a guia "Load Media". Desta vez, exclua o texto no campo "Content URL" ao lado do botão "Load by Content". Isso forçará nosso aplicativo a enviar uma solicitação LOAD contendo apenas a referência contentId à nossa mídia.

3e830710c562189f.png

Supondo que tudo tenha funcionado bem com suas modificações no receptor, o interceptador cuidará da transformação do objeto MediaInfo em algo que o SDK possa abrir na tela.

Clique no botão "Load by Content" para ver se a mídia é aberta corretamente. Se quiser, você poderá mudar o ID de conteúdo no arquivo content.json.

Os smart displays são dispositivos com funcionalidade touch que possibilitam a compatibilidade de aplicativos receptores com controles com tela touchscreen.

Esta seção explica como otimizar o aplicativo receptor quando iniciado em smart displays e como personalizar os controles do player.

Acessar os controles de IU

O objeto de controles de IU para smart displays pode ser acessado usando o cast.framework.ui.Controls.GetInstance(). Adicione o seguinte código ao seu arquivo js/receiver.js acima de context.start():

...

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();

context.start();

Se você não usar o elemento <cast-media-player>, será necessário definir o touchScreenOptimizedApp nas CastReceiverOptions. Neste codelab, estamos usando o elemento <cast-media-player>.

context.start({ touchScreenOptimizedApp: true });

Os botões de controle padrão são atribuídos a cada slot com base no MetadataType e MediaStatus.supportedMediaCommands.

Controles de vídeo

Para MetadataType.MOVIE, MetadataType.TV_SHOW e MetadataType.GENERIC, o objeto de controles de IU para smart displays será exibido como no exemplo abaixo.

5f5a3d567813cf10.png

  1. --playback-logo-image
  2. MediaMetadata.subtitle
  3. MediaMetadata.title
  4. MediaStatus.currentTime
  5. MediaInformation.duration
  6. ControlsSlot.SLOT_SECONDARY_1: ControlsButton.QUEUE_PREV
  7. ControlsSlot.SLOT_PRIMARY_1: ControlsButton.SEEK_BACKWARD_30
  8. PLAY/PAUSE
  9. ControlsSlot.SLOT_PRIMARY_2: ControlsButton.SEEK_FORWARD_30
  10. ControlsSlot.SLOT_SECONDARY_2: ControlsButton.QUEUE_NEXT

Controles de áudio

Para MetadataType.MUSIC_TRACK, o objeto de controles de IU para smart displays será exibido como o mostrado abaixo:

e93d4557f324e8c0.png

  1. --playback-logo-image
  2. MusicTrackMediaMetadata.albumName
  3. MusicTrackMediaMetadata.title
  4. MusicTrackMediaMetadata.albumArtist
  5. MusicTrackMediaMetadata.images[0]
  6. MediaStatus.currentTime
  7. MediaInformation.duration
  8. ControlsSlot.SLOT_SECONDARY_1: ControlsButton.NO_BUTTON
  9. ControlsSlot.SLOT_PRIMARY_1: ControlsButton.QUEUE_PREV
  10. PLAY/PAUSE
  11. ControlsSlot.SLOT_PRIMARY_2: ControlsButton.QUEUE_NEXT
  12. ControlsSlot.SLOT_SECONDARY_2: ControlsButton.NO_BUTTON

Atualizar comandos de mídia compatíveis

O objeto de controles de IU também determina se um ControlsButton é exibido ou não com base no MediaStatus.supportedMediaCommands.

Quando o valor de supportedMediaCommands for igual a ALL_BASIC_MEDIA, o layout de controle padrão será exibido da seguinte forma:

5b97ada8d239239c.png

Quando o valor de supportedMediaCommands for igual a ALL_BASIC_MEDIA | QUEUE_PREV | QUEUE_NEXT, o layout de controle padrão será exibido da seguinte forma:

2d8267d4d8b68e87.png

Quando o valor de supportedMediaCommands for igual a PAUSE | QUEUE_PREV | QUEUE_NEXT, o layout de controle padrão será exibido da seguinte forma:

39c9afe44c4fa8dc.png

Quando as faixas de texto estiverem disponíveis, o botão de closed caption será sempre exibido no SLOT_1.

771952ab29e2eb21.png

Para mudar dinamicamente o valor de supportedMediaCommands após iniciar um contexto de receptor, você pode chamar PlayerManager.setSupportedMediaCommands para substituir o valor. Além disso, é possível adicionar um novo comando usando addSupportedMediaCommands ou remover um comando existente usando removeSupportedMediaCommands.

Personalizar botões de controle

É possível personalizar os controles usando oPlayerDataBinder. Adicione o seguinte código ao seu arquivo js/receiver.js abaixo de touchControls para definir o primeiro slot dos controles:

...

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    // Clear default buttons and re-assign
    touchControls.clearDefaultSlotAssignments();
    touchControls.assignButton(
      cast.framework.ui.ControlsSlot.SLOT_PRIMARY_1,
      cast.framework.ui.ControlsButton.SEEK_BACKWARD_30
    );
  });

context.start();

O navegador de mídia é um recurso do receptor CAF que possibilita aos usuários explorar outros conteúdos em dispositivos touchscreen. Para implementar isso, você usará o PlayerDataBinder de modo a definir a IU do BrowseContent. Em seguida, você pode preenchê-lo com BrowseItems de acordo com o conteúdo que você quer exibir.

BrowseContent

Veja abaixo um exemplo da IU do BrowseContent e as propriedades dela:

e40623e1907d983a.png

  1. BrowseContent.title
  2. BrowseContent.items

Proporção

Use targetAspectRatio property para selecionar a melhor proporção para seus recursos de imagem. Três proporções são compatíveis com o SDK do receptor CAF: SQUARE_1_TO_1, PORTRAIT_2_TO_3 e LANDSCAPE_16_TO_9.

BrowseItem

Use o BrowseItem para exibir o título, o subtítulo, a duração e a imagem de cada item:

838a0db6c9224710.png

  1. BrowseItem.image
  2. BrowseItem.duration
  3. BrowseItem.title
  4. BrowseItem.subtitle

Definir dados do navegador de mídia

Você pode fornecer uma lista de conteúdo de mídia para navegação chamando setBrowseContent. Adicione o seguinte código ao seu arquivo js/receiver.js abaixo do playerDataBinder e no listener de eventos MEDIA_CHANGED para definir os itens de navegação com um título "Up Next".

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

...

let browseItems = getBrowseItems();

function getBrowseItems() {
  let browseItems = [];
  makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json')
  .then(function (data) {
    for (let key in data) {
      let item = new cast.framework.ui.BrowseItem();
      item.entity = key;
      item.title = data[key].title;
      item.subtitle = data[key].description;
      item.image = new cast.framework.messages.Image(data[key].poster);
      item.imageType = cast.framework.ui.BrowseImageType.MOVIE;
      browseItems.push(item);
    }
  });
  return browseItems;
}

let browseContent = new cast.framework.ui.BrowseContent();
browseContent.title = 'Up Next';
browseContent.items = browseItems;
browseContent.targetAspectRatio = cast.framework.ui.BrowseImageAspectRatio.LANDSCAPE_16_TO_9;

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    ....

    // Media browse
    touchControls.setBrowseContent(browseContent);
  });

Clicar em um item de navegação de mídia acionará o interceptador LOAD. Adicione o seguinte código ao seu interceptador LOAD para mapear o request.media.contentId para request.media.entity usando o item de navegação de mídia:

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      ...

      // Map contentId to entity
      if (request.media && request.media.entity) {
        request.media.contentId = request.media.entity;
      }

      return new Promise((resolve, reject) => {
            ...
        });
    });

Você também pode definir o objeto BrowseContent como null para remover a IU do navegador de mídia.

O SDK do receptor do Google Cast fornece outra opção para os desenvolvedores depurarem facilmente os apps receptores usando a API CastDebugLogger e uma ferramenta CaC complementar para capturar registros.

Inicialização

Para incorporar a API, adicione o script de origem da CastDebugLogger ao arquivo index.html. A origem precisa ser declarada na tag <head> após a declaração do SDK do receptor do Google Cast.

<head>
  ...
  <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <!-- Cast Debug Logger -->
  <script src="//www.gstatic.com/cast/sdk/libs/devtools/debug_layer/caf_receiver_logger.js"></script>
</head>

No js/receiver.js, na parte superior do arquivo e abaixo do playerManager, adicione o seguinte código para recuperar a instância da CastDebugLogger e ativar o agente de registro:

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

Quando o agente de registro de depuração estiver ativado, uma sobreposição exibindo o DEBUG MODE aparecerá no receptor.

d9cb99e7742fc240.png

Registrar eventos do player

Com a CastDebugLogger, é possível registrar facilmente os eventos do player que são acionados pelo SDK do receptor CAF e usar diferentes níveis do agente para registrar os dados do evento. A configuração loggerLevelByEvents usa cast.framework.events.EventType e cast.framework.events.category para especificar quais eventos serão registrados.

Adicione o seguinte código abaixo da declaração castDebugLogger para registrar quando um evento CORE do player for acionado ou uma mudança de mediaStatus for transmitida:

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

Mensagens de registro e tags personalizadas

A API CastDebugLogger permite criar mensagens de registro que aparecem na sobreposição de depuração do receptor com cores diferentes. Os seguintes métodos de registro estão disponíveis, listados da maior para a menor prioridade:

  • castDebugLogger.error(custom_tag, message);
  • castDebugLogger.warn(custom_tag, message);
  • castDebugLogger.info(custom_tag, message);
  • castDebugLogger.debug(custom_tag, message);

Para cada método de registro, o primeiro parâmetro é uma tag personalizada. Ela pode ser qualquer string de identificação que você considere significativa. A CastDebugLogger usa tags para filtrar os registros. O uso das tags é explicado mais detalhadamente abaixo. O segundo parâmetro é a mensagem de registro.

Para mostrar os registros em ação, adicione-os ao interceptador LOAD.

playerManager.setMessageInterceptor(
  cast.framework.messages.MessageType.LOAD,
  request => {
    castDebugLogger.info(LOG_TAG, 'Intercepting LOAD request');

    // Map contentId to entity
    if (request.media && request.media.entity) {
      request.media.contentId = request.media.entity;
    }

    return new Promise((resolve, reject) => {
      // Fetch content repository by requested contentId
      makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json')
        .then(function (data) {
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            castDebugLogger.error(LOG_TAG, 'Content not found');
            reject();
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.dash;
            request.media.contentType = 'application/dash+xml';
            castDebugLogger.warn(LOG_TAG, 'Playable URL:', request.media.contentUrl);

            // Add metadata
            let metadata = new cast.framework.messages.MovieMediaMetadata();
            metadata.metadataType = cast.framework.messages.MetadataType.MOVIE;
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
      });
    });
  });

Você pode controlar quais mensagens aparecem na sobreposição de depuração configurando o nível de registro em loggerLevelByTags para cada tag personalizada. Por exemplo, ativar uma tag personalizada com o nível de registro cast.framework.LoggerLevel.DEBUG exibirá todas as mensagens adicionadas com erro, aviso, informações e mensagens de registros de depuração. Ativar uma tag personalizada com o nível WARNING exibirá apenas mensagens de registro de erro e de aviso.

A configuração loggerLevelByTags é opcional. Se uma tag personalizada não for configurada para o nível do agente de registro, todas as mensagens de registro serão exibidas na sobreposição de depuração.

Adicione o seguinte código abaixo do agente de registro de eventos CORE:

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
    [LOG_TAG]: cast.framework.LoggerLevel.DEBUG,
};

Sobreposição de depuração

A API CastDebugLogger fornece uma sobreposição de depuração no receptor para exibir as mensagens de registro personalizadas no dispositivo de transmissão. Use showDebugLogs para alternar a sobreposição de depuração e clearDebugLogs para limpar as mensagens de registro nela.

Adicione o código a seguir para visualizar a sobreposição de depuração no seu receptor.

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

// Show debug overlay
castDebugLogger.showDebugLogs(true);

// Clear log messages on debug overlay
// castDebugLogger.clearDebugLogs();

356c6ac94318539c.png

Agora você sabe criar um app receptor personalizado usando o SDK do receptor do Google Cast mais recente. Mais apps de exemplo podem ser encontrados no GitHub em github.com/googlecast (link em inglês).