Amazon IVS: как быстро добавить видеотрансляции в приложение или на сайт

от автора

Video live streaming — это технология потоковой трансляции видео тысячам и миллионам зрителей в режиме близком к реальному времени. Видеостриминг находит своё применение в различных областях:

  • фитнес и образование;

  • онлайн события: презентации, конференции, семинары;

  • электронная коммерция;

  • музыкальные концерты;

  • игры и киберспорт.

В этой статье мы рассмотрим, как можно легко интегрировать видеотрансляции в приложение или на сайт, используя Amazon Interactive Video Service.

Что такое AWS Interactive Video Service?

AWS Interactive Video Service — это решение для прямых трансляций, которое быстро интегрируется в мобильные и веб-приложения.

В рамках своего сервиса Amazon предлагает:

  • быстрое создание новых трансляций в течении пары минут;

  • низкая (менее 3 секунд) задержка доставки видеопотока для зрителей;

  • простая интеграция с мобильными приложениями и веб-сайтами через SDK для трансляции и проигрывания видео;

  • масштабирование до десятков тысяч одновременных зрителей;

  • сохранение трансляции для просмотра в записи.

Ещё одна интересная возможность — это встраивание в трансляцию метаданных с гарантией их получения всеми зрителями. Метаданные будут синхронизированы с видео- и аудиопотоками. С помощью этой функциональности можно добавлять интерактивное взаимодействие со зрителями:

  • обновлять статистику в видеоигре;

  • показывать зрителю опросник и давать возможность выбрать ответ во время вебинара;

  • предоставлять информацию о продукте и давать возможность купить демонстрируемый товар во время трансляций в e-commerce приложениях.

Amazon предоставляет SDK для iOS, Android и Web для интеграции вещания и проигрывания видео. Также на сайте AWS можно найти SDK для работы с IVS более чем на 10 языках программирования, включая Java, Python и Go.

Чтобы понять как работать с AWS IVS, давайте реализуем пример, состоящий из трёх компонент:

  • стриминг видео из браузера;

  • проигрывание видео в браузере;

  • управление каналами и отправка метаданных зрителям на бэкенде.

Отправка видео- и аудиопотоков

Начнём с создания минималистичной веб-страницы, на которой мы организуем вещание видеотрансляции.

Создадим html страницу и включим туда ссылку на broadcast SDK:

<script src="https://web-broadcast.live-video.net/1.2.0/amazon-ivs-web-broadcast.js"></script> 

Далее нужно добавить canvas для отображения видео, которое мы будем транслировать:

<canvas id="preview"></canvas> 

Добавим также на страницу input элементы для ingest endpoint и stream key:

  <section>     <label for="ingest-endpoint">Ingest Endpoint</label>     <input type="text" id="ingest-endpoint" value="" />   </section>    <section>     <label for="stream-key">Stream Key</label>     <input type="text" id="stream-key" value="" />   </section> 

Ingest endpoint — это url сервера Amazon, куда будут отправляться видео- и аудиопотоки. Stream Key — текстовый ключ, который уникально идентифицирует канал вещания. Они предоставляются Amazon после создания канала.

Ну и ещё нам понадобятся 2 кнопки для запуска и остановки трансляции:

<button class="button" id="start" onclick="startBroadcast()">Start Broadcast</button> <button class="button" id="stop" onclick="stopBroadcast()">Stop Broadcast</button> 

Как вы видите, на событиях onclick вызываются функции startBroadcast() и stopBroadcast(). Про них поговорим чуть позже.

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

Первым делом нам надо создать broadcast client:

const streamConfig = IVSBroadcastClient.BASIC_LANDSCAPE; const client = IVSBroadcastClient.create({     streamConfig: streamConfig, }); 

SDK предлагает несколько предопределенных конфигураций, которыми можно воспользоваться. Полный список можно посмотреть в документации к библиотеке. Давайте для примера возьмём IVSBroadcastClient.BASIC_LANDSCAPE.

Чтобы отобразить на странице видео, которое мы будем захватывать с помощью камеры, привяжем созданный клиент к элементу preview:

const previewEl = document.getElementById('preview'); client.attachPreview(previewEl); 

Чтобы получить список устройств, с которых мы можем производить захват видео и аудио, сделаем navigator.mediaDevices.enumerateDevices():

const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter((d) => d.kind === 'videoinput'); const audioDevices = devices.filter((d) => d.kind === 'audioinput'); 

Теперь нужно получить поток одного из доступных видео устройств и зарегистрировать его в broadcast client:

