LiveStream Raspberry+ffmpeg+nginx
это перепост заметки, оригинал находится на моем сайте: https://lleo.me/dnevnik/2021/01/25
Знакомые попросили сделать видеотрансляцию для промышленного станка с температурой, наложенной на изображение. За два дня узнал много интересного и научился делать трансляции. Моделька смотрит из окна (жми на кнопки).
Для тех, кому это может быть полезно (а в основном для себя, чтобы позже мог зайти в этот пост), я подробно расскажу, как настроить Raspberry, собрать трансляционный nginx на сервере, как сделать правильные настройки ffmpeg, как подключить датчик температуры и вообще оборудовать всё полезными скриптами, чтобы само жило и не висло.
Одновременно у меня есть и вопросы к вам. В основном вопрос почему у меня не заработала встроенная Raspberry-камера, ну и может кто-то даст совет по организации интернет-радио
, раз уж мы увлеклись трансляциями.
Итак, для начала смотрим видео:
<input ... > <input ... > <input ... >
Две кнопки — потому что есть два разных формата веб-вещания, один для для мобильных устройств и Айфонов HLS, другой — для стационарных компьютеров DASH (Firefox, например, отказывается показывать HLS). Андроид показывает оба варианта, но HLS плавнее. Поскольку есть JS-плеер, который поддерживает HLS даже там, где его нет, то формат DASH предлагаю не рассматривать вовсе, дабы не плодить сущностей и машинного времени.
Ну а теперь кому интересно — расскажу по порядку, как я это настраивал. Там несложно.
Raspberry
Берется Raspberry 3, хотя лучше бы 4. Берется сама простая прошивка Raspberry Pi OS Lite, накатывается на карточку:
=============== cut ===============
sudo dd if=Raspberry.iso of=/dev/mmcblk0 status=progress
=============== /cut ===============
Логин «pi», пароль «raspberry». При первом включении потребуется дисплей HDMI или телевизор, иначе не настроить. Это вообще праздник, потому что мне Юра принес кучу Raspberry с выжженными дисплейными портами (для наших задач не нужно), пришлось выбрать рабочую и согнать отца с телевизора на пять минут, у меня дисплей не поддерживает HDMI. Настраивается в консоли через sudo raspi-config, а именно:
1. Настраиваем WiFi (я подключил LAN, но пусть будет): «1 System Options» -> «S1 Wireless LAN»
2. Идем в «3 Interface Options», чтобы включить доступ по SSH «P2 SSH», интерфейс датчика если собираемся его подключить «P7 1-Wire» и интерфейс камеры, если она есть «P1 Camera»
3. В конце обязательно расширяем систему на всю флешку, потом перезагрузимся: «6 Advanced Options» -> «A1 Expand Filesystem»
Настройка закончена.
nginx
Для веб-ретрансляций удобнее всего использовать nginx на любом принадлежащем вам сервере, лучше большом, мощном и далёком. Как это сделать?
Выкиньте нахуй и навсегда Apache2 «sudo apt remove apache2» и поставьте nginx «sudo apt install nginx». Это вообще давно пора было всем сделать. Но сам по себе из коробки nginx работать как видеострим не будет, его надо пересобрать самому с дополнительными опциями и модулем. Поэтому поставив штатный со всей обвязкой, уберите сам файл «sudo mv /usr/sbin/nginx /usr/sbin/nginx.old» и займитесь сборкой новой версии:
=============== cut ===============
# Устанавливаем всё, что понадобится для сборки:
sudo apt-get install libpcre3 libpcre3-dev libssl-dev
# Качаем последние исходники nginx, распаковываем:
wget http://nginx.org/download/nginx-1.17.8.tar.gz
# Качаем модуль трансляции, распаковываем рядом:
wget https://github.com/arut/nginx-rtmp-module/zipball/master -O nginx-rtmp-module-master.zip
unzip nginx-rtmp-module-master.zip -d nginx-rtmp-module-master
# заходим в папку исходников nginx, запускаем конфигурацию, но специальную:
cd nginx
./configure --prefix=/usr --add-module=../nginx-rtmp-module-master/ \
--pid-path=/var/run/nginx.pid --conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log \
--with-http_ssl_module
# собираем и устанавливаем
make
sudo make install
sudo cp ../nginx-rtmp-module-master/stat.xsl /etc/nginx/
# запускаем
sudo service nginx start
=============== /cut ===============
Теперь нужно прописать правильные секции: sudo mcedit /etc/nginx/nginx.conf
Начинаться он должен так:
=============== cut ===============
user www-data;
worker_processes auto;
pid /run/nginx.pid;
rtmp_auto_push on;
rtmp {
record all;
live on;
server {
listen 1935;
application streamer {
hls on;
hls_path /tmp/hls;
hls_fragment 5s;
}
}
}
[...]
=============== /cut ===============
Мы завели канал по имени streamer, где будем создавать любое количество нужных нам трансляций. И включили HLS. Чтобы дать браузерам посетителей доступ к файлам трансляций в /tmp, надо пойти уже в другой файл — файл описания сайта /etc/nginx/sites-enabled/default.conf и добавить туда доступ к /tmp/hls внутрь секции server:
=============== cut ===============
server {
listen 80;
listen [::]:80 ipv6only=on default_server;
server_name lleo.me *.lleo.me;
index index.html index.htm index.php;
location /hls {
root /tmp;
}
[...]
=============== /cut ===============
Настройка закончена.
ffmpeg
Трансляцию готовим с помощью утилиты ffmpeg. Она умеет делать всё с видео и аудио. В нашей задаче:
1. Брать живое видео с камеры
2. Наложить звук из файла mp3, зациклив его (трансляция без звуковой дорожки обычно нигде не работает)
3. Наложить на изображение титры из файла text.txt, указав, что он обновляемый (мы параллельно будем его ежесекундно обновлять другими стредствами)
4. Заливать полученное на трансляционный сервер, которым может быть Youtube или наш удаленный nginx.
Создаем на Raspberry скрипт трансляции /home/pi/lleo/streamGon.sh
=============== cut ===============
#!/bin/sh
while true ; do
/usr/bin/ffmpeg -f video4linux2 -i /dev/video0 \
\
-filter_complex "amovie='/home/pi/lleo/sea.mp3':loop=999,asetpts=N/SR/TB,\
aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo,volume=0.8" \
\
-vf drawtext="fontfile=OpenSans.ttf:textfile='/tmp/text.txt':reload=1:fontsize=28:\
fontcolor=white:borderw=3:bordercolor=black:x=10:y=10" \
\
-vcodec h264_omx -preset ultrafast \
-c:v h264_omx -x264opts "keyint=24:min-keyint=24:no-scenecut" -r 24 \
-bf 1 -b_strategy 0 -sc_threshold 0 -pix_fmt yuv420p \
-c:a aac -b:a 128k \
\
-f flv rtmp://lleo.me/streamer/moe_okno
done
}
=============== /cut ===============
Обратите внимание, что мы используем на Raspberry аппаратный кодек h264_omx вместо классического libx264, что снижает нам загрузку процессора раз в пять (спасибо Кириллу за совет).
Вспомогательные скрипты
Отдельно готовим титры — я написал демона /home/pi/lleo/temperTextStart.php для постоянного обновления файла титров /tmp/text.txt
=============== cut ===============
#!/usr/bin/php
<?php
exec("/usr/bin/ls /sys/bus/w1/devices",$o);
if(sizeof($o)!=2) die('Error Wire');
$unit=$o[0];
while(1) {
$T=trim(file_get_contents("/sys/bus/w1/devices/".$unit."/temperature"));
$T=floor(1*$T/10)/100;
$T2=exec("/usr/bin/vcgencmd measure_temp");
$T2=preg_replace("/[^\d\.]+/s",'',$T2);
$Z=exec("/usr/bin/cat /proc/loadavg");
list($Z,)=explode(' ',$Z,2);
$Z=floor($Z*100/2.2);
$D=date("Y-m-d H:i:s");
$o="$D\nДатчик: ${T}°C\nПроцессор: ${T2}°C\nЗагрузка: ${Z}\%";
file_put_contents("/tmp/text.txt",$o);
usleep(200000);
}
?>
=============== /cut ===============
Чтобы все работало и перезапускалось в случае крэша само, пишу следящего демона демонов /home/pi/lleo/GoneDaemon.sh
=============== cut ===============
#!/bin/bash
[ ТЕКСТ ПОД КАТОМ: Доступен только в оригинальной заметке на сайте ]
S=`/usr/bin/ps awuux | /usr/bin/grep [t]emperTextStart.php | /usr/bin/awk '{print $2}'`
if [ "X$S" == "X" ] ; then
/home/pi/lleo/temperTextStart.php &
fi
S=`/usr/bin/ps awuux | /usr/bin/grep [s]treamGon.sh | /usr/bin/awk '{print $2}'`
if [ "X$S" == "X" ] ; then
S=`/usr/bin/ps awuux | /usr/bin/grep '/[u]sr/bin/ffmpeg' | /usr/bin/awk '{print $2}'`
if [ "X$S" != "X" ] ; then
echo "Stop /usr/bin/ffmpeg: kill -9 $S"
kill -9 $S
fi
/home/pi/lleo/streamGon.sh &
fi
=============== /cut ===============
И прописываю его в «crontab -e» чтобы работал каждые 5 секунд (с долями минут в кронтабе сложно, поэтому так):
=============== cut ===============
* * * * * for ((i=0;i<12;i++)); do /usr/bin/bash /home/pi/lleo/GoneDaemon.sh & /usr/bin/sleep 5; done
=============== /cut ===============
Плеер на сайте
Итак, удаленная Raspberry делает видеопоток и гонит на трансляционный сервер. Тот его разбивает на кусочки для веб-трансляции. Чтобы смотреть на сайте, нужен плеер. Плеера, увы, два — я пока не понял, как сделать один и для десктопов и для мобильных. Для мобильного формата HLS достаточно было бы вставить ссылку на автоматические сознанный заголовок потока: