Adicionar recursos principais ao receptor da Web personalizado

Esta página contém snippets de código e descrições dos recursos disponíveis para um app de receptor personalizado da Web.

  1. Um elemento cast-media-player que representa a interface do player integrada fornecida com o Web Receiver.
  2. Estilo personalizado semelhante a CSS para o elemento cast-media-player para estilizar vários elementos da interface, como background-image, splash-image e font-family.
  3. Um elemento de script para carregar o framework do receptor da Web.
  4. código JavaScript para interceptar mensagens e lidar com eventos.
  5. Fila para reprodução automática.
  6. Opções para configurar a reprodução.
  7. Opções para definir o contexto do receptor da Web.
  8. Opções para definir comandos compatíveis com o app Web Receiver.
  9. Uma chamada JavaScript para iniciar o aplicativo receptor da Web.

Configuração e opções do aplicativo

Configurar o aplicativo

O CastReceiverContext é a classe mais externa exposta ao desenvolvedor. Ele gerencia o carregamento de bibliotecas subjacentes e processa a inicialização do SDK do receptor da Web. O SDK fornece APIs que permitem que os desenvolvedores de aplicativos configurem o SDK usando CastReceiverOptions. Essas configurações são avaliadas uma vez por inicialização do aplicativo e transmitidas ao SDK ao definir o parâmetro opcional na chamada para start.

O exemplo abaixo mostra como substituir o comportamento padrão para detectar se uma conexão de remetente ainda está ativamente conectada. Quando o Web Receiver não consegue se comunicar com um remetente por maxInactivity segundos, um evento SENDER_DISCONNECTED é enviado. A configuração abaixo modifica esse tempo limite. Isso pode ser útil ao depurar problemas, porque impede que o app receptor da Web feche a sessão do depurador remoto do Google Chrome quando não houver remetentes conectados no estado IDLE.

const context = cast.framework.CastReceiverContext.getInstance();
const options = new cast.framework.CastReceiverOptions();
options.maxInactivity = 3600; // Development only
context.start(options);

Configurar o player

Ao carregar conteúdo, o SDK do receptor da Web oferece uma maneira de configurar variáveis de reprodução, como informações de DRM, configurações de nova tentativa e gerenciadores de solicitação usando cast.framework.PlaybackConfig. Essas informações são processadas por PlayerManager e avaliadas no momento em que os jogadores são criados. Os players são criados sempre que um novo carregamento é transmitido ao SDK do receptor da Web. As modificações no PlaybackConfig após a criação do jogador são avaliadas no próximo carregamento de conteúdo. O SDK fornece os métodos abaixo para modificar o PlaybackConfig.

  • CastReceiverOptions.playbackConfig para substituir as opções de configuração padrão ao inicializar o CastReceiverContext.
  • PlayerManager.getPlaybackConfig() para receber a configuração atual.
  • PlayerManager.setPlaybackConfig() para substituir a configuração atual. Essa configuração é aplicada a todos os carregamentos subsequentes ou até que ela seja substituída novamente.
  • PlayerManager.setMediaPlaybackInfoHandler() para aplicar outras configurações apenas ao item de mídia que está sendo carregado sobre as configurações atuais. O gerenciador é chamado logo antes da criação do player. As mudanças feitas aqui não são permanentes e não são incluídas em consultas para getPlaybackConfig(). Quando o próximo item de mídia for carregado, esse gerenciador será chamado novamente.

O exemplo abaixo mostra como definir PlaybackConfig ao inicializar a CastReceiverContext. A configuração substitui as solicitações de saída para receber manifestos. Ele especifica que as solicitações de controle de acesso do CORS precisam ser feitas usando credenciais como cookies ou cabeçalhos de autorização.

const playbackConfig = new cast.framework.PlaybackConfig();
playbackConfig.manifestRequestHandler = requestInfo => {
  requestInfo.withCredentials = true;
};
context.start({playbackConfig: playbackConfig});

O exemplo abaixo mostra como substituir PlaybackConfig usando o getter e o setter fornecidos em PlayerManager. Essa configuração configura o player para retomar a reprodução de conteúdo após o carregamento de um segmento.

const playerManager =
    cast.framework.CastReceiverContext.getInstance().getPlayerManager();
const playbackConfig = (Object.assign(
            new cast.framework.PlaybackConfig(), playerManager.getPlaybackConfig()));
playbackConfig.autoResumeNumberOfSegments = 1;
playerManager.setPlaybackConfig(playbackConfig);

O exemplo abaixo mostra como substituir PlaybackConfig para uma solicitação de carregamento específica usando o gerenciador de informações de reprodução de mídia. O gerenciador chama um método implementado pelo aplicativo getLicenseUrlForMedia para receber o licenseUrl do contentId do item atual.

playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
  const mediaInformation = loadRequestData.media;
  playbackConfig.licenseUrl = getLicenseUrlForMedia(mediaInformation.contentId);

  return playbackConfig;
});

Listener de eventos

O SDK do receptor da Web permite que seu app receptor da Web gerencie eventos do player. Ele usa um parâmetro cast.framework.events.EventType (ou uma matriz desses parâmetros) que especifica os eventos que devem acionar o listener. As matrizes pré-configuradas de cast.framework.events.EventType que são úteis para depuração podem ser encontradas em cast.framework.events.category. O parâmetro do evento oferece mais informações.

Por exemplo, se você quiser saber quando uma mudança mediaStatus está sendo transmitida, use a seguinte lógica para processar o evento:

const playerManager =
    cast.framework.CastReceiverContext.getInstance().getPlayerManager();
playerManager.addEventListener(
    cast.framework.events.EventType.MEDIA_STATUS, (event) => {
      // Write your own event handling code, for example
      // using the event.mediaStatus value
});

Interceptação de mensagens

Com o SDK do receptor da Web, seu app receptor pode interceptar mensagens e executar um código personalizado nelas. O interceptador de mensagens usa um parâmetro cast.framework.messages.MessageType que especifica o tipo de mensagem que precisa ser interceptada.

O interceptador precisa retornar a solicitação modificada ou uma promessa que seja resolvida com o valor da solicitação modificada. Se você retornar null, isso impedirá que o gerenciador de mensagens padrão seja chamado. Consulte Como carregar mídia para mais detalhes.

Por exemplo, se você quiser alterar os dados da solicitação de carregamento, poderá usar a seguinte lógica para interceptá-los e modificá-los:

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

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      const error = new cast.framework.messages.ErrorData(
                      cast.framework.messages.ErrorType.LOAD_FAILED);
      if (!loadRequestData.media) {
        error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
        return error;
      }

      if (!loadRequestData.media.entity) {
        return loadRequestData;
      }

      return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
                                          loadRequestData.credentials)
        .then(asset => {
          if (!asset) {
            throw cast.framework.messages.ErrorReason.INVALID_REQUEST;
          }

          loadRequestData.media.contentUrl = asset.url;
          loadRequestData.media.metadata = asset.metadata;
          loadRequestData.media.tracks = asset.tracks;
          return loadRequestData;
        }).catch(reason => {
          error.reason = reason; // cast.framework.messages.ErrorReason
          return error;
        });
    });

context.start();

Tratamento de erros

Quando ocorrem erros no interceptador de mensagens, seu app receptor da Web precisa retornar um cast.framework.messages.ErrorType e cast.framework.messages.ErrorReason adequados.

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      const error = new cast.framework.messages.ErrorData(
                      cast.framework.messages.ErrorType.LOAD_CANCELLED);
      if (!loadRequestData.media) {
        error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
        return error;
      }

      ...

      return fetchAssetAndAuth(loadRequestData.media.entity,
                               loadRequestData.credentials)
        .then(asset => {
          ...
          return loadRequestData;
        }).catch(reason => {
          error.reason = reason; // cast.framework.messages.ErrorReason
          return error;
        });
    });

Interceptação de mensagens x listener de eventos

Estas são algumas diferenças importantes entre a interceptação de mensagens e o listener de eventos:

  • Um listener de eventos não permite que você modifique os dados da solicitação.
  • Um listener de eventos é melhor usado para acionar análises ou uma função personalizada.
playerManager.addEventListener(cast.framework.events.category.CORE,
    event => {
        console.log(event);
    });
  • A interceptação de mensagens permite ouvir uma mensagem, interceptá-la e modificar os próprios dados da solicitação.
  • A interceptação de mensagens é melhor usada para processar a lógica personalizada em relação aos dados da solicitação.

Como carregar mídia

MediaInformation fornece várias propriedades para carregar mídia na mensagem cast.framework.messages.MessageType.LOAD, incluindo entity, contentUrl e contentId.

  • O entity é a propriedade sugerida para uso na implementação dos apps remetente e receptor. A propriedade é um URL de link direto que pode ser uma playlist ou um conteúdo de mídia. Seu aplicativo deve analisar esse URL e preencher pelo menos um dos outros dois campos.
  • O contentUrl corresponde ao URL reproduzível que o player usará para carregar o conteúdo. Por exemplo, esse URL pode apontar para um manifesto DASH.
  • O contentId pode ser um URL de conteúdo reproduzível (semelhante ao da propriedade contentUrl) ou um identificador exclusivo do conteúdo ou da playlist que está sendo carregada. Ao usar essa propriedade como um identificador, seu aplicativo precisa preencher um URL jogável no contentUrl.

A sugestão é usar entity para armazenar o ID real ou os parâmetros de chave e usar contentUrl para o URL da mídia. Um exemplo disso é mostrado no snippet a seguir, em que o entity está presente na solicitação LOAD e o contentUrl reproduzível é recuperado:

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

      if (!loadRequestData.media.entity) {
        // Copy the value from contentId for legacy reasons if needed
        loadRequestData.media.entity = loadRequestData.media.contentId;
      }

      return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
                                          loadRequestData.credentials)
        .then(asset => {
          loadRequestData.media.contentUrl = asset.url;
          ...
          return loadRequestData;
        });
    });

Recursos do dispositivo

O método getDeviceCapabilities fornece informações sobre o dispositivo de transmissão conectado e o dispositivo de vídeo ou áudio conectado a ele. O método getDeviceCapabilities fornece informações de suporte para o Google Assistente, o Bluetooth e os dispositivos de tela e áudio conectados.

Esse método retorna um objeto que você pode consultar transmitindo um dos tipos enumerados especificados para conferir a capacidade do dispositivo para esse tipo enumerado. Os tipos enumerados são definidos em cast.framework.system.DeviceCapabilities.

Este exemplo verifica se o dispositivo receptor da Web é capaz de reproduzir HDR e DolbyVision (DV) com as chaves IS_HDR_SUPPORTED e IS_DV_SUPPORTED, respectivamente.

const context = cast.framework.CastReceiverContext.getInstance();
context.addEventListener(cast.framework.system.EventType.READY, () => {
  const deviceCapabilities = context.getDeviceCapabilities();
  if (deviceCapabilities &&
      deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED]) {
    // Write your own event handling code, for example
    // using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED] value
  }
  if (deviceCapabilities &&
      deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED]) {
    // Write your own event handling code, for example
    // using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED] value
  }
});
context.start();

Como processar interações do usuário

Um usuário pode interagir com seu app receptor da Web pelos apps remetentes (Web, Android e iOS), comandos de voz em dispositivos com Google Assistente, controles por toque em smart displays e controles remotos em dispositivos Android TV. O SDK do Cast fornece várias APIs para permitir que o app receptor da Web processe essas interações, atualize a interface do aplicativo por meio de estados de ação do usuário e, opcionalmente, envie as mudanças para atualizar os serviços de back-end.

Comandos de mídia compatíveis

Os estados dos controles de interface são determinados pelo MediaStatus.supportedMediaCommands para os controladores expandidos do remetente para iOS e Android, apps receptores e de controle remoto executados em dispositivos de toque e apps receptores em dispositivos Android TV. Quando um Command bit a bit específico é ativado na propriedade, os botões relacionados a essa ação são ativados. Se o valor não for definido, o botão estará desativado. Esses valores podem ser alterados no receptor da Web das seguintes formas:

  1. Usando PlayerManager.setSupportedMediaCommands para definir o Commands específico.
  2. Adicionar um novo comando usando addSupportedMediaCommands.
  3. Remover um comando atual usando removeSupportedMediaCommands.
playerManager.setSupportedMediaCommands(cast.framework.messages.Command.SEEK |
  cast.framework.messages.Command.PAUSE);

Quando o receptor prepara o MediaStatus atualizado, ele inclui as mudanças na propriedade supportedMediaCommands. Quando o status for transmitido, os apps de emissores conectados vão atualizar os botões na IU adequadamente.

Para saber mais sobre os comandos de mídia e dispositivos de toque compatíveis, consulte o guia Accessing UI controls.

Como gerenciar estados de ação do usuário

Quando os usuários interagem com a interface ou enviam comandos de voz, eles podem controlar a reprodução do conteúdo e das propriedades relacionadas ao item que está sendo reproduzido. As solicitações que controlam a reprodução são processadas automaticamente pelo SDK. As solicitações que modificam as propriedades do item sendo reproduzido, como um comando LIKE, exigem que o aplicativo receptor as processe. O SDK fornece uma série de APIs para processar esses tipos de solicitações. Para dar suporte a essas solicitações, faça o seguinte:

  • Defina o MediaInformation userActionStates com as preferências do usuário ao carregar um item de mídia.
  • Interceptar mensagens USER_ACTION e determinar a ação solicitada.
  • Atualize a UserActionState do MediaInformation para atualizar a interface.

O snippet a seguir intercepta a solicitação LOAD e preenche o MediaInformation do LoadRequestData. Nesse caso, o usuário gosta do conteúdo que está sendo carregado.

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, (loadRequestData) => {
      const userActionLike = new cast.framework.messages.UserActionState(
          cast.framework.messages.UserAction.LIKE);
      loadRequestData.media.userActionStates = [userActionLike];

      return loadRequestData;
    });

O snippet a seguir intercepta a mensagem USER_ACTION e processa a chamada do back-end com a mudança solicitada. Em seguida, ele faz uma chamada para atualizar o UserActionState no receptor.

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.USER_ACTION,
  (userActionRequestData) => {
    // Obtain the media information of the current content to associate the action to.
    let mediaInfo = playerManager.getMediaInformation();

    // If there is no media info return an error and ignore the request.
    if (!mediaInfo) {
        console.error('Not playing media, user action is not supported');
        return new cast.framework.messages.ErrorData(messages.ErrorType.BAD_REQUEST);
    }

    // Reach out to backend services to store user action modifications. See sample below.
    return sendUserAction(userActionRequestData, mediaInfo)

    // Upon response from the backend, update the client's UserActionState.
    .then(backendResponse => updateUserActionStates(backendResponse))

    // If any errors occurred in the backend return them to the cast receiver.
    .catch((error) => {
      console.error(error);
      return error;
    });
});

O snippet a seguir simula uma chamada para um serviço de back-end. A função verifica o UserActionRequestData para ver o tipo de alteração solicitada pelo usuário e só faz uma chamada de rede se a ação for compatível com o back-end.

function sendUserAction(userActionRequestData, mediaInfo) {
  return new Promise((resolve, reject) => {
    switch (userActionRequestData.userAction) {
      // Handle user action changes supported by the backend.
      case cast.framework.messages.UserAction.LIKE:
      case cast.framework.messages.UserAction.DISLIKE:
      case cast.framework.messages.UserAction.FOLLOW:
      case cast.framework.messages.UserAction.UNFOLLOW:
      case cast.framework.messages.UserAction.FLAG:
      case cast.framework.messages.UserAction.SKIP_AD:
        let backendResponse = {userActionRequestData: userActionRequestData, mediaInfo: mediaInfo};
        setTimeout(() => {resolve(backendResponse)}, 1000);
        break;
      // Reject all other user action changes.
      default:
        reject(
          new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.INVALID_REQUEST));
    }
  });
}

O snippet a seguir usa o UserActionRequestData e adiciona ou remove o UserActionState do MediaInformation. Atualizar o UserActionState do MediaInformation muda o estado do botão associado à ação solicitada. Essa mudança é refletida na interface dos controles de smart display, no app de controle remoto e na interface do Android TV. Ele também é transmitido por mensagens MediaStatus enviadas para atualizar a IU do controlador expandido para remetentes do iOS e do Android.

