Используем ShakaPlayer в LightningJS

Возможно, сейчас вы задумались над тем, зачем вообще нужен какой-то другой плеер? Ответ на этот вопрос довольно прост: Lightning по умолчанию поставляется с поддержкой обычного воспроизведения HTML5. Это означает, что у вас есть возможность воспроизводить, например, простые файлы mp4. Однако, если предстоит более сложная задача воспроизведения видео с применением технологий HLS, MPEG-Dash и, конечно же, DRM, потребуется другой плеер. Lightning просто использует тег видео для воспроизведения, поэтому в текущем состоянии он не сможет поддерживать подобные сценарии использования.

Чтобы успешно справляться с такими случаями, придется интегрировать внешний плеер. Я рекомендую ShakaPlayer, который хорошо зарекомендовал себя на SmartTV и обладает широким диапазоном настроек.

Шаг 1. Начальная настройка

Для начала понадобится готовый проект Lightning, в котором можно писать код. Если у вас уже есть готовый проект, можете перейти к шагу 2.

Чтобы создать проект Lightning, нужно сначала установить Lightning CLI:

npm install -g @lightningjs/cli

После установки Lightning CLI создадим проект с помощью следующей команды:

lng create

Вы можете пройтись по опциям, предоставляемым CLI, и определить, какие настройки вам больше нравится. Данное руководство не предписывает, какие именно опции нужно выбрать. Я выбрал Typescript, но вы решайте сами, что вам подходит.

После создания проекта установите модули node, запустив npm i в командной строке.

Теперь, когда проект настроен, перейдем к следующему шагу по запуску ShakaPlayer в приложении Lightning.

Шаг 2. Создание страницы Player

Поскольку плееры обычно не запускаются на главной странице приложений, необходимо создать специальную страницу для плеера и включить в приложение маршрутизацию, используя встроенный в Lightning маршрутизатор (Router). Мы не будем рассматривать все шаги по созданию маршрутизатора. Рекомендую следовать шагам, определенным в документации Lightning.

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

  • Элемент-обертка, содержащим все элементы UI. Мы будем использовать этот элемент для плавного показа и скрытия интерфейса.
  • Кнопка Play/Pause, которая будет реализована внутри обертки. Она нужна для переключения состояния воспроизведения.

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

import {Lightning} from "@lightningjs/sdk";
import {RouterPage} from "./RouterPage"; // Определяет isPage:true в TypeConfig.

export default class Player extends Lightning.Component<Lightning.Component.TemplateSpecLoose, RouterPage> {
static override _template(): Lightning.Component.Template<Lightning.Component.TemplateSpecLoose> {
return {
Wrapper: {
alpha: 1
PlayPause: {
mount: 0.5,
x: 1920 / 2,
y: 1080 / 2,
rect: true,
w: 250,
h: 250,
color: 0x00000000,
Background: {
rect: true,
w: 250,
h: 250,
color: 0xffababab,
shader: {
type: Lightning.shaders.RoundedRectangle,
radius: 125
},
},
Text: {
mount: 0.5,
x: 125,
y: 125,
text: {
textColor: 0xffffffff,
text: 'Pause'
}
}
}
}
}
}
}

Шаг 3. Установка и импортирование ShakaPlayer

Это довольно простой шаг. Есть несколько способов получить ссылку на ShakaPlayer в проекте.

  • Использовать ссылку на CDN и включить этот скрипт в index.html (но в контексте Metrological Appstore у нас не будет доступа к этому индексу).
  • Внедрить код, вручную создав тег script с помощью JavaScript (это также не лучший вариант для Metrological).
  • Включить в виде пакета NPM, который будет встроен в файлы приложения.

Последний метод предпочтительнее первых двух, поскольку позволит Metrological лучше управлять ресурсами, используемыми приложением в контексте их Appstore.

Начнем с установки пакета из NPM (вы можете применить любой менеджер пакетов):

npm i shaka-player

Затем импортируем ссылку ShakaPlayer в верхней части страницы Player:

import shaka from 'shaka-player';

Шаг 4. Подключение Lightning к ShakaPlayer

Lightning имеет собственную интеграцию с воспроизведением видео, так что стоит воспользоваться этой особенностью. К счастью, с Lightning сделать это очень просто. Начнем с настройки элемента VideoPlayer для работы со страницей Player:

import {VideoPlayer} from "@lightningjs/sdk";

override _firstActive() {
VideoPlayer.consumer(this);
VideoPlayer.loader(this._loadPlayback);
VideoPlayer.unloader(this._unloadPlayback);
}

Приведенный выше код выполняет три функции для VideoPlayer. Первая, consumer, предназначена для того, чтобы позволить странице Player обрабатывать события воспроизведения (описание можно найти здесь, в рамках туториала мы не будем это реализовывать). Функции loader и unloader используются для обработки воспроизведения с помощью ShakaPlayer. Реализуем их прямо сейчас. Приведенный ниже пример показывает пример простых настроек.

