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 долларов за такую трансляцию:
Заключение
Мы рассмотрели простой пример, как можно воспользоваться 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://ivs.rocks/ — сайт с примерами применения.
-
https://aws.amazon.com/ru/developer/tools/ — доступные SDK.
-
https://docs.aws.amazon.com/ivs/latest/userguide/what-is.html — руководство пользователя.
-
https://aws.amazon.com/ivs/pricing/ — цены на сервис.
-
https://github.com/mastersobg/awsivs — репозиторий с исходным кодом для данной статьи.
ссылка на оригинал статьи https://habr.com/ru/post/707488/
Добавить комментарий