Как транслировать прямую трансляцию на Amazon IVS из браузера

Как транслировать прямую трансляцию на Amazon IVS из браузера

16 ноября 2022 г.

Добро пожаловать в эту серию статей, в которой мы узнаем, как начать прямую трансляцию в облаке с помощью Amazon Interactive Video Service (Amazon IVS). Если вы впервые присоединяетесь к нам, я призываю вас наверстать упущенное, прочитав остальные сообщения в этой серии!

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

В сегодняшней публикации мы собираемся переключиться и рассмотреть другой вариант — трансляцию нашей прямой трансляции прямо из браузера!

Почему?

Хороший вопрос! Существует множество вариантов рабочего стола для прямой трансляции, и многие из них предлагают такие функции, как настраиваемые сцены, фоны, анимированные переходы и многое другое. Но иногда бизнес-правила наших приложений не требуют всех этих функций. Иногда вам просто нужно транслировать в прямом эфире камеру и микрофон — и, возможно, поделиться своим рабочим столом со своими зрителями.

Для настольных потоковых решений требуется установка и знание стороннего программного обеспечения. Это не всегда желательно (или даже вариант). По этим причинам (среди прочих) SDK веб-вещания Amazon IVS дает нам возможность выполнять потоковую передачу на наш канал непосредственно из веб-браузеров.

:::информация Трансляция из браузера не означает, что вы не можете иметь причудливые фоны, наложения, анимацию и переходы, доступные во многих предложениях для настольных компьютеров. С современными HTML и JavaScript можно сделать массу вещей, и мы рассмотрим некоторые из них в следующем посте. Сегодня мы сосредоточимся на потоковой передаче с основной камеры и микрофона.

:::

Сбор информации о нашей трансляции

Как и во многих предыдущих сообщениях этой серии, мы собираемся создать демонстрацию с помощью CodePen.

Чтобы использовать SDK веб-вещания, нам потребуется немного информации.

Использование консоли управления Amazon IVS