function updateUserActionStates(backendResponse) {
  // Unwrap the backend response.
  let mediaInfo = backendResponse.mediaInfo;
  let userActionRequestData = backendResponse.userActionRequestData;

  // If the current item playing has changed, don't update the UserActionState for the current item.
  if (playerManager.getMediaInformation().entity !== mediaInfo.entity) {
    return;
  }

  // Check for existing userActionStates in the MediaInformation.
  // If none, initialize a new array to populate states with.
  let userActionStates = mediaInfo.userActionStates || [];

  // Locate the index of the UserActionState that will be updated in the userActionStates array.
  let index = userActionStates.findIndex((currUserActionState) => {
    return currUserActionState.userAction == userActionRequestData.userAction;
  });

  if (userActionRequestData.clear) {
    // Remove the user action state from the array if cleared.
    if (index >= 0) {
      userActionStates.splice(index, 1);
    }
    else {
      console.warn("Could not find UserActionState to remove in MediaInformation");
    }
  } else {
    // Add the UserActionState to the array if enabled.
    userActionStates.push(
      new cast.framework.messages.UserActionState(userActionRequestData.userAction));
  }

  // Update the UserActionState array and set the new MediaInformation
  mediaInfo.userActionStates = userActionStates;
  playerManager.setMediaInformation(mediaInfo, true);
  return;
}

Comandos de voz

No momento, os comandos de mídia abaixo têm suporte no SDK do receptor da Web para dispositivos com Google Assistente. As implementações padrão desses comandos estão em cast.framework.PlayerManager.

Comando Descrição
Tocar Reproduzir ou retomar a reprodução no estado pausado.
Pausar Pausa o conteúdo em reprodução.
Anterior Pule para o item de mídia anterior na fila de mídia.
Próxima Pule para o próximo item de mídia na fila.
Parar Interrompe a mídia em reprodução.
Repetir nenhum Desativar a repetição de itens de mídia na fila quando a reprodução do último item na fila terminar.
Repetir single Repetir a mídia em reprodução indefinidamente.
Repetir tudo Repete todos os itens da fila depois que o último item é reproduzido.
Repetir tudo e embaralhar Quando a reprodução do último item da fila terminar, embaralhe a fila e repita todos os itens da fila.
Ordem aleatória Embaralhe os itens de mídia na sua fila de mídia.
Legendas ATIVADAS / DESATIVADAS Ativar / desativar o closed captioning para sua mídia. Ativar / desativar também está disponível por idioma.
Ir para o tempo absoluto Pula para o tempo absoluto especificado.
Procurar para tempo relativo ao horário atual Avança ou retrocede pelo período especificado em relação ao tempo de reprodução atual.
Jogar de novo Reinicie a mídia em reprodução no momento ou reproduza o último item de mídia reproduzido se nada estiver tocando.
Definir a taxa de reprodução Variem a taxa de reprodução de mídia. Isso deve ser processado por padrão. Você pode usar o interceptador de mensagens SET_PLAYBACK_RATE para substituir as solicitações de taxa recebidas.

Comandos de mídia compatíveis com voz

Para impedir que um comando de voz acione um comando de mídia em um dispositivo com Google Assistente, defina primeiro os comandos de mídia compatíveis que você pretende oferecer. Em seguida, aplique esses comandos ativando a propriedade CastReceiverOptions.enforceSupportedCommands. A interface nos remetentes do SDK do Cast e nos dispositivos com toque será alterada para refletir essas configurações. Se a flag não estiver ativada, os comandos de voz recebidos serão executados.

Por exemplo, se você permitir PAUSE nos aplicativos emissores e nos dispositivos com toque ativado, também será necessário configurar o receptor para refletir essas configurações. Quando configurados, todos os comandos de voz recebidos vão ser descartados se não forem incluídos na lista de comandos com suporte.

No exemplo abaixo, fornecemos o CastReceiverOptions ao iniciar a CastReceiverContext. Adicionamos suporte ao comando PAUSE e exigimos que o jogador ofereça suporte apenas a esse comando. Agora, se um comando de voz solicitar outra operação, como SEEK, ela será negada. O usuário será notificado que o comando ainda não é compatível.

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

context.start({
  enforceSupportedCommands: true,
  supportedCommands: cast.framework.messages.Command.PAUSE
});

Você pode aplicar uma lógica separada para cada comando que quiser restringir. Remova a sinalização enforceSupportedCommands e, para cada comando que você quer restringir, possa interceptar a mensagem recebida. Aqui, interceptamos a solicitação fornecida pelo SDK para que os comandos SEEK emitidos para dispositivos com Google Assistente não acionem uma busca no seu app receptor da Web.

Para comandos de mídia não compatíveis com o aplicativo, retorne um motivo de erro apropriado, como NOT_SUPPORTED.

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.SEEK,
  seekData => {
    // Block seeking if the SEEK supported media command is disabled
    if (!(playerManager.getSupportedMediaCommands() & cast.framework.messages.Command.SEEK)) {
      let e = new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType
      .INVALID_REQUEST);
      e.reason = cast.framework.messages.ErrorReason.NOT_SUPPORTED;
      return e;
    }

    return seekData;
  });

Segundo plano com base na atividade de voz

Se a plataforma de transmissão colocar o som do seu aplicativo em segundo plano devido à atividade do Google Assistente, como ouvir a fala do usuário ou responder à mensagem, uma mensagem FocusState de NOT_IN_FOCUS será enviada ao aplicativo Web Receiver quando a atividade for iniciada. Outra mensagem com IN_FOCUS é enviada quando a atividade termina. Dependendo do seu app e da mídia que está sendo reproduzida, convém pausar a mídia quando o FocusState for NOT_IN_FOCUS, interceptando o tipo de mensagem FOCUS_STATE.

Por exemplo, uma boa experiência do usuário é pausar a reprodução do audiolivro se o Google Assistente estiver respondendo a uma consulta do usuário.

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.FOCUS_STATE,
  focusStateRequestData => {
    // Pause content when the app is out of focus. Resume when focus is restored.
    if (focusStateRequestData.state == cast.framework.messages.FocusState.NOT_IN_FOCUS) {
      playerManager.pause();
    } else {
      playerManager.play();
    }

    return focusStateRequestData;
  });

Idioma de legenda especificado por voz

Quando um usuário não indica explicitamente o idioma das legendas, o idioma usado para elas será o mesmo em que o comando foi falado. Nessas situações, o parâmetro isSuggestedLanguage da mensagem recebida indica se o idioma associado foi sugerido ou solicitado explicitamente pelo usuário.

Por exemplo, isSuggestedLanguage está definido como true para o comando "Ok Google, ativar legendas", porque o idioma foi inferido pelo idioma em que o comando foi falado. Se o idioma for solicitado explicitamente, como em "Ok Google, ative as legendas em inglês", o isSuggestedLanguage vai ser definido como false.

Metadados e transmissão de voz

Embora os comandos de voz sejam processados pelo receptor da Web por padrão, é necessário garantir que os metadados do conteúdo sejam completos e precisos. Isso garante que os comandos de voz sejam processados corretamente pelo Google Assistente e que os metadados sejam exibidos corretamente em novos tipos de interfaces, como o app Google Home e telas inteligentes, como o Google Home Hub.

Transferência de streaming

Preservar o estado da sessão é a base da transferência de stream, em que os usuários podem mover streams de áudio e vídeo existentes entre dispositivos usando comandos de voz, o app Google Home ou smart displays. A mídia é interrompida em um dispositivo (a origem) e continua em outro (o destino). Qualquer dispositivo de transmissão com o firmware mais recente pode servir como origens ou destinos em uma transferência de stream.

O fluxo de eventos para a transferência de stream é:

  1. No dispositivo de origem:
    1. A mídia é interrompida.
    2. O aplicativo receptor da Web recebe um comando para salvar o estado atual da mídia.
    3. O aplicativo Web Receiver foi encerrado.
  2. No dispositivo de destino:
    1. O aplicativo Web Receiver está carregado.
    2. O app receptor da Web recebe um comando para restaurar o estado da mídia salva.
    3. A mídia será retomada.

