Включить трансляцию для веб-приложения

1. Обзор

Логотип Google Cast

Эта лаборатория кода научит вас, как модифицировать существующее веб-видеоприложение для трансляции контента на устройство с поддержкой Google Cast .

Что такое Google Cast?

Google Cast позволяет пользователям транслировать контент с мобильного устройства на телевизор. Затем пользователи могут использовать свое мобильное устройство в качестве пульта дистанционного управления для воспроизведения мультимедиа на телевизоре.

Google Cast SDK позволяет расширить приложение для управления телевизором или звуковой системой. Cast SDK позволяет добавлять необходимые компоненты пользовательского интерфейса на основе контрольного списка Google Cast Design Checklist .

Контрольный список Google Cast Design предназначен для того, чтобы сделать работу с Cast простой и предсказуемой на всех поддерживаемых платформах.

Что мы будем строить?

Когда вы закончите эту лабораторную работу, у вас будет приложение для веб-видео Chrome, которое сможет транслировать видео на устройство Google Cast.

Что вы узнаете

  • Как добавить SDK Google Cast в пример видеоприложения.
  • Как добавить кнопку Cast для выбора устройства Google Cast.
  • Как подключиться к Cast-устройству и запустить медиа-ресивер.
  • Как залить видео.
  • Как интегрировать Cast Connect

Что вам понадобится

  • Последний браузер Google Chrome .
  • Служба хостинга HTTPS, такая как Firebase Hosting или ngrok .
  • Устройство Google Cast, например Chromecast или Android TV, с доступом в Интернет.
  • Телевизор или монитор с входом HDMI.
  • Chromecast с Google TV требуется для тестирования интеграции Cast Connect, но не является обязательным для остальной части Codelab. Если у вас его нет, вы можете пропустить шаг добавления поддержки Cast Connect ближе к концу этого руководства.

Опыт

  • Вам понадобятся предыдущие знания в области веб-разработки.
  • Вам также понадобятся предварительные знания о просмотре телевизора :)

Как вы будете использовать этот учебник?

Прочитайте только это Прочтите его и выполните упражнения

Как бы вы оценили свой опыт создания веб-приложений?

Новичок Средний Опытный

Как бы вы оценили свой опыт просмотра телевизора?

Новичок Средний Опытный

2. Получите пример кода

Вы можете загрузить весь пример кода на свой компьютер...

и распакуйте загруженный zip-файл.

3. Запустите пример приложения

Логотип Google Chrome

Во-первых, давайте посмотрим, как выглядит готовый пример приложения. Приложение представляет собой базовый видеоплеер. Пользователь может выбрать видео из списка, а затем воспроизвести видео локально на устройстве или транслировать его на устройство Google Cast.

Чтобы иметь возможность использовать готовое, его необходимо разместить.

Если у вас нет доступного сервера, вы можете использовать Firebase Hosting или ngrok .

Запустить сервер

После того, как вы настроите службу по вашему выбору, перейдите к app-done и запустите свой сервер.

В браузере перейдите по URL-адресу https для размещенного вами образца.

  1. Вы должны увидеть, как появится видеоприложение.
  2. Нажмите кнопку Cast и выберите свое устройство Google Cast.
  3. Выберите видео, нажмите на кнопку воспроизведения.
  4. Видео начнет воспроизводиться на вашем устройстве Google Cast.

Изображение видео, воспроизводимого на устройстве Cast

Нажмите кнопку паузы в элементе видео, чтобы приостановить воспроизведение видео на приемнике. Нажмите кнопку воспроизведения в элементе видео, чтобы продолжить воспроизведение видео еще раз.

Нажмите кнопку Cast, чтобы остановить трансляцию на устройство Google Cast.

Прежде чем двигаться дальше, остановите сервер.

4. Подготовить стартовый проект

Изображение видео, воспроизводимого на устройстве Cast

Нам нужно добавить поддержку Google Cast в загруженное вами начальное приложение. Вот некоторые термины Google Cast, которые мы будем использовать в этой кодовой лаборатории:

  • приложение отправителя работает на мобильном устройстве или ноутбуке,
  • приложение- приемник работает на устройстве Google Cast.

Теперь вы готовы строить поверх начального проекта с помощью вашего любимого текстового редактора:

  1. Выберите значок папки каталог app-start из загрузки примера кода.
  2. Запустите приложение, используя свой сервер, и изучите пользовательский интерфейс.

