Стриминг динамических веб-страниц с сервера на Ubuntu

156
02 января 2022, 13:10

Веду разработку сервиса, который позволяет создавать "живые счетчики" (например, показывающие сколько осталось секунд до наступления того или иного события). Эти счетчики конечно же реализованы с использованием JS. Встала задача создать механизм позволяющий делиться этими счетчиками в соц-сетях, да так чтобы они оставались живыми (то есть чтобы юзер увидев пост мог лицезреть бегущие циферки). Единственным решением я увидел стриминг. То есть видео-стрим. Тот же ВК позволяет создавать видео-трансляции и делится ими. Для создания трансляции со своего рабочего стола достаточно установить необходимый софт, и в его настройках ввести ссылку (URL) и ключ (KEY) потока. Эти данные представляются самим VK при создании трансляции. Но.. как мне сделать так чтобы сервер запускал этот стрим? Причем этот стрим должен захватывать конкретный "живой счетчик"...

Все это в данный момент работает на PHP (Ubuntu + Nginx), но разумеется чтобы решить такую задачу одним только PHP не обойтись. В моей голове выстроилась такая схема

  • Когда юзер сервиса решит поделиться своим "живым счетчиком" и начать его стримить, он врубает галку "стрим" и вводит данные предоставленные соц-сетью (URL и ключ потока)
  • Некий "демон" постоянно крутится на сервере и проверяет БД на предмет единицы в неком поле "streming" в таблице этих самых счетчиков
  • Как только такая запись в БД находится, "демон" тут же должен на сервере запустить браузер в котором будет открыта страница с этим "живым счетчиком". Это окно с браузером должно располагаться в определенном месте на "виртуальном экране"
  • Затем этот же демон должен запустить стрим, подставив URL и ключ потока из бд. Причем захватывать прога для стрима должна именно ту область, где находится нужное окно с браузером на виртуальном экране (ибо для каждого "живого счетчика" должно быть свое окно для отдельного стрима)

Вопрос(ы)

Насколько вообще такой подход жизнеспособен? Не упадет ли сервер если таких стримов будет сразу очень много? Возможно ли вообще нечто подобное провернуть на сервере с Ubuntu? Может есть какие-то более оптимальные варианты решения такой задачи? Знаю что есть puppeteer для эмуляции бразуера, но можно ли его как-то приспособить для стриминга?

Answer 1

Удалось найти решение, не известно насколько удачное, но мало ли, может кому-то пригодится. Решение таки заключается в онлайн-трансляции виджетов (по сути веб-страниц) в соц-сети.

Как это работает?

  • Виджет, с анимированными при помощи JS циферками загружается в увеличенном варианте на отдельной странице (например есть на сайте страница show/{ID} которая отображает этот виджет)
  • Программно эмулируем браузер, "заходим" этим браузером на эту страницу и.. делаем скриншоты этой страницы (например 10 раз в секунду), перезаписывая какой-то один файл
  • Используя какой-то спец-инструмент запускаем трансляцию из этого файла (который постоянно обновляется) в соц-сеть.

Что нам для этого нужно?

  • Node.js + puppeteer - puppeteer это API для управления браузером chrome. То есть при помощи puppeteer сервер как-раз сможет заходить на страницы и рендерить их в картинку. Но есть одно "но" - puppeteer работает на nodejs, поэтому в начале нужно установить сам nodejs на сервер, а затем при помощи нодовского пакетного менеджера npm установить puppeteer. Код "скринкастера" у меня получился таким:

    (async () => {
        try{
            // Заходим на страницу
            const browser = await puppeteer.launch();
            const page = await browser.newPage();
            await page.setViewport({width: WIDTH, height: HEIGHT});
            await page.goto(URL);
            await console.log('Some kind of screen casting started...');
            // Начинаем неистово скриншотить
            // Использую здесь именно for для уникальности названия временных файлов
            for(let i = 0; i < Number.MAX_SAFE_INTEGER; i++)
            {
                try{
                    // Скриншотим во временный файл, затем его  переименовываем
                    // для избежания проблем с блокировкой ресурса
                    await page.screenshot({path: __dirname+'/../frames/frame_'+ID+'.tmp.jpg',quality: QUALITY});
                    await fs.renameSync(__dirname+'/../frames/frame_'+ID+'.tmp.jpg',__dirname+'/../frames/frame_'+ID+'.jpg');
                }
                catch(err){
                    await console.log(err);
                }
            }
            // Выход из браузера
            await browser.close();
            await console.log('Some kind of screen casting stopped.');
        }catch(error){
            console.log(error);
            process.exit();
        }
    })();
    
  • FFmpeg - для трансляции этого изображения в соц-сети. FFmpeg в целом это инструмент для работы с видео, но он также поддерживает возможность трансляции видео по протоколу rtmp, который и используют большинство соц-сетей для проведения трансляций. Во первых нужно установить ffmpeg на сервер, во вторых нужно подобрать правильные параметры запуска чтобы транслировать наш постоянно-меняющийся скриншот как видео. В итоге у меня получилась примерно такая команда: ffmpeg -re -y -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -framerate 15 -re -loop 1 -f image2 -i {$pathToFrame} -c:a aac -strict -2 -ac 2 -ar 44100 -b:a 128k -b:v 2500K -vcodec libx264 -pix_fmt yuv420p -vf scale=1280:-1 -r 30 -g 60 -f flv {$rtmpUrl}. Это запустит трансляцию видео с частотой кадров 15 FPS, битрейтом 2500K, пустой звуковой дорожкой с частотой дискретизации 44100 и битрейтом 128k (иногда соц-сети не запускают трансляцию без аудио). Исходная картинка $pathToFrame должна по размерам соответствовать необходимым требованиям соц-сетей. $rtmpUrl - это сам URL трансляции представляющий из себя URL/KEY (через слеш)

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

READ ALSO
Как создать функцию для добавления нового объекта в массив

Как создать функцию для добавления нового объекта в массив

Создать массив «Список покупок»Каждый элемент массива является объектом, который содержит название продукта, необходимое количество и куплен...

190
Использование компонентов Vue

Использование компонентов Vue

У меня есть компонент(в этом случае не важно какой, важна суть), в котором что-то записано в template, все по дефолту

186
несколько вариантов в indexOf

несколько вариантов в indexOf

Я не силен в js и только начинаю разбираться с нимСмог смастерить вот такой код, его задача:

192
SyntaxError vue js [закрыт]

SyntaxError vue js [закрыт]

Хотите улучшить этот вопрос? Обновите вопрос так, чтобы он вписывался в тематику Stack Overflow на русском

185