const cameraStream = await navigator.mediaDevices.getUserMedia({     video: {         deviceId: videoDevices[0].deviceId,         width: {             ideal: streamConfig.maxResolution.width,             max: streamConfig.maxResolution.width,         },         height: {             ideal: streamConfig.maxResolution.height,             max: streamConfig.maxResolution.height,         },     }, }); client.addVideoInputDevice(cameraStream, 'camera1', {index: 0}); 

То же самое нужно сделать и для аудиопотока:

const microphoneStream = await navigator.mediaDevices.getUserMedia({     audio: {deviceId: audioDevices[0].deviceId}, }); client.addAudioInputDevice(microphoneStream, 'mic1'); 

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

Всё почти готово к отправке медиапотоков на сервера AWS и началу трансляции. Осталось несколько штрихов.

Во-первых, напишем обработчика для кнопки Start Broadcast:

async function startBroadcast() {     const streamKeyEl = document.getElementById("stream-key");     const endpointEl = document.getElementById("ingest-endpoint");     const start = document.getElementById("start");      try {         start.disabled = true;         await window.client.startBroadcast(streamKeyEl.value, endpointEl.value);     } catch (err) {         start.disabled = false;         setError(err.toString());     } } 

Тут мы получаем ingest endpoint и stream key из input элементов на странице и вызываем метод startBroadcast() у клиента.

Во-вторых, сделаем остановку вещания через метод stopBroadcast:

async function stopBroadcast() {     const start = document.getElementById("start");     start.disabled = false;     try {         await window.client.stopBroadcast();     } catch (err) {         setError(err.toString());     } } 

Итак, для вещания трансляции всё готово.

Проигрывание видеопотока

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

Включим ссылку на Amazon IVS Player на нашу страницу:

<script src="https://player.live-video.net/1.14.0/amazon-ivs-player.min.js"> 

Добавим на страницу 3 блока: поле для ввода url видеотрансляции, счётчик онлайн зрителей и видео элемент для плеера:

<label for="playback-url">Playback URL</label> <input type="text" id="playback-url" value=""/>  Online viewers: <span id="viewers">0</span>  <video id="video-player" controls playsinline></video> 

С html кодом для страницы плеера закончили. Теперь перейдем к javascript коду.

Первым делом проверим, поддерживает ли браузер плеер от Amazon:

if (!IVSPlayer.isPlayerSupported) { setError("Player is not supported by browser"); } 

Далее создадим сам плеер и привяжем его к video элементу на странице:

function createPlayer() { const player = IVSPlayer.create(); player.attachHTMLVideoElement(document.getElementById('video-player')); return player; } 

У плеера есть метод addEventListener, через который можно добавить подписчиков на изменения состояния трансляции и получать уведомления о них. В нашем примере нас интересуют 2 события: об ошибке и о полученных метаданных. Именно через метаданные потока мы будем передавать текущее количество онлайн зрителей.

Напишем функцию, которая будет выводить ошибку пользователю на события ERROR и обновлять значение в элементе viewers при получении новых метаданных в трансляции:

function attachEventListeners(player) {     const PlayerEventType = IVSPlayer.PlayerEventType;     player.addEventListener(PlayerEventType.ERROR, function (err) {         setError(err.message);     });     player.addEventListener(PlayerEventType.TEXT_METADATA_CUE, function func(e) {         const viewersEl = document.getElementById("viewers");         viewersEl.innerHTML = e.text;     }); } 

Всё почти готово. Нам только осталось запустить проигрывание потока. Для этого у плеера есть функция load, которая принимает url видеопотока. Чтобы запустить проигрывание, нужно вызвать play(). Для запуска проигрывания подпишемся на обновления поля playback-url:

function initURLInput() { const urlInput = document.getElementById("playback-url"); urlInput.addEventListener("input", function(e) { player.load(e.target.value); player.play(); }, true);  } 

Проигрывание готово.

Работа с AWS на бэкенде

Вещания и проигрывания трансляций недостаточно, чтобы интегрировать AWS Interactive Video Service. Наверняка вы не захотите создавать трансляции вручную, поэтому потребуется автоматизация на бэкенде. Amazon предоставляет SDK для взаимодействия с API их сервисов для большинства популярных языков программирования. В нашем примере давайте напишем бэкенд на Golang.

Создадим go module в директории проекта:

go mod init github.com/mastersobg/aws-ivs 

Подключим в проект зависимости на aws sdk, прописам их в go.mod:

go get github.com/aws/aws-sdk-go-v2/service/ivs go get github.com/aws/aws-sdk-go-v2/config 

Чтобы подключиться к API Amazon из кода, надо получить AWS access keys. Это можно сделать в веб-интерфейсе AWS:

  • перейдите в IAM console

  • в меню слева выберите Users

  • далее выберите своего пользователя или создайте нового

  • во вкладке Security credentials нажмите Create access key

  • скачайте созданные секреты и сохраните в ~/.aws/credentials

Также нужно в файл ~/.aws/config сохранить регион по умолчанию, в котором у нас будут трансляции:

[default] region = eu-west-1 

Перейдем к написанию клиента, который будет в нашем бэкенде обёрткой к обращениям к AWS API.

Сначала загрузим конфигурацию из ~/.aws/config и создадим клиент к API IVS:

import ( "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ivs" )   type Client struct { client *ivs.Client }  func NewClient(ctx context.Context) (*Client, error) { // Load the shared AWS configuration from ~/.aws/config cfg, err := config.LoadDefaultConfig(ctx) if err != nil { return nil, err } // Create an IVS service client return &Client{ client: ivs.NewFromConfig(cfg), }, nil } 

Теперь мы можем использовать созданный клиент для общения с API IVS.

Перейдем к созданию нового Channel (канал), в который можно будет транслировать видео. Канал в терминах Amazon хранит всю конфигурацию, относящуюся к трансляции. При создании канала можно указать несколько важных настроек:

  • LatencyMode. Данный параметр принимает 2 значения: NORMAL и LOW. LOW значение по умолчанию и означает низкую задержку доставки медиапотока зрителям.

  • Type. Тип канала может быть BASIC или STANDARD. BASIC означает, что видео будет доставляться зрителям без перекодирования. STANDARD подразумевает, что видео будет перекодировано и оптимизировано под устройства пользователей и состояние их сети. По умолчанию используется режим STANDARD.

  • Name. Можно задать имя, чтобы было проще ориентироваться между каналами. Например, в Name можно указать текстовый id трансляции во внутренней системе.

func (c *Client) CreateChannel(ctx context.Context, name string) (*types.Channel, *types.StreamKey, error) { resp, err := c.client.CreateChannel(ctx, &ivs.CreateChannelInput{ Name: aws.String(name), }) if err != nil { return nil, nil, err } return resp.Channel, resp.StreamKey, nil } 

Метод CreateChannel() возвращает описание Channel. Нас оттуда интересуют следующие поля:

  • Arn — уникальный amazon resource name. Его нужно куда-нибудь сохранить, например, в базу, т.к. он нам понадобится позднее для работы с каналом через API Amazon.

  • IngestEndpoint и StreamKey — этот url и ключ, которые понадобятся для отправки видео- и аудиопотоков.

  • PlaybackUrl — это url для просмотра видеопотока зрителями.

Запустим, что у нас получилось к данному моменту:

func run(ctx context.Context) error { client, err := ivsclient.NewClient(ctx) if err != nil { return err } channel, err = client.CreateChannel(ctx, "test") if err != nil { return err } } 

Если вы перейдете в раздел IVS в интерфейсе AWS, то увидите в списке новый канал под названием test.

Ранее мы обсудили, что будем через метаданные потока передавать текущее количество онлайн зрителей. API Amazon предоставляет объект Stream, в котором представлены следующие поля:

  • ViewerCount — текущее количество зрителей.

  • State — состояние стрима, принимает значения LIVE и OFFLINE.

  • Health обозначает уровень «здоровья» стрима. Доступные значения HEALTHY, STARVING и UNKNOWN. Состояние STARVING означает, что есть проблемы с отправкой видео/аудиопотоков. Например, у ведущего трансляции наблюдаются проблемы с интернетом, и происходят задержки отправки потоков.

  • StartTime — время начала трансляции.

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

Чтобы получить данные о Stream, мы можем воспользоваться методом GetStream(). На входе ему надо передать ARN канала:

func (c *Client) GetStream(ctx context.Context, channelARN *string) (*types.Stream, error) { resp, err := c.client.GetStream(ctx, &ivs.GetStreamInput{ ChannelArn: channelARN, }) if err != nil { return nil, err } return resp.Stream, nil } 

Чтобы добавить метаданные в медиапоток, существует метод PutMetadata(). Этот метод принимает ARN канала и строку-метаданные. Важно отметить, что максимальный размер метаданных, которые можно передать за один вызов, ограничен 1KB. Это ограничение стоит держать в голове, потому что передавать мегабайты текста, к сожалению, не получится. Также при вызове PutMetadata() стоит сделать проверку, находится ли Stream в состоянии вещания (LIVE). Иначе AWS вернёт ошибку ChannelNotBroadcasting.

func (c *Client) PutMetadata(ctx context.Context, channelARN *string, metadata string) error { _, err := c.client.PutMetadata(ctx, &ivs.PutMetadataInput{ ChannelArn: channelARN, Metadata:   &metadata, }) return err } 

Теперь объединим методы GetStream и PutMetadata и реализуем функцию streamOnlineViewers:

func streamOnlineViewers(ctx context.Context, client *ivsclient.Client) error { if channelArn == nil || len(*channelArn) == 0 { return errors.New("empty channel arn") } for { stream, err := client.GetStream(ctx, channelArn) if err != nil { return err } if stream.State == types.StreamStateStreamLive { metadata := strconv.FormatInt(stream.ViewerCount, 10) if err := client.PutMetadata(ctx, channelArn, metadata); err != nil { return nil } } time.Sleep(time.Second) } return nil } 

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

Ну и напоследок давайте реализуем удаление канала. Дело в том, что у AWS есть лимит на количество каналов — 5000 в одном регионе. Поэтому после окончания трансляции имеет смысл канал удалить:

func (c *Client) DeleteChannel(ctx context.Context, channelARN *string) error { _, err := c.client.DeleteChannel(ctx, &ivs.DeleteChannelInput{ Arn: channelARN, }) return err } 

Проверяем всё вместе

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

Запустим наш Go код, чтобы создать новый канал:

$ go run . --operation=createChannel Channel arn: arn:aws:ivs:eu-central-1:064934160738:channel/3mLp3ITPmt9t Ingest endpoint: 9c9ef534b100.global-contribute.live-video.net Playback url: https://9c9ef534b100.eu-central-1.playback.live-video.net/api/video/v1/eu-central-1.064934160738.channel.3mLp3ITPmt9t.m3u8 Stream key: sk_eu-central-1_WgIXgdnD8L6G_DiLVpk66XYPD2iOT0SHLa12iJFL58H 

Далее запустим обновление счётчика онлайн зрителей:

$ go run . --operation=streamOnlineViewers --channelArn=arn:aws:ivs:eu-central-1:064934160738:channel/3mLp3ITPmt9t 

Скопируем ingest endpoint и stream key на вкладку с трансляцией видео. Вставим playback url в соответствующее поле на вкладке с плеером. Запустим трансляцию, нажав на кнопку Start Broadcast.

На скриншоте видно, что трансляция успешно запустилась, а плеер корректно проигрывает видеопоток. Задержка составляет 4.5 секунды, что немного больше 3 секунд, но тоже достаточно близко. Также видим, что счётчик зрителей показывает 1. Если мы откроем плеер в другой вкладке или зайдем в админку AWS IVS, то счётчик обновится.

После окончания трансляции удалим канал:

$ go run . --operation=deleteChannel --channelArn=arn:aws:ivs:eu-central-1:064934160738:channel/3mLp3ITPmt9t 

Сколько это стоит

Amazon Interactive Video Service тарифицирует отдельно отправку видео на их серверы (video input) и доставку до зрителей (video output).

Отправка оплачивается за час видеопотока. В зависимости от типа выбранного канала (basic или standard) цена за час составляет $0.2 или $2.

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

Чтобы понять, сколько это может стоить, посчитаем на примере:

  • 1 час видеотрансляции;

  • 1000 зрителей из Европы, в среднем каждый посмотрел по 30 минут;

  • одна половина зрителей смотрели в HD, а другая — в FullHD.

Итого получаем 30 долларов за такую трансляцию:

2 + 500 * 30 / 60 * (0,0375 + 0,0750) = 30,125\$

Заключение

Мы рассмотрели простой пример, как можно воспользоваться Amazon Interactive Video Service для организации трансляций с низкой задержкой доставки видео зрителям. Amazon сделали удобный сервис, который скрывает всю сложность работы с видеотрансляциями за простым интерфейсом библиотек для разных языков.

Interactive Video Service предлагает и другие возможности, которые мы не рассмотрели в этой статье. Например, сохранение трансляций в S3 с возможностью просмотра зрителями в записи. Другой интересной возможностью является IVS Chat для организации общения зрителей во время трансляций. При интеграции с IVS также полезно получать события об изменениях в состоянии трансляций (начало/конец, ошибки, запись стала доступна в S3 для просмотра) через сервис AWS EventBridge. AWS IVS публикует события в EventBridge, на которые можно подписаться в своём бэкенд сервисе.

Из минусов Interactive Video Service можно выделить стоимость сервиса: с ростом объёмов цена может начать кусаться. С другой стороны для быстрого старта и проверки полезности функции видеотрансляций для ваших пользователей Amazon Interactive Video Service может отлично подойти.

Ну и в заключение список полезных ссылок, которые могут помочь в интеграции Interactive Video Service:


ссылка на оригинал статьи https://habr.com/ru/post/707488/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *