Cómo habilitar la compatibilidad de una app web con Cast

1. Descripción general

Logotipo de Google Cast

En este codelab, aprenderás a modificar una app de video web existente para transmitir contenido en un dispositivo compatible con Google Cast.

¿Qué es Google Cast?

Google Cast permite a los usuarios transmitir contenido desde un dispositivo móvil a una TV. De esa manera, los usuarios pueden usar su dispositivo móvil como control remoto de modo que se reproduzca contenido multimedia en la TV.

El SDK de Google Cast posibilita que extiendas tu app para controlar una TV o un sistema de sonido. Este SDK te permitirá agregar los componentes de la IU necesarios según la lista de tareas de diseño de Google Cast.

Te proporcionamos la lista de tareas de diseño de Google Cast con el fin de que la experiencia del usuario de Cast resulte sencilla y predecible en todas las plataformas compatibles.

¿Qué compilaremos?

Cuando completes este codelab, tendrás una app de video web de Chrome que podrá transmitir videos a un dispositivo Google Cast.

Qué aprenderás

  • Cómo agregar el SDK de Google Cast a una app de video de muestra
  • Cómo agregar el botón para transmitir a fin de seleccionar un dispositivo Google Cast
  • Cómo conectarse a un dispositivo de transmisión e iniciar un receptor de contenido multimedia
  • Cómo transmitir un video
  • Cómo integrar Cast Connect

Requisitos

  • El navegador más reciente de Google Chrome
  • Servicio de hosting de HTTPS, como Firebase Hosting o ngrok
  • Un dispositivo Google Cast, como Chromecast o Android TV, que esté configurado con acceso a Internet
  • Una TV o un monitor con entrada HDMI
  • Se requiere un Chromecast con Google TV para probar la integración de Cast Connect, pero es opcional para el resto del codelab. Si no tienes una, puedes omitir el paso Agregar compatibilidad con Cast Connect al final de este instructivo.

Experiencia

  • Debes tener conocimientos previos sobre desarrollo web.
  • También deberás tener experiencia como usuario de TV :)

¿Cómo usarás este instructivo?

Ler Leer y completar los ejercicios

¿Cómo calificarías tu experiencia en la compilación de aplicaciones web?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia cuando miras TV?

Principiante Intermedio Avanzado

2. Obtén el código de muestra

Puedes descargar el código de muestra completo a tu computadora…

y descomprimir el archivo ZIP que se descargó.

3. Ejecuta la app de muestra

Logotipo de Google Chrome

Primero, veamos el aspecto de la app de muestra completa. La app es un reproductor de video básico. El usuario podrá seleccionar un video de una lista y, a continuación, reproducirlo en un dispositivo local o transmitirlo a uno compatible con Google Cast.

Para poder usar la versión completada, debe estar alojada.

Si no tienes un servidor disponible, puedes usar Firebase Hosting o ngrok.

Ejecuta el servidor

Una vez que hayas configurado el servicio que deseas, navega hasta app-done y, luego, inicia tu servidor.

En tu navegador, visita la URL HTTPS de la muestra que alojaste.

  1. Debería aparecer la app de video.
  2. Haz clic en el botón para transmitir y selecciona tu dispositivo Google Cast.
  3. Selecciona un video y haz clic en el botón de reproducción.
  4. El video comenzará a reproducirse en tu dispositivo Google Cast.

Imagen de un video que se reproduce en un dispositivo de transmisión

Haz clic en el botón de pausa en el elemento de video para pausarlo en el receptor. Haz clic en el botón de reproducción para reanudar la reproducción.

Haz clic en el botón para transmitir a fin de detener la transmisión al dispositivo Google Cast.

Antes de continuar, detén el servidor.

4. Prepara el proyecto inicial

Imagen de un video que se reproduce en un dispositivo de transmisión

Debemos agregar compatibilidad con Google Cast a la app inicial que descargaste. Estos son algunos términos relacionados con Google Cast que usaremos en este codelab:

  • una app emisora se ejecuta en un dispositivo móvil o una laptop.
  • una app receptora se ejecuta en el dispositivo Google Cast.

Ahora ya puedes compilar sobre el proyecto inicial con tu editor de texto favorito:

  1. Selecciona el directorio ícono de carpetaapp-start de la descarga del código de muestra.
  2. Ejecuta la app con tu servidor y explora la IU.

Ten en cuenta que, mientras trabajas en este codelab, deberás volver a alojar la muestra en tu servidor según el servicio.

Diseño de apps

La app recuperará una lista de videos de un servidor web remoto y proporcionará una lista para que el usuario explore. Los usuarios podrán seleccionar un video de forma que vean los detalles o reproducirlo localmente en el dispositivo móvil.

La app consta de una vista principal, definida en index.html, y el controlador principal, CastVideos.js..

index.html

Este archivo HTML declara casi toda la IU de la app web.

Hay algunas secciones de vistas. Tenemos nuestro div#main_video, que contiene el elemento de video. En relación con nuestro div de video, tenemos div#media_control, que define todos los controles para el elemento de video. Debajo, se encuentra media_info, que muestra los detalles del video en la vista. Por último, el elemento div carousel muestra una lista de videos en un elemento div.

El archivo index.html también inicia el SDK de Cast y le indica a la función CastVideos que cargue.

La mayor parte del contenido que se propagará a estos elementos se define, se inserta y se controla en CastVideos.js. Echemos un vistazo.

CastVideos.js

Esta secuencia de comandos administra toda la lógica para la app web de Cast Videos. La lista de videos y los metadatos asociados, definidos en CastVideos.js, se encuentran en un objeto llamado mediaJSON.

Hay algunas secciones principales que juntas se encargan de administrar y reproducir el video de forma local y remota. En general, es una aplicación web bastante sencilla.

CastPlayer es la clase principal que administra toda la app, configura el reproductor, selecciona contenido multimedia y vincula eventos a PlayerHandler para reproducir contenido multimedia. CastPlayer.prototype.initializeCastPlayer es el método que configura todas las funciones de Cast. CastPlayer.prototype.switchPlayer cambia el estado entre reproductores locales y remotos. CastPlayer.prototype.setupLocalPlayer y CastPlayer.prototype.setupRemotePlayer inicializan reproductores locales y remotos.

PlayerHandler es la clase responsable de administrar la reproducción de contenido multimedia. Existen otros métodos responsables de los detalles de la administración del contenido multimedia y la reproducción.

Preguntas frecuentes

5. Agrega el botón para transmitir

Imagen de una app compatible con Cast

Una app compatible con Cast muestra el botón para transmitir en el elemento de video. Al hacer clic en ese botón, se mostrará la lista de dispositivos de transmisión que un usuario puede seleccionar. Si el usuario estaba reproduciendo contenido de forma local en el dispositivo emisor, al seleccionar un dispositivo de transmisión podrá iniciar o reanudar la reproducción en ese dispositivo. En cualquier momento de una sesión de transmisión, el usuario podrá hacer clic en el botón para transmitir y dejar de transmitir tu aplicación al dispositivo de transmisión. El usuario debe poder conectarse al dispositivo de transmisión o desconectarse de él desde cualquier pantalla de la aplicación, como se describe en la Lista de tareas de diseño de Google Cast.

Configuración

El proyecto de inicio requiere las mismas dependencias y configuración que para la app de ejemplo completa, pero esta vez aloja el contenido de app-start.

En tu navegador, visita la URL https de la muestra que alojaste.

Recuerda que, a medida que realices cambios, deberás volver a alojar la muestra en tu servidor, según el servicio.

Inicialización

El framework de Cast tiene un objeto singleton global, el CastContext, que coordina todas las actividades del framework. Este objeto debe inicializarse con anticipación en el ciclo de vida de la aplicación, por lo general, se llama desde una devolución de llamada asignada a window['__onGCastApiAvailable'], que se llama después de que se carga el SDK de Cast, y está disponible para su uso. En este caso, se llama a CastContext en CastPlayer.prototype.initializeCastPlayer, que se llama desde la devolución de llamada mencionada anteriormente.

Cuando inicializas CastContext, se debe proporcionar un objeto JSON options. Esta clase contiene opciones que afectan el comportamiento del framework. El más importante es el ID de la aplicación receptora, que se usa para filtrar la lista de dispositivos de transmisión disponibles a fin de mostrar solo los que pueden ejecutar la app especificada y para iniciar la aplicación receptora cuando se inicia una sesión de transmisión.

Cuando desarrolles tu propia app compatible con Cast, tendrás que registrarte como desarrollador de Cast y, luego, obtener el ID de aplicación correspondiente a tu app. Para este codelab, usaremos un ID de app de muestra.

Agrega el siguiente código a index.html al final de la sección body:

<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>

Agrega el siguiente código a index.html para inicializar la app CastVideos y también inicializar CastContext:

<script src="CastVideos.js"></script>
<script type="text/javascript">
var castPlayer = new CastPlayer();
window['__onGCastApiAvailable'] = function(isAvailable) {
  if (isAvailable) {
    castPlayer.initializeCastPlayer();
  }
};
</script>

Ahora, debemos agregar un método nuevo en CastVideos.js, que corresponda al método que acabamos de llamar en index.html. Agreguemos un nuevo método, llamado initializeCastPlayer, que establece opciones en CastContext e inicializa nuevos RemotePlayer y RemotePlayerControllers:

/**
 * This method sets up the CastContext, and a few other members
 * that are necessary to play and control videos on a Cast
 * device.
 */
CastPlayer.prototype.initializeCastPlayer = function() {

    var options = {};

    // Set the receiver application ID to your own (created in
    // the Google Cast Developer Console), or optionally
    // use the chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
    options.receiverApplicationId = 'C0868879';

    // Auto join policy can be one of the following three:
    // ORIGIN_SCOPED - Auto connect from same appId and page origin
    // TAB_AND_ORIGIN_SCOPED - Auto connect from same appId, page origin, and tab
    // PAGE_SCOPED - No auto connect
    options.autoJoinPolicy = chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED;

    cast.framework.CastContext.getInstance().setOptions(options);

    this.remotePlayer = new cast.framework.RemotePlayer();
    this.remotePlayerController = new cast.framework.RemotePlayerController(this.remotePlayer);
    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,
        this.switchPlayer.bind(this)
    );
};

Por último, debemos crear las variables para RemotePlayer y RemotePlayerController:

var CastPlayer = function() {
  //...
  /* Cast player variables */
  /** @type {cast.framework.RemotePlayer} */
  this.remotePlayer = null;
  /** @type {cast.framework.RemotePlayerController} */
  this.remotePlayerController = null;
  //...
};

Botón para transmitir

Ahora que se inicializó CastContext, debemos agregar el botón para transmitir a fin de permitir que el usuario seleccione un dispositivo de transmisión. El SDK de Cast proporciona un componente de botón para transmitir llamado google-cast-launcher con el ID "castbutton"". Se puede agregar al elemento de video de la aplicación con solo agregar un button en la sección media_control.

Así se verá el elemento del botón:

<google-cast-launcher id="castbutton"></google-cast-launcher>

Agrega el siguiente código a index.html en la sección media_control:

<div id="media_control">
  <div id="play"></div>
  <div id="pause"></div>
  <div id="progress_bg"></div>
  <div id="progress"></div>
  <div id="progress_indicator"></div>
  <div id="fullscreen_expand"></div>
  <div id="fullscreen_collapse"></div>
  <google-cast-launcher id="castbutton"></google-cast-launcher>
  <div id="audio_bg"></div>
  <div id="audio_bg_track"></div>
  <div id="audio_indicator"></div>
  <div id="audio_bg_level"></div>
  <div id="audio_on"></div>
  <div id="audio_off"></div>
  <div id="duration">00:00:00</div>
</div>

A continuación, actualiza la página en el navegador Chrome. Deberías ver un botón para transmitir en el elemento de video y, cuando hagas clic en él, se mostrarán los dispositivos de transmisión en tu red local. El navegador Chrome administra automáticamente la detección de dispositivos. Selecciona tu dispositivo de transmisión. La app receptora de muestra se cargará en él.

No establecimos ningún tipo de compatibilidad para reproducir contenido multimedia, por lo que aún no podrás reproducir videos en el dispositivo de transmisión. Haz clic en el botón para transmitir a fin de detener la transmisión.

6. Transmite contenido de video

Imagen de una app compatible con Cast con el menú de selección de dispositivos de transmisión

Extenderemos la app de muestra de modo que también reproduzca videos de forma remota en un dispositivo de transmisión. Para ello, tenemos que escuchar los diferentes eventos generados por el framework de Cast.

Transmisión de contenido multimedia

En términos generales, si quieres reproducir contenido multimedia en un dispositivo de transmisión, debe suceder lo siguiente:

  1. Crea un objeto MediaInfo JSON desde el SDK de Cast que modele un elemento multimedia.
  2. El usuario se conecta al dispositivo de transmisión para iniciar la aplicación receptora.
  3. Cargar el objeto MediaInfo en tu receptor y reproducir el contenido
  4. Realizar un seguimiento del estado del contenido multimedia
  5. Enviar comandos de reproducción al receptor según las interacciones del usuario

El paso 1 equivale a asignar un objeto a otro. MediaInfo es algo que el SDK de Cast comprende y mediaJSON es el encapsulamiento de nuestra app para un elemento multimedia. Podemos asignar fácilmente un mediaJSON a un MediaInfo. Ya realizamos el paso 2 en la sección anterior. El paso 3 es fácil de completar con el SDK de Cast.

La app de ejemplo CastPlayer ya distingue entre la reproducción local y la remota en el método switchPlayer:

if (cast && cast.framework) {
  if (this.remotePlayer.isConnected) {
    //...

En este codelab, no es importante que comprendas exactamente cómo funciona toda la lógica del reproductor de muestra. Sin embargo, es importante comprender que se deberá modificar el reproductor multimedia de tu app para que pueda reconocer la reproducción local y la remota.

Por el momento, el reproductor local siempre estará en el estado de reproducción local porque aún no conoce los estados de transmisión. Deberemos actualizar la IU en función de las transiciones de estado que ocurran en el framework de Cast. Por ejemplo, si comenzamos a transmitir, deberemos detener la reproducción local e inhabilitar algunos controles. Del mismo modo, si dejamos de transmitir cuando estamos en este controlador de vista, debemos hacer la transición a la reproducción local. Para ello, tenemos que escuchar los diferentes eventos generados por el framework de Cast.

Administración de sesiones de transmisión

En el framework de Cast, una sesión de Cast combina los pasos para conectarse a un dispositivo, iniciar (o unirse a una sesión existente), conectarse a una aplicación receptora e inicializar un canal de control de contenido multimedia, si corresponde. El canal de control de contenido multimedia es la forma en que el framework de Cast envía y recibe mensajes relacionados con la reproducción de contenido multimedia desde el receptor.

La sesión de transmisión se iniciará automáticamente cuando el usuario seleccione un dispositivo desde el botón para transmitir y se detendrá automáticamente cuando el usuario se desconecte. El framework de Cast también controla automáticamente la reconexión a la sesión de un receptor debido a problemas de red.

El CastSession administra las sesiones de transmisión, a las que se puede acceder a través de cast.framework.CastContext.getInstance().getCurrentSession(). Las devoluciones de llamada de EventListener se pueden usar para supervisar los eventos de sesión, como la creación, suspensión, reanudación y finalización.

En nuestra aplicación actual, toda la administración de la sesión y el estado se controla por nosotros en el método setupRemotePlayer. Comencemos a configurar eso en tu app agregando el siguiente código a tu CastVideos.js:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    var castSession = cast.framework.CastContext.getInstance().getCurrentSession();

    this.playerHandler.setTarget(playerTarget);

    // Setup remote player volume right on setup
    // The remote player may have had a volume set from previous playback
    if (this.remotePlayer.isMuted) {
        this.playerHandler.mute();
    }
    var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
    var p = document.getElementById('audio_bg_level');
    p.style.height = currentVolume + 'px';
    p.style.marginTop = -currentVolume + 'px';

    this.hideFullscreenButton();

    this.playerHandler.play();
};

Aún necesitamos vincular todos los eventos de las devoluciones de llamada y controlar todos los eventos entrantes. Esto es bastante sencillo, así que comprobémoslo ahora:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    var castSession = cast.framework.CastContext.getInstance().getCurrentSession();

    // Add event listeners for player changes which may occur outside sender app
    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED,
        function() {
            if (this.remotePlayer.isPaused) {
                this.playerHandler.pause();
            } else {
                this.playerHandler.play();
            }
        }.bind(this)
    );

    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_MUTED_CHANGED,
        function() {
            if (this.remotePlayer.isMuted) {
                this.playerHandler.mute();
            } else {
                this.playerHandler.unMute();
            }
        }.bind(this)
    );

    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.VOLUME_LEVEL_CHANGED,
        function() {
            var newVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
            var p = document.getElementById('audio_bg_level');
            p.style.height = newVolume + 'px';
            p.style.marginTop = -newVolume + 'px';
        }.bind(this)
    );

    // This object will implement PlayerHandler callbacks with
    // remotePlayerController, and makes necessary UI updates specific
    // to remote playback
    var playerTarget = {};

    playerTarget.play = function () {
        if (this.remotePlayer.isPaused) {
            this.remotePlayerController.playOrPause();
        }

        var vi = document.getElementById('video_image');
        vi.style.display = 'block';
        var localPlayer = document.getElementById('video_element');
        localPlayer.style.display = 'none';
    }.bind(this);

    playerTarget.pause = function () {
        if (!this.remotePlayer.isPaused) {
            this.remotePlayerController.playOrPause();
        }
    }.bind(this);

    playerTarget.stop = function () {
         this.remotePlayerController.stop();
    }.bind(this);

    playerTarget.getCurrentMediaTime = function() {
        return this.remotePlayer.currentTime;
    }.bind(this);

    playerTarget.getMediaDuration = function() {
        return this.remotePlayer.duration;
    }.bind(this);

    playerTarget.updateDisplayMessage = function () {
        document.getElementById('playerstate').style.display = 'block';
        document.getElementById('playerstatebg').style.display = 'block';
        document.getElementById('video_image_overlay').style.display = 'block';
        document.getElementById('playerstate').innerHTML =
            this.mediaContents[ this.currentMediaIndex]['title'] + ' ' +
            this.playerState + ' on ' + castSession.getCastDevice().friendlyName;
    }.bind(this);

    playerTarget.setVolume = function (volumeSliderPosition) {
        // Add resistance to avoid loud volume
        var currentVolume = this.remotePlayer.volumeLevel;
        var p = document.getElementById('audio_bg_level');
        if (volumeSliderPosition < FULL_VOLUME_HEIGHT) {
            var vScale =  this.currentVolume * FULL_VOLUME_HEIGHT;
            if (volumeSliderPosition > vScale) {
                volumeSliderPosition = vScale + (pos - vScale) / 2;
            }
            p.style.height = volumeSliderPosition + 'px';
            p.style.marginTop = -volumeSliderPosition + 'px';
            currentVolume = volumeSliderPosition / FULL_VOLUME_HEIGHT;
        } else {
            currentVolume = 1;
        }
        this.remotePlayer.volumeLevel = currentVolume;
        this.remotePlayerController.setVolumeLevel();
    }.bind(this);

    playerTarget.mute = function () {
        if (!this.remotePlayer.isMuted) {
            this.remotePlayerController.muteOrUnmute();
        }
    }.bind(this);

    playerTarget.unMute = function () {
        if (this.remotePlayer.isMuted) {
            this.remotePlayerController.muteOrUnmute();
        }
    }.bind(this);

    playerTarget.isMuted = function() {
        return this.remotePlayer.isMuted;
    }.bind(this);

    playerTarget.seekTo = function (time) {
        this.remotePlayer.currentTime = time;
        this.remotePlayerController.seek();
    }.bind(this);

    this.playerHandler.setTarget(playerTarget);

    // Setup remote player volume right on setup
    // The remote player may have had a volume set from previous playback
    if (this.remotePlayer.isMuted) {
        this.playerHandler.mute();
    }
    var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
    var p = document.getElementById('audio_bg_level');
    p.style.height = currentVolume + 'px';
    p.style.marginTop = -currentVolume + 'px';

    this.hideFullscreenButton();

    this.playerHandler.play();
};