Один из способов получить эти биты — войти в консоль управления Amazon IVS (https://console.aws.amazon. com/ivs) и выбираем канал, с которым работаем. В сведениях о канале прокрутите вниз до раздела Конфигурация потока и скопируйте Конечную точку загрузки и Ключ потока.

Использование интерфейса командной строки AWS

Если вы предпочитаете получать конечную точку загрузки и ключ потоковой передачи через интерфейс командной строки, вы можете сделать это с помощью нескольких отдельных вызовов. Во-первых, нам понадобится канал ARN. Мы можем получить это через вызов list-channels(docs) и используя канал Name в качестве фильтра. В этом примере demo-channel — это мой канал Name.

$ CHANNEL_ARN=$(aws ivs list-channels --filter-by-name "demo-channel" --query "channels[0].arn" --output text)

Теперь мы можем получить конечную точку Ingest с помощью вызова get-channel (документы).

$ aws ivs get-channel --arn=$CHANNEL_ARN --query "channel.ingestEndpoint" --output text

Чтобы получить ключ потока, нам сначала нужно получить его ARN. Мы можем получить это через list-stream-keys (документы).

$ STREAM_KEY_ARN=$(aws ivs list-stream-keys --channel-arn=$CHANNEL_ARN --query "streamKeys[0].arn" --output text)

Наконец, мы получаем значение ключа потока с помощью get-stream-key (документы).

$ aws ivs get-stream-key --arn=$STREAM_KEY_ARN --query "streamKey.value" --output text

Использование AWS SDK

Если вы хотите использовать SDK для получения конечной точки загрузки и ключа потоковой передачи, см. Документация SDK для вашего любимого языка. Вот пример того, как это можно сделать с помощью Node.JS SDK:

import {
    IvsClient,
    GetChannelCommand,
    GetStreamKeyCommand,
    ListChannelsCommand,
    ListStreamKeysCommand
} from "@aws-sdk/client-ivs";

const client = new IvsClient();
const channelName = 'demo-channel';

// list channels, filtering by name, to get the ARN
const listChannelsRequest = new ListChannelsCommand({ filterByName: channelName });
const listChannelsResponse = await client.send(listChannelsRequest);

if (!listChannelsResponse.channels.length) {
    console.warn(`No channels matching '${channelName}' were found!`);
    const process = await import('node:process')
    process.exit(1);
}

const channelArn = listChannelsResponse.channels[0].arn;
console.log(`Channel ARN: ${channelArn}`);

// get the channel (by ARN) to get ingestEndpoint
const getChannelRequest = new GetChannelCommand({ arn: channelArn });
const getChannelResponse = await client.send(getChannelRequest);
const ingestEndpoint = getChannelResponse.channel.ingestEndpoint;
console.log(`Ingest Endpoint: ${ingestEndpoint}`);

// list stream keys to get the stream key ARN
const listStreamKeysRequest = new ListStreamKeysCommand({ channelArn: channelArn });
const listStreamKeysResponse = await client.send(listStreamKeysRequest);
const streamKeyArn = listStreamKeysResponse.streamKeys[0].arn;

// get stream key
const getStreamKeyRequest = new GetStreamKeyCommand({ arn: streamKeyArn });
const getStreamKeyResponse = await client.send(getStreamKeyRequest);
const streamKey = getStreamKeyResponse.streamKey.value;
console.log(`Stream Key: ${streamKey}`);

Запуск приведенного выше кода с допустимым channelName приведет к выводу, подобному следующему.

Channel ARN: arn:aws:ivs:us-east-1:<redacted>:channel/<redacted>
Ingest Endpoint: <redacted>.global-contribute.live-video.net
Stream Key: sk_us-east-1_<redacted>

Создание демонстрации веб-трансляции

Для начала нам нужно включить SDK веб-вещания на нашу страницу.

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

Прежде чем мы рассмотрим использование SDK веб-вещания, давайте добавим на страницу HTML-разметку. Мы начнем с элемента <canvas>, который мы можем использовать для предварительного просмотра того, что будет транслироваться нашим зрителям.

Мы добавим два элемента <select>, чтобы позволить вещательной компании выбрать камеру и микрофон, используемые для захвата аудио и видео. Поскольку эта демонстрация выполняется в CodePen, мы также добавим несколько текстовых входных данных для захвата конечной точки загрузки и ключа потоковой передачи, необходимых для настройки широковещательного клиента.

Нам понадобится кнопка для запуска трансляции, поэтому мы добавим ее под текстовые поля. Чтобы управлять макетом и стилем, я включил Bootstrap в CodePen и применил некоторые соответствующие классы к макету и входным данным.

<div class="row">
  <div class="col-sm-6 offset-sm-3">
    <span class="badge bg-info fs-3 d-none mb-3 w-100" id="online-indicator">Online</span>
    <canvas id="broadcast-preview" class="rounded-4 shadow w-100"></canvas>
  </div>
</div>

<div class="d-flex flex-column col-sm-6 offset-sm-3 p-1">
  <select name="cam-select" id="cam-select" class="form-select w-100 mb-3"></select>
  <select name="mic-select" id="mic-select" class="form-select w-100 mb-3"></select>
  <input type="text" name="endpoint" id="endpoint" class="form-control w-100 mb-3" placeholder="Ingest Endpoint" />
  <input type="password" name="stream-key" id="stream-key" class="form-control w-100 mb-3" placeholder="Stream Key" />
  <button class="btn btn-primary w-100 shadow" id="stream-btn">Stream</button>
</div>

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

Подключение предпросмотра трансляции, камеры и микрофона

Давайте добавим в демонстрацию немного JavaScript, чтобы мы могли заполнить раскрывающиеся списки камеры и микрофона и предварительно просмотреть камеру вещателя. Добавьте функцию init() и обработчик для вызова этой функции, когда DOM будет готов.

const init = async () => {

};
document.addEventListener('DOMContentLoaded', init);

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

Давайте добавим функцию handlePermissions(), чтобы позаботиться об этом.

const handlePermissions = async () => {
  let permissions = { video: true, audio: true };
  try {
    await navigator.mediaDevices.getUserMedia(permissions);
  }
  catch (err) {
    console.error(err.message);
    permissions = { video: false, audio: false };
  }
  if (!permissions.video) console.error('Failed to get video permissions.');
  if (!permissions.audio) console.error('Failed to get audio permissions.');
};

Функция handlePermissions() использует navigator.mediaDevices.getUserMedia() для получения разрешений видео и аудио через Объект разрешения. Мы назовем эту функцию самым первым действием внутри нашей функции init().

const init = async () => {
  await handlePermissions();
};

Затем мы получим список видео- и аудиоустройств (камера и микрофон) с помощью navigator.mediaDevices.enumerateDevices() и заполним элементы <select>. Мы установим первое найденное устройство как «выбранное» по умолчанию.

const getDevices = async () => {
  const cameraSelect = document.getElementById('cam-select');
  const micSelect = document.getElementById('mic-select');
  const devices = await navigator.mediaDevices.enumerateDevices();
  const videoDevices = devices.filter((d) => d.kind === 'videoinput');
  const audioDevices = devices.filter((d) => d.kind === 'audioinput');
  videoDevices.forEach((device, idx) => {
    const opt = document.createElement('option');
    opt.value = device.deviceId;
    opt.innerHTML = device.label;
    if (idx === 0) {
      window.selectedVideoDeviceId = device.deviceId;
      opt.selected = true;
    }
    cameraSelect.appendChild(opt);
  });
  audioDevices.forEach((device, idx) => {
    const opt = document.createElement('option');
    opt.value = device.deviceId;
    opt.innerHTML = device.label;
    if (idx === 0) {
      window.selectedAudioDeviceId = device.deviceId;
      opt.selected = true;
    }
    micSelect.appendChild(opt);
  });
};

И обновите нашу функцию init(), чтобы она вызывала getDevices().

const init = async () => {
  await handlePermissions();
  await getDevices();
};

Создание широковещательного клиента

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

const init = async () => {
  await handlePermissions();
  await getDevices();

  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.STANDARD_LANDSCAPE,
  });
};

В зависимости от типа канала, на который вы транслируете, вам может потребоваться обновить значение streamConfig до одного из доступных пресетов:

IVSBroadcastClient.BASIC_LANDSCAPE;
IVSBroadcastClient.STANDARD_LANDSCAPE;
IVSBroadcastClient.BASIC_PORTRAIT;
IVSBroadcastClient.STANDARD_PORTRAIT;

Создание видео- и аудиопотоков

Теперь, когда у нас есть широковещательный клиент, мы можем добавить наши устройства ввода видео и аудио к клиенту с помощью addVideoInputDevice() и addAudioInputDevice() соответственно. Мы будем повторно использовать эти функции, чтобы позволить вещателям переключать камеру и микрофон во время трансляции, если они этого хотят, поэтому мы добавим некоторую логику, чтобы сначала удалить все существующие устройства, прежде чем добавлять устройство.

const createVideoStream = async () => {
  if (window.broadcastClient && window.broadcastClient.getVideoInputDevice('camera1')) window.broadcastClient.removeVideoInputDevice('camera1');
  const streamConfig = IVSBroadcastClient.STANDARD_LANDSCAPE;
  window.videoStream = await navigator.mediaDevices.getUserMedia({
    video: {
      deviceId: { exact: window.selectedVideoDeviceId },
      width: {
        ideal: streamConfig.maxResolution.width,
        max: streamConfig.maxResolution.width,
      },
      height: {
        ideal: streamConfig.maxResolution.height,
        max: streamConfig.maxResolution.height,
      },
    },
  });
  if (window.broadcastClient) window.broadcastClient.addVideoInputDevice(window.videoStream, 'camera1', { index: 0 });
};

Функция captureAudioStream() аналогична функции captureVideoStream():

const createAudioStream = async () => {
  if (window.broadcastClient && window.broadcastClient.getAudioInputDevice('mic1')) window.broadcastClient.removeAudioInputDevice('mic1');
  window.audioStream = await navigator.mediaDevices.getUserMedia({
    audio: {
      deviceId: window.selectedAudioDeviceId
    },
  });
  if (window.broadcastClient) window.broadcastClient.addAudioInputDevice(window.audioStream, 'mic1');
};

Давайте изменим функцию init(), чтобы она вызывала их.

const init = async () => {
  await handlePermissions();
  await getDevices();

  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.STANDARD_LANDSCAPE,
  });

  await createVideoStream();
  await createAudioStream();
};

Далее мы добавим две функции для обновления выбранного deviceId при изменении значения <select>. Позже мы добавим для них прослушиватели событий, а пока просто добавим функции.

const selectCamera = async (e) => {
  window.selectedVideoDeviceId = e.target.value;
  await createVideoStream();
};

const selectMic = async (e) => {
  window.selectedAudioDeviceId = e.target.value;
  await createAudioStream();
};