_setupShakaPlayer (videoEl: HTMLVideoElement) {
videoEl.autoplay = true;
this._player = new shaka.Player(videoEl);
}

async _loadPlayback (url: string, videoEl: HTMLVideoElement) {
this._setupShakaPlayer(videoEl);
await this._player.load(url);
}
async _unloadPlayback () {
await this._player.unload();
}

Мы просто передаем элемент видео и URL воспроизведения вновь созданному экземпляру ShakaPlayer. Как видите, мы установили videoEl.autoplay = true для немедленного запуска воспроизведения. Если вам не нужна эта функция, можно убрать autoplay. Когда видео выгружается, просим ShakaPlayer выгрузиться тоже.

Шаг 5. Запуск воспроизведения

Мы почти у цели! Следующий шаг очень прост. Поскольку ShakaPlayer настроен для работы с объектом VideoPlayer программы Lightning, дальше нужно лишь отдать команду VideoPlayer, чтобы он начал воспроизводить видео. Для этого воспользуемся функцией open, которую можно вызвать, например, из метода жизненного цикла _active:

override _active() {
VideoPlayer.open('https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.mpd')
}

В этом примере я использовал поток MPEG-Dash без DRM, который взял со страницы демонстрационных URL на платформе Unified Streaming. Если вы применили тот же код и открыли страницу Player в своем браузере, у вас должно начаться воспроизведение!

Шаг 6. Воспроизведение и пауза

Часть работы, посвященная воспроизведению видео, завершена. Это означает, что можно приступать к дальнейшей реализации. Хочу привести небольшой пример того, как можно реализовать рабочую кнопку Play/Pause.

В настройках страницы Player уже создан элемент пользовательского интерфейса для кнопки Play/Pause. Теперь надо подключить к нему обработчики событий, чтобы нажатие на кнопку взаимодействовало с плеером. Начнем с выделения кнопки Play/Pause в отдельный компонент Lightning, чтобы она смогла принимать фокус, и добавим несколько методов для изменения состояния кнопки:

// Компонент страницы Player.

static override _template(): Lightning.Component.Template<Lightning.Component.TemplateSpecLoose> {
    return {
        Wrapper: {
            alpha: 1,
            PlayPause: {
                type: PlayPause
            }
        }
    }
}
override _getFocused(): Lightning.Component {
    return this.tag('PlayPause');
}

// Новый компонент PlayPause.

import {Lightning} from "@lightningjs/sdk";

export default class PlayPause extends Lightning.Component {

    // @ts-ignore
    _isPlaying: boolean

    override _setup() {
        this._isPlaying = true;
    }

    static override _template () {
        return {
            mount: 0.5,
            x: 1920 / 2,
            y: 1080 / 2,
            rect: true,
            w: 250,
            h: 250,
            color: 0x00000000,
            Background: {
                rect: true,
                w: 250,
                h: 250,
                color: 0xffababab,
                shader: {
                    type: Lightning.shaders.RoundedRectangle,
                    radius: 125
                },
            },
            Text: {
                mount: 0.5,
                x: 125,
                y: 125,
                text: {
                    textColor: 0xffffffff,
                    text: 'Pause'
                }
            }
        }
    }

    set isPlaying(isPlaying: boolean) {
        this._isPlaying = isPlaying;
        this.tag('Text').patch({
            text: {
                text: isPlaying ? 'Pause' : 'Play'
            }
        });
    }

    get isPlaying(): boolean {
        return this._isPlaying;
    }
}

Приведенный выше код выделяет кнопку в отдельный компонент и добавляет метод isPlaying. Мы будем использовать его для обозначения состояния воспроизведения и паузы, что обеспечит смену текста кнопки с “Play” на “Pause” и наоборот. В реальном сценарии использования вы, скорее всего, замените эти слова иконками воспроизведения и паузы.

Теперь, когда кнопка настроена, можно прикрепить ее к обработчику события для кнопки “Enter”. Затем в зависимости от состояния кнопки мы будем либо приостанавливать, либо возобновлять воспроизведение видео:

override _handleEnter () {
const button = this.tag('PlayPause');

button.isPlaying = !button.isPlaying;
button.isPlaying ? VideoPlayer.play() : VideoPlayer.pause();
}

Мы стремительно приближаемся к завершению! После добавления вышеописанной реализации нажатие кнопки “OK” на TV-Remote (или “Enter” на клавиатуре) обеспечивает переключение между состояниями воспроизведения и паузы. Плеер готов к работе.

Шаг 7. Следующие этапы реализации

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

Для совершения следующих шагов стоит ознакомиться с документацией ShakaPlayer. Это даст более четкое понимание того, как работать с различными событиями, поступающими от плеера (например, с обновлением времени и ошибками).

Использованный здесь код доступен на Github.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Matthijs Langendijk: Using ShakaPlayer with LightningJS

Предыдущая статьяОбзор JavaScript на основе диалога с ChatGPT
Следующая статья3 ошибки на собеседованиях по программированию, из-за которых можно получить отказ