Carga de contenido multimedia

En el SDK de Cast, el RemotePlayer y el RemotePlayerController proporcionan un conjunto de APIs convenientes para administrar la reproducción de contenido multimedia remoto en el receptor. Para una CastSession que admite la reproducción de contenido multimedia, el SDK creará automáticamente las instancias de RemotePlayer y RemotePlayerController. Se puede acceder a ellas creando instancias de cast.framework.RemotePlayer y cast.framework.RemotePlayerController respectivamente, como se muestra más arriba en el codelab.

A continuación, debemos cargar el video seleccionado actualmente en la app receptora compilando un objeto MediaInfo para que el SDK procese y pase la solicitud. Para ello, agrega el siguiente código a setupRemotePlayer:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    //...

    playerTarget.load = function (mediaIndex) {
        console.log('Loading...' + this.mediaContents[mediaIndex]['title']);
        var mediaInfo = new chrome.cast.media.MediaInfo(
            this.mediaContents[mediaIndex]['sources'][0], 'video/mp4');

        mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
        mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
        mediaInfo.metadata.title = this.mediaContents[mediaIndex]['title'];
        mediaInfo.metadata.images = [
            {'url': MEDIA_SOURCE_ROOT + this.mediaContents[mediaIndex]['thumb']}];

        var request = new chrome.cast.media.LoadRequest(mediaInfo);
        castSession.loadMedia(request).then(
            this.playerHandler.loaded.bind(this.playerHandler),
            function (errorCode) {
                this.playerState = PLAYER_STATE.ERROR;
                console.log('Remote media load error: ' +
                    CastPlayer.getErrorMessage(errorCode));
            }.bind(this));
    }.bind(this);

    //...
};

Ahora, agrega un método para alternar entre la reproducción local y la remota:

/**
 * This is a method for switching between the local and remote
 * players. If the local player is selected, setupLocalPlayer()
 * is run. If there is a cast device connected we run
 * setupRemotePlayer().
 */
CastPlayer.prototype.switchPlayer = function() {
    this.stopProgressTimer();
    this.resetVolumeSlider();
    this.playerHandler.stop();
    this.playerState = PLAYER_STATE.IDLE;
    if (cast && cast.framework) {
        if (this.remotePlayer.isConnected) {
            this.setupRemotePlayer();
            return;
        }
    }
    this.setupLocalPlayer();
};

Por último, agrega un método para controlar los mensajes de error de Cast:

/**
 * Makes human-readable message from chrome.cast.Error
 * @param {chrome.cast.Error} error
 * @return {string} error message
 */
CastPlayer.getErrorMessage = function(error) {
  switch (error.code) {
    case chrome.cast.ErrorCode.API_NOT_INITIALIZED:
      return 'The API is not initialized.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CANCEL:
      return 'The operation was canceled by the user' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CHANNEL_ERROR:
      return 'A channel to the receiver is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.EXTENSION_MISSING:
      return 'The Cast extension is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.INVALID_PARAMETER:
      return 'The parameters to the operation were not valid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.RECEIVER_UNAVAILABLE:
      return 'No receiver was compatible with the session request.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.SESSION_ERROR:
      return 'A session could not be created, or a session was invalid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.TIMEOUT:
      return 'The operation timed out.' +
        (error.description ? ' :' + error.description : '');
  }
};