Предварительный просмотр видеопотока

Теперь, когда мы добавили наше устройство ввода видео в поток, мы можем предварительный просмотр в элементе <canvas> с помощью функции previewVideo().

const previewVideo = () => {
  const previewEl = document.getElementById('broadcast-preview');
  window.broadcastClient.attachPreview(previewEl);
};

И добавьте вызов previewVideo() в init().

const init = async () => {
  await handlePermissions();
  await getDevices();

  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.STANDARD_LANDSCAPE,
  });

  await createVideoStream();
  await createAudioStream();

  previewVideo();
};

Подготовка к трансляции

Мы почти готовы к трансляции на наш канал. Давайте добавим функцию toggleBroadcast() для обработки кликов по кнопке "Поток".

const toggleBroadcast = () => {
  if(!window.isBroadcasting) {
    startBroadcast();
  }
  else {
    stopBroadcast();
  }
};

Здесь мы проверяем, транслируется ли поток в данный момент, и вызываем startBroadcast() или stopBroadcast().

Функция startBroadcast() проверит конечную точку загрузки и ключ потока, обновит пользовательский интерфейс, а затем вызовет startBroadcast() в broadcastClient на начать трансляцию.

const startBroadcast = () => {
  const key = document.getElementById('stream-key').value;
  const endpoint = document.getElementById('endpoint').value;
  const streamBtn = document.getElementById('stream-btn');
  const onlineIndicator = document.getElementById('online-indicator');

  if(!key && !endpoint) {
    alert('Please enter a Stream Key and Ingest Endpoint!');
  }
  else {
    window.broadcastClient
      .startBroadcast(key, endpoint)
      .then(() => {
        streamBtn.innerHTML = 'Stop';
        onlineIndicator.classList.remove('d-none');
        window.isBroadcasting = true;
      })
      .catch((error) => {
        streamBtn.innerHTML = 'Stream';
        onlineIndicator.classList.add('d-none');
        window.isBroadcasting = false;
        console.error(error);
      });  
  }
};

Функция stopBroadcast(), как вы могли догадаться, вызовет stopBroadcast() в broadcastClient и обновит пользовательский интерфейс.

const stopBroadcast = () => {
  window.broadcastClient.stopBroadcast();
  window.isBroadcasting = false;
  document.getElementById('online-indicator').classList.add('d-none');
}

Наконец, мы закончим функцию init(), добавив прослушиватели событий для обновления устройств камеры и микрофона и вызовем toggleBroadcast(), когда пользователь нажимает кнопку «Поток». кнопка.

const init = async () => {
  await handlePermissions();
  await getDevices();

  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.STANDARD_LANDSCAPE,
  });

  await createVideoStream();
  await createAudioStream();

  previewVideo();

  document.getElementById('cam-select').addEventListener('change', selectCamera);
  document.getElementById('mic-select').addEventListener('change', selectMic);
  document.getElementById('stream-btn').addEventListener('click', toggleBroadcast);
};

Трансляция на канал Amazon IVS из Интернета

Мы готовы к трансляции! Для начала откройте демо в отдельной вкладке браузера.

:::информация Внимание! Поскольку CodePen выполняет встраивание в <iframe>, мы не можем встроить эту демонстрацию непосредственно в этот пост блога из-за изолированной природы <iframe> на HackerNoon. просмотрите CodePen непосредственно в отдельной вкладке браузера.

:::

Подключите конечную точку загрузки и ключ потока и нажмите "Поток", чтобы попробовать. Вы можете убедиться, что ваша трансляция транслируется с помощью функции «Предварительный просмотр в реальном времени» в консоли управления Amazon IVS после обновления пользовательского интерфейса для подтверждения. что ваша трансляция онлайн.

:::подсказка Примечание. Если трансляция вашего потока не начинается, убедитесь, что конечная точка загрузки и ключ потока введены точно так, как показано в консоли управления Amazon IVS. Если у вас по-прежнему возникают проблемы, проверьте, не блокирует ли VPN-подключение требуемый порт. Если это так, повторите демонстрацию, отключившись от VPN.

:::

Обзор

В этом посте мы узнали, как транслировать на наш канал Amazon IVS с помощью SDK Amazon IVS Web Broadcast SDK. Дополнительные сведения см. в документах SDK.


Также опубликовано здесь .


Оригинал