В этой статье поднимем несколько надоевшую тему вебинаров и инструментов для их проведения. Нет. Писать систему для проведения вебинара не будем. Их уже до нас написано превеликое множество. Обсудим возможность подключить к вебинару рисовалку, чтобы можно было делать пометки от руки и транслировать все это дело в поток.
На первый взгляд решение лежит на поверхности. Надо только добавить на страницу холст — элемент HTML 5 «Canvas», и на нем рисовать. Добавить-то добавили, а как завернуть то, что нарисовано, в WebRTC?
Итак, давайте разберем, что такое WebRTC стриминг с холста, или Canvas стриминг, и какие в нем могут быть подводные камни.
Что такое Canvas?
Если верить Википедии, то:
Canvas (англ. canvas — «холст», рус. канва́с) — элемент HTML5, предназначенный для создания растрового двухмерного изображения при помощи скриптов, обычно на языке JavaScript. Используется, как правило, для отрисовки графиков для статей.
Из плюсов использования Canvas можно выделить:
-
имеет аппаратное ускорение;
-
можно манипулировать каждым пикселем;
Минусы:
-
чрезмерно нагружает процессор и оперативную память;
-
из-за ограничения сборщика мусора нет возможности очистить память;
-
необходимо самому обрабатывать события с объектами;
-
плохая производительность при высоком разрешении;
-
приходится отрисовывать отдельно каждый элемент.
Минусов получилось больше, чем плюсов. Но несмотря на это, у Canvas есть огромное преимущество в использовании — можно стримить поток со слайдами и делать пометки на этих слайдах в процессе вебинара.
Особенности захвата потока
При захвате потока из элемента Canvas браузер считает этот элемент черным. Поэтому текст, который будет написан на холсте черными буквами, при захвате потока сольется с фоном и в поток будет транслироваться черный экран. То же самое может произойти при отрисовке .png изображения с прозрачным фоном и черным рисунком.
Кроме этого, поток не может состоять из одного кадра. Поэтому, когда вы один раз что-то вывели на Canvas, и это изображение статично там висит, поток не может сформироваться, т.к. был всего один кадр. Решение — отрисовывать статичный контент циклично.
Например, ниже функция, которая реализует обновление картинки, отрисованной на Сanvas с частотой 30 fps:
function loop(){ canvasContext.drawImage(img, 10, 10); setTimeout(loop, 1000 / 30); // drawing at 30fps }
Еще один нюанс напрямую связан с политикой безопасности браузеров — «Политикой одного источника».
Изображение, которое нужно нарисовать на холсте, необходимо разместить локально на веб-сервере или на веб-сервере в том же домене. Если изображение, которое будет отрисовано на холсте загружается из Интернета, браузер выдаст исключение.
Обойти эту проблему можно используя технологию Cross-Origin Resource Sharing (CORS), но это выходит за рамки нашей статьи.
Рецепты по приготовлению Canvas
По традиции, дадим готовые рецепты. Рецепта сегодня два:
-
Отрисовка текста на Canvas и стриминг под соусом WebRTC;
-
Отрисовка на Canvas изображения и дальнейший стриминг по WebRTC.
HTML часть будет практически идентична. Создаем HTML файл canvas-streaming-min.html
В хэде страницы пропишем обращения к скриптам:
<script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="canvas-streaming-min.js"></script>
В боди — размещаем HTML 5 элемент Canvas и div элемент для публикации потока. Инициализируем Flashphoner API при загрузке HTML страницы:
<body onload="init_page()"> <canvas width="480" height="320" id="canvas"></canvas> <div id="localDisplay" hidden></div> <br> </body>
Для простоты примера в HTML файле для холста с изображением добавим картинку, которая и будет отрисована на холсте. Файл картинки мы разместили в той же папке на сервере, что и файлы примера:
<img src="2307589.png" alt="logo" id="myimage" >
Полный код HTML страницы для канваса с текстом:
<!DOCTYPE html> <html lang="en"> <head> <script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="canvas-streaming-min.js"></script> </head> <body onload="init_page()"> <canvas width="480" height="320" id="canvas"></canvas> <div id="localDisplay" hidden></div> </body> </html>
Для канваса с изображением:
<!DOCTYPE html> <html lang="en"> <head> <script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="canvas-streaming-min.js"></script> </head> <body onload="init_page()"> <canvas width="480" height="320" id="canvas"></canvas> <div id="localDisplay" hidden></div> <br> <img src="2307589.png" alt="logo" id="myimage" > </body> </html>
Теперь переходим к написанию JS скриптов.
Отрисовка текста на Canvas и стриминг под соусом WebRTC
Определяем константы и глобальные переменные:
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;
Пишем на Canvas фразу «Hello World». Нарисуем желтый прямоугольник 320px на 176px, текст напишем шрифтом Arial, кегль 30px, цвет — синий. Используем функцию «loop()» которую уже упоминали выше, чтобы перерисовывать канвас с частотой 30 fps:
function createCanvas() { var canvasContext = canvas.getContext ("2d"); canvasContext.font = "30px Arial"; canvasContext.fillStyle = "yellow"; canvasContext.fillRect(0,0,320,176); canvasContext.strokeStyle = 'black'; canvasContext.fillStyle = "blue"; (function loop(){ canvasContext.fillText("Hello World!", 10, 50); setTimeout(loop, 1000 / 30); // drawing at 30fps })(); }
Подключаемся к WCS серверу через WebSocket и публикуем поток с канваса:
//Connect to WCS server over webSockets function connect() { session = Flashphoner.createSession({ urlServer: "wss://demo.flashphoner.com:8443" //specify the address of your WCS }).on(SESSION_STATUS.ESTABLISHED, function(session) { publishStream(session); }); } //Capturing stream of Canvas element function publishStream(session) { publishStream = session.createStream({ name: "test-canvas", display: localDisplay, constraints: { audio: false, video: false, customStream: canvas.captureStream(30) } }); publishStream.publish(); }
Полный код JS скрипта для отрисовки холста с текстом и захвата его в поток:
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay; //init api function init_page() { Flashphoner.init({}); localDisplay = document.getElementById("localDisplay"); canvas = document.getElementById("canvas"); createCanvas(); connect(); } //create Canvas function createCanvas() { var canvasContext = canvas.getContext ("2d"); canvasContext.font = "30px Arial"; canvasContext.fillStyle = "yellow"; canvasContext.fillRect(0,0,320,176); canvasContext.strokeStyle = 'black'; canvasContext.fillStyle = "blue"; (function loop(){ canvasContext.fillText("Hello World!", 10, 50); setTimeout(loop, 1000 / 30); // drawing at 30fps })(); } //Connect to WCS server over webSockets function connect() { session = Flashphoner.createSession({ urlServer: "wss://demo.flashphoner.com:8443" //specify the address of your WCS }).on(SESSION_STATUS.ESTABLISHED, function(session) { publishStream(session); }); } //Capturing stream of Canvas element function publishStream(session) { publishStream = session.createStream({ name: "test-canvas", display: localDisplay, constraints: { audio: false, video: false, customStream: canvas.captureStream(30) } }); publishStream.publish(); }
Привет мир!
-
Откройте созданную Web-страницу. В этом примере мы не используем кнопок, поэтому канвас отрисовывается сразу при инициализации страницы:
-
В другой вкладке браузера откройте демо-пример «Player» на вашем WCS сервере. Укажите имя потока, в который был захвачен canvas и запустите воспроизведение. На скриншоте исходный канвас и вид потока в плеере:
Отрисовка на Canvas изображения и дальнейший стриминг по WebRTC
Определяем константы и глобальные переменные:
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;
Получаем картинку с web-страницы и циклично отрисовываем ее на канвасе, чтобы получить 30 кадров в секунду:
function createCanvas() { var canvasContext = canvas.getContext ("2d"); var img = document.getElementById ("myimage"); (function loop(){ canvasContext.drawImage(img, 10, 10); setTimeout(loop, 1000 / 30); // drawing at 30fps })(); }
Подключаемся к WCS серверу через WebSocket и публикуем поток с канваса:
//Connect to WCS server over webSockets function connect() { session = Flashphoner.createSession({ urlServer: "wss://demo.flashphoner.com:8443" //specify the address of your WCS }).on(SESSION_STATUS.ESTABLISHED, function(session) { publishStream(session); }); } //Capturing stream of Canvas element function publishStream(session) { publishStream = session.createStream({ name: "test-canvas", display: localDisplay, constraints: { audio: false, video: false, customStream: canvas.captureStream(30) } }); publishStream.publish(); }
Полный код JS скрипта для отрисовки холста с изображением и захвата его в поток:
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay; //init api function init_page() { Flashphoner.init({}); localDisplay = document.getElementById("localDisplay"); canvas = document.getElementById("canvas"); createCanvas(); connect(); } //create Canvas function createCanvas() { var canvasContext = canvas.getContext ("2d"); var img = document.getElementById ("myimage"); (function loop(){ canvasContext.drawImage(img, 10, 10); setTimeout(loop, 1000 / 30); // drawing at 30fps })(); } //Connect to WCS server over webSockets function connect() { session = Flashphoner.createSession({ urlServer: "wss://demo.flashphoner.com:8443" //specify the address of your WCS }).on(SESSION_STATUS.ESTABLISHED, function(session) { publishStream(session); }); } //Capturing stream of Canvas element function publishStream(session) { publishStream = session.createStream({ name: "test-canvas", display: localDisplay, constraints: { audio: false, video: false, customStream: canvas.captureStream(30) } }); publishStream.publish(); }
Привет мир! часть вторая
-
Откройте созданную Web-страницу. Канвас будет отрисован сразу при инициализации страницы. Картинка (в нижней части страницы) отображается на холсте (в верхней части страницы):
-
Откройте демо-пример «Player» на вашем WCS сервере. Укажите имя потока, в который был захвачен canvas и запустите воспроизведение. На скриншоте исходный канвас и вид потока в плеере:
Заключение
В этой статье мы не ставили целью показать что-то принципиально новое. Но среди обращений в нашу техническую поддержку бывают вопросы по стримингу с канваса, и в этой статье мы разобрали минимальные примеры кода, с помощью которых можно использовать этот функционал в своем проекте.
Хорошего стриминга!
Ссылки
WCS на Amazon EC2 — Быстрое развертывание WCS на базе Amazon
WCS на DigitalOcean — Быстрое развертывание WCS на базе DigitalOcean
WCS в Docker — Запуск WCS как Docker контейнера
Документация по быстрому развертыванию и тестированию WCS сервера
Документация по захвату и трансляции видеопотока с элемента HTML5 Canvas (whiteboard)
ссылка на оригинал статьи https://habr.com/ru/company/flashphoner/blog/568680/
Добавить комментарий