Ahora, ejecuta la app. Conéctate a tu dispositivo de transmisión y comienza a reproducir un video. Deberías ver que el video se reproduce en el receptor.

7. Cómo agregar compatibilidad con Cast Connect

La biblioteca de Cast Connect permite que las aplicaciones emisoras existentes se comuniquen con aplicaciones de Android TV a través del protocolo de transmisión. Cast Connect se basa en la infraestructura de Cast, y tu app de Android TV actúa como receptor.

Dependencias

  • Navegador Chrome versión M87 o superior

Cómo configurar la compatibilidad con el receptor Android

Para iniciar la aplicación de Android TV, también conocida como receptor de Android, debemos establecer la marca androidReceiverCompatible como verdadera en el objeto CastOptions.

Agrega el siguiente código a tu CastVideos.js en la función initializeCastPlayer:

var options = {};
...
options.androidReceiverCompatible = true;

cast.framework.CastContext.getInstance().setOptions(options);

Establece credenciales de inicio

Del lado del remitente, puedes especificar CredentialsData para que represente quién se une a la sesión. credentials es una cadena que puede definir el usuario, siempre que tu app de ATV pueda comprenderla. El CredentialsData solo se pasa a tu app de Android TV durante el inicio o la hora de unirse a la app. Si la vuelves a configurar mientras tienes conexión, no se pasará a la app de Android TV.

Para establecer las credenciales de lanzamiento, se debe definir CredentialsData en cualquier momento después de configurar las opciones de inicio.

Agrega el siguiente código a tu clase CastVideos.js en la función initializeCastPlayer:

cast.framework.CastContext.getInstance().setOptions(options);
...
let credentialsData = new chrome.cast.CredentialsData("{\"userId\": \"abc\"}");
cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData);
...

Configurar credenciales en una solicitud de carga

En caso de que la app de Web Receiver y la app para Android TV manejen credentials de manera diferente, es posible que debas definir credenciales distintas para cada una. Para solucionarlo, agrega el siguiente código en CastVideos.js, en playerTarget.load, en la función setupRemotePlayer:

...
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.credentials = 'user-credentials';
request.atvCredentials = 'atv-user-credentials';
...

Según la app receptora a la que tu remitente esté transmitiendo, ahora el SDK controlará automáticamente qué credenciales usar para la sesión actual.

Cómo probar Cast Connect

Pasos para instalar el APK de Android TV en Chromecast con Google TV:

  1. Busca la dirección IP del dispositivo Android TV. Por lo general, está disponible en Configuración > Internet y redes > (Nombre de la red a la que está conectado tu dispositivo). A la derecha, mostrará los detalles y la IP de tu dispositivo en la red.
  2. Usa la dirección IP de tu dispositivo para conectarte a ella a través de ADB con el terminal:
$ adb connect <device_ip_address>:5555
  1. Desde la ventana de la terminal, navega a la carpeta de nivel superior de las muestras del codelab que descargaste al comienzo. Por ejemplo:
$ cd Desktop/chrome_codelab_src
  1. Instala el archivo .apk de esta carpeta en tu Android TV. Para ello, ejecuta el siguiente comando:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Ahora deberías poder ver una app con el nombre Transmitir videos en el menú Tus apps del dispositivo Android TV.
  2. Ejecuta el código de remitente web actualizado y establece una sesión de transmisión con el dispositivo Android TV. Para ello, usa el ícono de transmisión o selecciona Cast.. en el menú desplegable del navegador Chrome. Con esta acción, deberías iniciar la app de Android TV en tu receptor de Android y permitirte controlar la reproducción con el control remoto de Android TV.

8. Felicitaciones

Ahora sabes cómo habilitar la transmisión de contenido en una app de video con los widgets del SDK de Cast en una app web de Chrome.

Para obtener más detalles, consulta la guía para desarrolladores sobre remitente web.