Os elementos de estado de mídia incluem:

  • Posição ou marcação de tempo específica da música, do vídeo ou do item de mídia.
  • Lugar em uma fila mais ampla (como uma playlist ou rádio de um artista).
  • O usuário autenticado.
  • Estado de reprodução (por exemplo, em reprodução ou pausada)

Ativando a transferência de stream

Para implementar a transferência de stream para seu receptor da Web:

  1. Atualize supportedMediaCommands com o comando STREAM_TRANSFER:
    playerManager.addSupportedMediaCommands(
    cast.framework.messages.Command.STREAM_TRANSFER, true);
  2. Se quiser, modifique os interceptadores de mensagens SESSION_STATE e RESUME_SESSION, conforme descrito em Como preservar o estado da sessão. Só faça isso se for preciso armazenar os dados personalizados como parte do snapshot da sessão. Caso contrário, a implementação padrão para preservar estados da sessão será compatível com a transferência de stream.

Como preservar o estado da sessão

O SDK do receptor da Web fornece uma implementação padrão para apps de receptor da Web para preservar os estados da sessão criando um snapshot do status da mídia atual, convertendo o status em uma solicitação de carregamento e retomando a sessão com a solicitação de carregamento.

A solicitação de carregamento gerada pelo Web Receiver pode ser substituída no interceptador de mensagens SESSION_STATE, se necessário. Se você quiser adicionar dados personalizados à solicitação de carregamento, sugerimos colocá-los em loadRequestData.customData.

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.SESSION_STATE,
    function (sessionState) {
        // Override sessionState.loadRequestData if needed.
        const newCredentials = updateCredentials_(sessionState.loadRequestData.credentials);
        sessionState.loadRequestData.credentials = newCredentials;

        // Add custom data if needed.
        sessionState.loadRequestData.customData = {
            'membership': 'PREMIUM'
        };

        return sessionState;
    });

Os dados personalizados podem ser recuperados de loadRequestData.customData no interceptador de mensagens RESUME_SESSION.

let cred_ = null;
let membership_ = null;

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.RESUME_SESSION,
    function (resumeSessionRequest) {
        let sessionState = resumeSessionRequest.sessionState;

        // Modify sessionState.loadRequestData if needed.
        cred_ = sessionState.loadRequestData.credentials;

        // Retrieve custom data.
        membership_ = sessionState.loadRequestData.customData.membership;

        return resumeSessionRequest;
    });

Pré-carregamento de conteúdo

O receptor da Web oferece suporte ao pré-carregamento de itens de mídia após o item de reprodução atual na fila.

A operação de pré-carregamento faz o download prévio de vários segmentos dos próximos itens. A especificação é feita no valor preloadTime no objeto QueueItem (o padrão é 20 segundos, se não for fornecido). O tempo é expresso em segundos, em relação ao final do item em reprodução no momento . Somente valores positivos são válidos. Por exemplo, se o valor for 10 segundos, o item será pré-carregado 10 segundos antes da conclusão do item anterior. Se o tempo de pré-carregamento for maior que o tempo restante no currentItem, o pré-carregamento acontecerá o mais rápido possível. Portanto, se um valor muito grande de pré-carregamento for especificado emqueueItem, é possível conseguir o efeito de, sempre que estivermos reproduzindo o item atual, já estamos pré-carregando o próximo item. No entanto, deixamos a configuração e a escolha para o desenvolvedor, já que esse valor pode afetar a largura de banda e o desempenho do streaming do item em reprodução atual.

Por padrão, o pré-carregamento vai funcionar para conteúdo com streaming HLS, DASH e Smooth.

Arquivos de vídeo e áudio MP4 normais, como MP3, não serão pré-carregados, já que os dispositivos de transmissão oferecem suporte a apenas um elemento de mídia e não podem ser usados para pré-carregamento enquanto um item de conteúdo existente ainda está sendo reproduzido.

Mensagens personalizadas

A troca de mensagens é o principal método de interação para aplicativos receptores da Web.