Обратите внимание, что при работе с этой лабораторией кода вам потребуется повторно разместить образец на своем сервере в зависимости от службы.

Дизайн приложения

Приложение получает список видео с удаленного веб-сервера и предоставляет пользователю список для просмотра. Пользователи могут выбрать видео, чтобы просмотреть подробности, или воспроизвести видео локально на мобильном устройстве.

Приложение состоит из одного основного представления, определенного в index.html , и основного контроллера CastVideos.js.

index.html

Этот html-файл объявляет почти весь пользовательский интерфейс для веб-приложения.

Есть несколько разделов представлений, у нас есть div#main_video , который содержит элемент видео. Что касается нашего видео div, у нас есть div#media_control , который определяет все элементы управления для видео элемента. Ниже находится media_info , в котором отображаются детали просматриваемого видео. Наконец, блок carousel отображает список видео в блоке.

Файл index.html также загружает Cast SDK и сообщает функции CastVideos о загрузке.

Большая часть контента, который будет заполнять эти элементы, определяется, внедряется и контролируется в CastVideos.js . Итак, давайте посмотрим на это.

CastVideos.js

Этот сценарий управляет всей логикой веб-приложения Cast Videos. Список видео и связанных с ними метаданных, определенных в CastVideos.js , содержится в объекте с именем mediaJSON .

Есть несколько основных разделов, которые вместе отвечают за управление и воспроизведение видео как локально, так и удаленно. В целом, это довольно простое веб-приложение.

CastPlayer — это основной класс, который управляет всем приложением, настраивает проигрыватель, выбирает мультимедиа и привязывает события к PlayerHandler для воспроизведения мультимедиа. CastPlayer.prototype.initializeCastPlayer — это метод, который настраивает все функции Cast. CastPlayer.prototype.switchPlayer переключает состояние между локальным и удаленным плеерами. CastPlayer.prototype.setupLocalPlayer и CastPlayer.prototype.setupRemotePlayer инициализируют локальные и удаленные проигрыватели.

PlayerHandler — это класс, отвечающий за управление воспроизведением мультимедиа. Существует ряд других методов, отвечающих за детали управления мультимедиа и воспроизведением.

Часто задаваемые вопросы

5. Добавление кнопки Cast

Изображение приложения с поддержкой Cast

Приложение с поддержкой Cast отображает кнопку Cast в видеоэлементе. При нажатии на кнопку Cast отображается список устройств Cast, которые может выбрать пользователь. Если пользователь воспроизводил контент локально на устройстве-отправителе, выбор устройства Cast запускает или возобновляет воспроизведение на этом устройстве Cast. В любой момент во время сеанса Cast пользователь может нажать кнопку Cast и прекратить трансляцию вашего приложения на устройство Cast. Пользователь должен иметь возможность подключаться к устройству Cast или отключаться от него, находясь на любом экране вашего приложения, как описано в Контрольном списке дизайна Google Cast .

Конфигурация

Начальный проект требует тех же зависимостей и настройки, что и для готового примера приложения, но на этот раз размещает содержимое app-start .

В браузере перейдите по URL-адресу https для размещенного вами образца.

Помните, что при внесении изменений вам потребуется повторно разместить образец на своем сервере в зависимости от службы.

Инициализация

Фреймворк Cast имеет глобальный одноэлементный объект CastContext , который координирует все действия фреймворка. Этот объект должен быть инициализирован на ранней стадии жизненного цикла приложения, обычно вызываемый из обратного вызова, назначенного для window['__onGCastApiAvailable'] , который вызывается после загрузки Cast SDK и доступен для использования. В этом случае CastContext вызывается в CastPlayer.prototype.initializeCastPlayer , который вызывается из вышеупомянутого обратного вызова.

Объект options JSON должен быть предоставлен при инициализации CastContext . Этот класс содержит параметры, влияющие на поведение фреймворка. Наиболее важным из них является идентификатор приложения-приемника, который используется для фильтрации списка доступных устройств Cast, чтобы отображались только те устройства, на которых можно запускать указанное приложение, и для запуска приложения-приемника при запуске сеанса Cast.

Когда вы разрабатываете собственное приложение с поддержкой Cast, вы должны зарегистрироваться в качестве разработчика Cast, а затем получить идентификатор приложения для своего приложения. Для этой кодовой лаборатории мы будем использовать пример идентификатора приложения.

Добавьте следующий код в index.html в самом конце раздела body :

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

Добавьте следующий код в index.html для инициализации приложения CastVideos , а также для инициализации CastContext :

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

Теперь нам нужно добавить новый метод в CastVideos.js , который соответствует методу, который мы только что вызвали в index.html . Давайте добавим новый метод с именем initializeCastPlayer , который устанавливает параметры CastContext и инициализирует новые RemotePlayer и 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)
    );
};

Наконец, нам нужно создать переменные для RemotePlayer и RemotePlayerController :

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

Кнопка трансляции

Теперь, когда CastContext инициализирован, нам нужно добавить кнопку Cast, чтобы пользователь мог выбрать устройство Cast. Cast SDK предоставляет компонент кнопки Cast под названием google-cast-launcher с идентификатором castbutton" . Его можно добавить к видеоэлементу приложения, просто добавив button в раздел media_control .

Вот как будет выглядеть элемент кнопки:

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

Добавьте следующий код в index.html в разделе 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>

Теперь обновите страницу в браузере Chrome. Вы должны увидеть кнопку Cast в элементе видео, и когда вы нажмете на нее, он отобразит список устройств Cast в вашей локальной сети. Обнаружение устройства автоматически управляется браузером Chrome. Выберите свое устройство Cast, и образец приложения-приемника загрузится на устройство Cast.

Мы не подключали какую-либо поддержку воспроизведения мультимедиа, поэтому вы пока не можете воспроизводить видео на устройстве Cast. Нажмите кнопку Cast, чтобы остановить трансляцию.

6. Трансляция видеоконтента

Изображение приложения с поддержкой Cast с меню выбора устройства Cast

Мы расширим пример приложения, чтобы также удаленно воспроизводить видео на устройстве Cast. Для этого нам нужно прослушивать различные события, генерируемые фреймворком Cast.

Кастинг СМИ

На высоком уровне, если вы хотите воспроизвести медиафайл на устройстве Cast, должно произойти следующее:

  1. Создайте объект MediaInfo JSON из Cast SDK, который моделирует элемент мультимедиа.
  2. Пользователь подключается к устройству Cast, чтобы запустить приложение-приемник.
  3. Загрузите объект MediaInfo в свой ресивер и воспроизведите содержимое.
  4. Отслеживайте статус носителя.
  5. Отправляйте команды воспроизведения на приемник на основе взаимодействия с пользователем.

Шаг 1 сводится к сопоставлению одного объекта с другим; MediaInfo — это то, что понимает Cast SDK, а mediaJSON — это инкапсуляция нашего приложения для элемента мультимедиа; мы можем легко сопоставить mediaJSON с MediaInfo . Мы уже сделали шаг 2 в предыдущем разделе. Шаг 3 легко выполнить с помощью Cast SDK.

Пример приложения CastPlayer уже различает локальное и удаленное воспроизведение в методе switchPlayer :

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

В этой кодовой лаборатории не важно, чтобы вы точно понимали, как работает вся логика проигрывателя примеров. Однако важно понимать, что медиаплеер вашего приложения необходимо модифицировать, чтобы он учитывал как локальное, так и удаленное воспроизведение.

В настоящий момент локальный проигрыватель всегда находится в состоянии локального воспроизведения, так как он еще ничего не знает о состояниях трансляции. Нам нужно обновить пользовательский интерфейс на основе переходов состояний, которые происходят в среде Cast. Например, если мы начинаем кастинг, нам нужно остановить локальное воспроизведение и отключить некоторые элементы управления. Точно так же, если мы остановим кастинг, когда находимся в этом контроллере представления, нам нужно перейти к локальному воспроизведению. Чтобы справиться с этим, нам нужно прослушивать различные события, генерируемые платформой Cast.

Управление сессиями трансляции

Для платформы Cast сеанс Cast сочетает в себе этапы подключения к устройству, запуска (или присоединения к существующему сеансу), подключения к приложению-получателю и инициализации канала управления мультимедиа, если это необходимо. Канал управления мультимедиа — это то, как платформа Cast отправляет и получает сообщения, связанные с воспроизведением мультимедиа, от получателя.

Сеанс трансляции будет запущен автоматически, когда пользователь выберет устройство с помощью кнопки трансляции, и будет автоматически остановлен, когда пользователь отключится. Повторное подключение к сеансу получателя из-за проблем с сетью также автоматически обрабатывается платформой Cast.

Сеансы трансляции управляются CastSession , к которому можно получить доступ через cast.framework.CastContext.getInstance().getCurrentSession() . Обратные вызовы EventListener можно использовать для отслеживания событий сеанса, таких как создание, приостановка, возобновление и завершение.

В нашем текущем приложении все управление сеансом и состоянием обрабатывается для нас в методе setupRemotePlayer . Давайте начнем настраивать это в вашем приложении, добавив следующий код в ваш 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();
};

Нам все еще нужно связать все события от обратных вызовов и обработать все поступающие события. Это довольно просто сделать, поэтому давайте позаботимся об этом сейчас:

/**
 * 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();
};

Загрузка носителя

В Cast SDK RemotePlayer и RemotePlayerController предоставляют набор удобных API-интерфейсов для управления удаленным воспроизведением мультимедиа на приемнике. Для CastSession , поддерживающего воспроизведение мультимедиа, экземпляры RemotePlayer и RemotePlayerController будут автоматически создаваться пакетом SDK. Доступ к ним можно получить, создав экземпляры cast.framework.RemotePlayer и cast.framework.RemotePlayerController соответственно, как показано ранее в кодовой лаборатории.

Затем нам нужно загрузить текущее выбранное видео в приемник, создав объект MediaInfo для обработки SDK и передачи запроса. Для этого добавьте следующий код в 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);

    //...
};

Теперь добавьте метод для переключения между локальным и удаленным воспроизведением:

/**
 * 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();
};

Наконец, добавьте метод для обработки любых сообщений об ошибках 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 : '');
  }
};

Теперь запустите приложение. Подключитесь к устройству Cast и начните воспроизведение видео. Вы должны увидеть видео, воспроизводимое на приемнике.

7. Добавьте поддержку Cast Connect

Библиотека Cast Connect позволяет существующим приложениям-отправителям взаимодействовать с приложениями Android TV через протокол Cast. Cast Connect строится на основе инфраструктуры Cast, а ваше приложение Android TV выступает в роли приемника.

Зависимости

  • Браузер Chrome версии M87 или выше

Установить совместимость приемника Android

Чтобы запустить приложение Android TV, также называемое Android Receiver, нам нужно установить для флага androidReceiverCompatible значение true в объекте CastOptions .

Добавьте следующий код в ваш CastVideos.js в функции initializeCastPlayer :

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

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

Установить учетные данные для запуска

На стороне отправителя вы можете указать CredentialsData для представления того, кто присоединяется к сеансу. credentials — это строка, которая может быть определена пользователем, если ваше приложение ATV может ее понять. CredentialsData передается вашему приложению Android TV только во время запуска или присоединения. Если вы установите его снова, когда вы подключены, он не будет передан в ваше приложение Android TV.

Для установки учетных данных запуска CredentialsData необходимо определить в любое время после установки параметров запуска.

Добавьте следующий код в свой класс CastVideos.js в функции initializeCastPlayer :

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

Установить учетные данные при запросе загрузки

Если ваше приложение Web Receiver и ваше приложение Android TV обрабатывают credentials по-разному, вам может потребоваться определить отдельные учетные данные для каждого из них. Чтобы позаботиться об этом, добавьте следующий код в ваш CastVideos.js в playerTarget.load в функции setupRemotePlayer :

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

В зависимости от приложения-получателя, в которое транслируется ваш отправитель, SDK теперь будет автоматически обрабатывать учетные данные, которые следует использовать для текущего сеанса.

Тестирование Cast Connect

Шаги по установке Android TV APK на Chromecast с Google TV:

  1. Найдите IP-адрес вашего устройства Android TV. Обычно он доступен в разделе «Настройки» > «Сеть и Интернет» > (имя сети, к которой подключено ваше устройство) . Справа будут показаны подробности и IP-адрес вашего устройства в сети.
  2. Используйте IP-адрес вашего устройства, чтобы подключиться к нему через ADB с помощью терминала:
$ adb connect <device_ip_address>:5555
  1. В окне терминала перейдите в папку верхнего уровня для образцов кодовой лаборатории, которые вы загрузили в начале этой кодовой лаборатории. Например:
$ cd Desktop/chrome_codelab_src
  1. Установите файл .apk из этой папки на свой Android TV, запустив:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Теперь вы сможете увидеть приложение под названием Cast Videos в меню «Ваши приложения» на устройстве Android TV.
  2. Запустите обновленный код веб-отправителя и установите сеанс трансляции на устройстве Android TV, используя значок трансляции или выбрав Cast.. в раскрывающемся меню в браузере Chrome. Теперь это должно запустить приложение Android TV на вашем Android-приемнике и позволить вам управлять воспроизведением с помощью пульта Android TV.

8. Поздравления

Теперь вы знаете, как включить Cast для видеоприложения с помощью виджетов Cast SDK в веб-приложении Chrome.

Дополнительные сведения см. в руководстве разработчика Web Sender .

,

1. Обзор

Логотип Google Cast

Эта лаборатория кода научит вас, как модифицировать существующее веб-видеоприложение для трансляции контента на устройство с поддержкой Google Cast .

Что такое Google Cast?

Google Cast позволяет пользователям транслировать контент с мобильного устройства на телевизор. Затем пользователи могут использовать свое мобильное устройство в качестве пульта дистанционного управления для воспроизведения мультимедиа на телевизоре.

Google Cast SDK позволяет расширить приложение для управления телевизором или звуковой системой. Cast SDK позволяет добавлять необходимые компоненты пользовательского интерфейса на основе контрольного списка Google Cast Design Checklist .

Контрольный список Google Cast Design предназначен для того, чтобы сделать работу с Cast простой и предсказуемой на всех поддерживаемых платформах.

Что мы будем строить?

Когда вы закончите эту лабораторную работу, у вас будет приложение для веб-видео Chrome, которое сможет транслировать видео на устройство Google Cast.

Что вы узнаете

  • Как добавить SDK Google Cast в пример видеоприложения.
  • Как добавить кнопку Cast для выбора устройства Google Cast.
  • Как подключиться к Cast-устройству и запустить медиа-ресивер.
  • Как залить видео.
  • Как интегрировать Cast Connect

Что вам понадобится

  • Последний браузер Google Chrome .
  • Служба хостинга HTTPS, такая как Firebase Hosting или ngrok .
  • Устройство Google Cast, например Chromecast или Android TV, с доступом в Интернет.
  • Телевизор или монитор с входом HDMI.
  • Chromecast с Google TV требуется для тестирования интеграции Cast Connect, но не является обязательным для остальной части Codelab. Если у вас его нет, вы можете пропустить шаг добавления поддержки Cast Connect ближе к концу этого руководства.

Опыт

  • Вам понадобятся предыдущие знания в области веб-разработки.
  • Вам также понадобятся предварительные знания о просмотре телевизора :)

Как вы будете использовать этот учебник?

Прочитайте только это Прочтите его и выполните упражнения

Как бы вы оценили свой опыт создания веб-приложений?

Новичок Средний Опытный

Как бы вы оценили свой опыт просмотра телевизора?

Новичок Средний Опытный

2. Получите пример кода

Вы можете загрузить весь пример кода на свой компьютер...

и распакуйте загруженный zip-файл.

3. Запустите пример приложения

Логотип Google Chrome

Во-первых, давайте посмотрим, как выглядит готовый пример приложения. Приложение представляет собой базовый видеоплеер. Пользователь может выбрать видео из списка, а затем воспроизвести видео локально на устройстве или транслировать его на устройство Google Cast.

Чтобы иметь возможность использовать готовое, его необходимо разместить.

Если у вас нет доступного сервера, вы можете использовать Firebase Hosting или ngrok .

Запустить сервер

После того, как вы настроите службу по вашему выбору, перейдите к app-done и запустите свой сервер.

В браузере перейдите по URL-адресу https для размещенного вами образца.

  1. Вы должны увидеть, как появится видеоприложение.
  2. Нажмите кнопку Cast и выберите свое устройство Google Cast.
  3. Выберите видео, нажмите на кнопку воспроизведения.
  4. Видео начнет воспроизводиться на вашем устройстве Google Cast.

Изображение видео, воспроизводимого на устройстве Cast

Нажмите кнопку паузы в элементе видео, чтобы приостановить воспроизведение видео на приемнике. Нажмите кнопку воспроизведения в элементе видео, чтобы продолжить воспроизведение видео еще раз.

Нажмите кнопку Cast, чтобы остановить трансляцию на устройство Google Cast.

Прежде чем двигаться дальше, остановите сервер.

4. Подготовить стартовый проект

Изображение видео, воспроизводимого на устройстве Cast

Нам нужно добавить поддержку Google Cast в загруженное вами начальное приложение. Вот некоторые термины Google Cast, которые мы будем использовать в этой кодовой лаборатории:

  • приложение отправителя работает на мобильном устройстве или ноутбуке,
  • приложение- приемник работает на устройстве Google Cast.

Теперь вы готовы строить поверх начального проекта с помощью вашего любимого текстового редактора:

  1. Выберите значок папки каталог app-start из загрузки примера кода.
  2. Запустите приложение, используя свой сервер, и изучите пользовательский интерфейс.

Обратите внимание, что при работе с этой лабораторией кода вам потребуется повторно разместить образец на своем сервере в зависимости от службы.

Дизайн приложения

Приложение получает список видео с удаленного веб-сервера и предоставляет пользователю список для просмотра. Пользователи могут выбрать видео, чтобы просмотреть подробности, или воспроизвести видео локально на мобильном устройстве.

Приложение состоит из одного основного представления, определенного в index.html , и основного контроллера CastVideos.js.

index.html

Этот html-файл объявляет почти весь пользовательский интерфейс для веб-приложения.

Есть несколько разделов представлений, у нас есть div#main_video , который содержит элемент видео. Что касается нашего видео div, у нас есть div#media_control , который определяет все элементы управления для видео элемента. Ниже находится media_info , в котором отображаются детали просматриваемого видео. Наконец, блок carousel отображает список видео в блоке.

Файл index.html также загружает Cast SDK и сообщает функции CastVideos о загрузке.

Большая часть контента, который будет заполнять эти элементы, определяется, внедряется и контролируется в CastVideos.js . Итак, давайте посмотрим на это.

CastVideos.js

Этот сценарий управляет всей логикой веб-приложения Cast Videos. Список видео и связанных с ними метаданных, определенных в CastVideos.js , содержится в объекте с именем mediaJSON .

Есть несколько основных разделов, которые вместе отвечают за управление и воспроизведение видео как локально, так и удаленно. В целом, это довольно простое веб-приложение.

CastPlayer — это основной класс, который управляет всем приложением, настраивает проигрыватель, выбирает мультимедиа и привязывает события к PlayerHandler для воспроизведения мультимедиа. CastPlayer.prototype.initializeCastPlayer — это метод, который настраивает все функции Cast. CastPlayer.prototype.switchPlayer переключает состояние между локальным и удаленным плеерами. CastPlayer.prototype.setupLocalPlayer и CastPlayer.prototype.setupRemotePlayer инициализируют локальные и удаленные проигрыватели.

PlayerHandler — это класс, отвечающий за управление воспроизведением мультимедиа. Существует ряд других методов, отвечающих за детали управления мультимедиа и воспроизведением.

Часто задаваемые вопросы

5. Добавление кнопки Cast

Изображение приложения с поддержкой Cast

Приложение с поддержкой Cast отображает кнопку Cast в видеоэлементе. При нажатии на кнопку Cast отображается список устройств Cast, которые может выбрать пользователь. Если пользователь воспроизводил контент локально на устройстве-отправителе, выбор устройства Cast запускает или возобновляет воспроизведение на этом устройстве Cast. В любой момент во время сеанса Cast пользователь может нажать кнопку Cast и прекратить трансляцию вашего приложения на устройство Cast. Пользователь должен иметь возможность подключаться к устройству Cast или отключаться от него, находясь на любом экране вашего приложения, как описано в Контрольном списке дизайна Google Cast .

Конфигурация

Начальный проект требует тех же зависимостей и настройки, что и для готового примера приложения, но на этот раз размещает содержимое app-start .

В браузере перейдите по URL-адресу https для размещенного вами образца.

Помните, что при внесении изменений вам потребуется повторно разместить образец на своем сервере в зависимости от службы.

Инициализация

Фреймворк Cast имеет глобальный одноэлементный объект CastContext , который координирует все действия фреймворка. Этот объект должен быть инициализирован в начале жизненного цикла приложения, обычно вызываемый из обратного вызова, назначенного для window['__onGCastApiAvailable'] , который вызывается после загрузки Cast SDK и доступен для использования. В этом случае CastContext вызывается в CastPlayer.prototype.initializeCastPlayer , который вызывается из вышеупомянутого обратного вызова.

Объект options JSON должен быть предоставлен при инициализации CastContext . Этот класс содержит параметры, влияющие на поведение фреймворка. Наиболее важным из них является идентификатор приложения-приемника, который используется для фильтрации списка доступных устройств Cast, чтобы отображались только те устройства, на которых можно запускать указанное приложение, и для запуска приложения-приемника при запуске сеанса Cast.

Когда вы разрабатываете собственное приложение с поддержкой Cast, вы должны зарегистрироваться в качестве разработчика Cast, а затем получить идентификатор приложения для своего приложения. Для этой кодовой лаборатории мы будем использовать пример идентификатора приложения.

Добавьте следующий код в index.html в самом конце раздела body :

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

Добавьте следующий код в index.html для инициализации приложения CastVideos , а также для инициализации CastContext :

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

Теперь нам нужно добавить новый метод в CastVideos.js , который соответствует методу, который мы только что вызвали в index.html . Давайте добавим новый метод с именем initializeCastPlayer , который устанавливает параметры CastContext и инициализирует новые RemotePlayer и 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)
    );
};

Наконец, нам нужно создать переменные для RemotePlayer и RemotePlayerController :

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

Кнопка трансляции

Теперь, когда CastContext инициализирован, нам нужно добавить кнопку Cast, чтобы пользователь мог выбрать устройство Cast. Cast SDK предоставляет компонент кнопки Cast под названием google-cast-launcher с идентификатором castbutton" . Его можно добавить к видеоэлементу приложения, просто добавив button в раздел media_control .

Вот как будет выглядеть элемент кнопки:

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

Добавьте следующий код в index.html в разделе 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>

Теперь обновите страницу в браузере Chrome. Вы должны увидеть кнопку Cast в элементе видео, и когда вы нажмете на нее, он отобразит список устройств Cast в вашей локальной сети. Обнаружение устройства автоматически управляется браузером Chrome. Выберите свое устройство Cast, и образец приложения-приемника загрузится на устройство Cast.

Мы не подключали какую-либо поддержку воспроизведения мультимедиа, поэтому вы пока не можете воспроизводить видео на устройстве Cast. Нажмите кнопку Cast, чтобы остановить трансляцию.

6. Трансляция видеоконтента

Изображение приложения с поддержкой Cast с меню выбора устройства Cast

Мы расширим пример приложения, чтобы также удаленно воспроизводить видео на устройстве Cast. Для этого нам нужно прослушивать различные события, генерируемые фреймворком Cast.

Кастинг СМИ

На высоком уровне, если вы хотите воспроизвести медиафайл на устройстве Cast, должно произойти следующее:

  1. Создайте объект MediaInfo JSON из Cast SDK, который моделирует элемент мультимедиа.
  2. Пользователь подключается к устройству Cast, чтобы запустить приложение-приемник.
  3. Загрузите объект MediaInfo в свой ресивер и воспроизведите содержимое.
  4. Отслеживайте статус носителя.
  5. Отправляйте команды воспроизведения на приемник на основе взаимодействия с пользователем.

Шаг 1 сводится к сопоставлению одного объекта с другим; MediaInfo — это то, что понимает Cast SDK, а mediaJSON — это инкапсуляция нашего приложения для элемента мультимедиа; мы можем легко сопоставить mediaJSON с MediaInfo . Мы уже сделали шаг 2 в предыдущем разделе. Шаг 3 легко выполнить с помощью Cast SDK.

Пример приложения CastPlayer уже различает локальное и удаленное воспроизведение в методе switchPlayer :

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

В этой кодовой лаборатории не важно, чтобы вы точно понимали, как работает вся логика проигрывателя примеров. Однако важно понимать, что медиаплеер вашего приложения необходимо модифицировать, чтобы он учитывал как локальное, так и удаленное воспроизведение.

В настоящий момент локальный проигрыватель всегда находится в состоянии локального воспроизведения, так как он еще ничего не знает о состояниях трансляции. Нам нужно обновить пользовательский интерфейс на основе переходов состояний, которые происходят в среде Cast. Например, если мы начинаем кастинг, нам нужно остановить локальное воспроизведение и отключить некоторые элементы управления. Точно так же, если мы остановим кастинг, когда находимся в этом контроллере представления, нам нужно перейти к локальному воспроизведению. Чтобы справиться с этим, нам нужно прослушивать различные события, генерируемые платформой Cast.

Управление сессиями трансляции

Для платформы Cast сеанс Cast сочетает в себе этапы подключения к устройству, запуска (или присоединения к существующему сеансу), подключения к приложению-получателю и инициализации канала управления мультимедиа, если это необходимо. Канал управления мультимедиа — это то, как платформа Cast отправляет и получает сообщения, связанные с воспроизведением мультимедиа, от получателя.

Сеанс трансляции будет запущен автоматически, когда пользователь выберет устройство с помощью кнопки трансляции, и будет автоматически остановлен, когда пользователь отключится. Повторное подключение к сеансу получателя из-за проблем с сетью также автоматически обрабатывается платформой Cast.

Сеансы трансляции управляются CastSession , к которому можно получить доступ через cast.framework.CastContext.getInstance().getCurrentSession() . Обратные вызовы EventListener можно использовать для отслеживания событий сеанса, таких как создание, приостановка, возобновление и завершение.

В нашем текущем приложении все управление сеансом и состоянием обрабатывается для нас в методе setupRemotePlayer . Давайте начнем настраивать это в вашем приложении, добавив следующий код в ваш 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();
};

Нам все еще нужно связать все события от обратных вызовов и обработать все поступающие события. Это довольно просто сделать, поэтому давайте позаботимся об этом сейчас:

/**
 * 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();
};

Загрузка носителя

В Cast SDK RemotePlayer и RemotePlayerController предоставляют набор удобных API-интерфейсов для управления удаленным воспроизведением мультимедиа на приемнике. Для CastSession , поддерживающего воспроизведение мультимедиа, экземпляры RemotePlayer и RemotePlayerController будут автоматически создаваться пакетом SDK. Доступ к ним можно получить, создав экземпляры cast.framework.RemotePlayer и cast.framework.RemotePlayerController соответственно, как показано ранее в кодовой лаборатории.

Next, we need to load the currently selected video on the receiver by building a MediaInfo object for the SDK to process and pass in the request. Add the following code to setupRemotePlayer to do so:

/**
 * 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);

    //...
};

Now add a method to switch between local and remote playback:

/**
 * 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();
};

Finally, add a method to handle any Cast error messages:

/**
 * 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 : '');
  }
};

Now, run the app. Connect to your Cast device and start playing a video. You should see the video playing on the receiver.

7. Add Cast Connect Support

Cast Connect library allows existing sender applications to communicate with Android TV applications via the Cast protocol. Cast Connect builds on top of the Cast infrastructure, with your Android TV app acting as a receiver.

Dependencies

  • Chrome browser version M87 or higher

Set Android Receiver Compatible

In order to launch the Android TV application, also referred to as the Android Receiver, we need to set the androidReceiverCompatible flag to true in the CastOptions object.

Add the following code to your CastVideos.js in the initializeCastPlayer function:

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

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

Set Launch Credentials

On the sender side, you can specify CredentialsData to represent who is joining the session. The credentials is a string which can be user-defined, as long as your ATV app can understand it. The CredentialsData is only passed to your Android TV app during launch or join time. If you set it again while you are connected, it won't be passed to your Android TV app.

In order to set Launch Credentials CredentialsData needs to be defined anytime after the launch options are set.

Add the following code to your CastVideos.js class under initializeCastPlayer function:

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

Set Credentials on Load Request

In case your Web Receiver app and your Android TV app handle credentials differently, you might need to define separate credentials for each. In order to take care of that, add the following code in your CastVideos.js under playerTarget.load in setupRemotePlayer function:

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

Depending on the receiver app your sender is casting to, the SDK would now automatically handle which credentials to use for the current session.

Testing Cast Connect

Steps to install the Android TV APK on Chromecast with Google TV:

  1. Find the IP Address of your Android TV device. Usually, it's available under Settings > Network & Internet > (Network name your device is connected to) . On the right hand it will show the details and your device's IP on the network.
  2. Use the IP address for your device to connect to it via ADB using the terminal:
$ adb connect <device_ip_address>:5555
  1. From your terminal window, navigate into the top level folder for the codelab samples that you downloaded at the start of this codelab. Например:
$ cd Desktop/chrome_codelab_src
  1. Install the .apk file in this folder to your Android TV by running:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. You should now be able to see an app by the name of Cast Videos in the Your Apps menu on your Android TV device.
  2. Run the updated web sender code and establish a cast session with your Android TV device using the cast icon or selecting Cast.. from the drop-down menu in your Chrome browser. This should now launch the Android TV app on your Android Receiver and allow you to control the playback using your Android TV remote.

8. Поздравления

You now know how to Cast-enable a video app using the Cast SDK widgets on a Chrome web app.

For more details, see the Web Sender developer guide.