Um remetente emite mensagens para um receptor da Web usando as APIs do remetente para a plataforma que está sendo executada (Android, iOS, Web). O objeto do evento, que é a manifestação de uma mensagem, transmitido aos listeners de eventos, tem um elemento de dados (event.data) em que os dados assumem as propriedades do tipo de evento específico.

Um aplicativo receptor da Web pode optar por detectar mensagens em um namespace especificado. Em virtude disso, o aplicativo receptor da Web é considerado compatível com esse protocolo de namespace. Cabe a qualquer remetente conectado que queira se comunicar nesse namespace use o protocolo apropriado.

Todos os namespaces são definidos por uma string e precisam começar com "urn:x-cast:" seguido por qualquer string. Por exemplo, "urn:x-cast:com.example.cast.mynamespace".

Veja a seguir um snippet de código para o receptor da Web detectar mensagens personalizadas de remetentes conectados:

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

const CUSTOM_CHANNEL = 'urn:x-cast:com.example.cast.mynamespace';
context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
  // handle customEvent.
});

context.start();

Da mesma forma, os aplicativos Web Receiver podem manter os remetentes informados sobre o estado do Web Receiver enviando mensagens para remetentes conectados. Um app receptor da Web pode enviar mensagens usando sendCustomMessage(namespace, senderId, message) no CastReceiverContext. Um receptor da Web pode enviar mensagens para um remetente individual, em resposta a uma mensagem recebida ou devido a uma mudança de estado do aplicativo. Além das mensagens ponto a ponto (com um limite de 64 KB), um receptor da Web também pode transmitir mensagens para todos os remetentes conectados.

Transmitir para dispositivos de áudio

Consulte o Guia do Google Cast para dispositivos de áudio para receber suporte relacionado à reprodução somente de áudio.

Android TV

Esta seção discute como o Google Web Receiver usa suas entradas como reprodução e a compatibilidade com o Android TV.

Como integrar seu aplicativo ao controle remoto

O Google Web Receiver em execução no dispositivo Android TV converte entradas das entradas de controle do dispositivo (por exemplo, o controle remoto portátil) como mensagens de reprodução de mídia definidas para o namespace urn:x-cast:com.google.cast.media, conforme descrito em Mensagens de reprodução de mídia. O app precisa oferecer suporte a essas mensagens para controlar a reprodução de mídia e permitir o controle básico de reprodução das entradas de controle do Android TV.

Diretrizes de compatibilidade com o Android TV

Aqui estão algumas recomendações e armadilhas comuns a serem evitadas para garantir que seu aplicativo seja compatível com o Android TV:

  • Esteja ciente de que a string do user agent contém "Android" e "C iniciou". Alguns sites podem redirecionar para um site somente para dispositivos móveis porque detectam o rótulo "Android". Não presuma que "Android" na string do user agent sempre indica um usuário de dispositivo móvel.
  • A pilha de mídia do Android pode usar GZIP transparente para buscar dados. Confira se os dados de mídia podem responder a Accept-Encoding: gzip.
  • Os eventos de mídia HTML5 do Android TV podem ser acionados em momentos diferentes do Chromecast. Isso pode revelar problemas que estavam ocultos no Chromecast.
  • Ao atualizar a mídia, use eventos relacionados a ela disparados por elementos <audio>/<video>, como timeupdate, pause e waiting. Evite usar eventos relacionados à rede, como progress, suspend e stalled, porque eles tendem a depender da plataforma. Consulte Eventos de mídia para mais informações sobre como processar eventos de mídia no seu receptor.
  • Ao configurar os certificados HTTPS do site receptor, inclua certificados de AC intermediários. Consulte a página de teste de SSL da Qualsys para verificar: se o caminho de certificação confiável do seu site inclui um certificado de CA rotulado como "download extra", ele não é carregado em plataformas baseadas no Android.
  • Embora o Chromecast exiba a página receptora em um plano gráfico de 720p, outras plataformas do Google Cast, incluindo o Android TV, podem exibir a página em até 1080p. Garanta que a página receptora seja dimensionada corretamente em diferentes resoluções.