Streaming on YouTube from VM w/o video/audio cards

#1 - upgrade ubuntu from 20 to 21

apt updateapt upgradeapt dist-upgradedo-release-upgrade

#2 - prepare the system

cat >> /etc/environment <<'EOF'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"LC_ALL=en_US.UTF-8LANG=en_US.UTF-8LANGUAGE=en_US.UTF-8LC_CTYPE=en_US.UTF-8EOF
apt install pulseaudio-utils libtool autoconf automake mc htop iotop atop iptraf-ng screen nmap sox git cmake libncurses5-dev gettext build-essential libass-dev libssl-dev net-tools v4l2loopback-dkms v4l2loopback-utils lebiniou pkg-config gcc make libglib2.0-dev libfftw3-dev libswscale-dev libsdl2-dev libcaca-dev libjack-dev libsndfile1-dev libmagickwand-dev libjansson-dev libulfius-dev linux-modules-extra-$(uname -r)
#disable firewall and open all portsiptables -D INPUT 6iptables -A INPUT -p tcp -m tcp --dport 30543 -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 1935 -j ACCEPTiptables-save > /etc/iptables/rules.v4ufw disablereboot now

#3 - start building alsa packages

mkdir ~/ffmpeg_sources
cd ~/ffmpeg_sources ;git clone https://github.com/alsa-project/alsa-lib.git; cd alsa-lib/; ./gitcompile; make install
cd ~/ffmpeg_sources;wget -L https://www.alsa-project.org/files/pub/utils/alsa-utils-1.2.6.tar.bz2 ;tar xjvf alsa-utils-1.2.6.tar.bz2;cd alsa-utils-1.2.6;./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --disable-nls; make; make install

#4 - start building ffmpeg packages

cd ~/ffmpeg_sources;curl -O -L https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.bz2 ; tar xjvf nasm-2.15.05.tar.bz2 ;cd nasm-2.15.05;./autogen.sh ;./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin"; make; make install
cd ~/ffmpeg_sources; curl -O -L https://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz ; tar xzvf yasm-1.3.0.tar.gz; cd yasm-1.3.0; ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin"; make ; make install
cd ~/ffmpeg_sources; git clone --branch stable --depth 1 https://code.videolan.org/videolan/x264.git ;cd x264; PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --enable-static;make ; make install
cd ~/ffmpeg_sources ; git clone --branch stable --depth 2 https://bitbucket.org/multicoreware/x265_git ; cd ~/ffmpeg_sources/x265_git/build/linux ; cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/ffmpeg_build" -DENABLE_SHARED:bool=off ../../source;make ; make install
cd ~/ffmpeg_sources; git clone --depth 1 https://github.com/mstorsjo/fdk-aac ; cd fdk-aac; autoreconf -fiv ;./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --disable-shared;make ; make install
cd ~/ffmpeg_sources ; curl -O -L https://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz ;tar xzvf lame-3.100.tar.gz ;cd lame-3.100 ; ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --disable-shared --enable-nasm;make ; make install
cd ~/ffmpeg_sources; curl -O -L https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz; tar xzvf opus-1.3.1.tar.gz ; cd opus-1.3.1;./configure --prefix="$HOME/ffmpeg_build" --disable-shared;make ; make install
cd ~/ffmpeg_sources; git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git ; cd libvpx ; ./configure --prefix="$HOME/ffmpeg_build" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --as=yasm;make ; make install
cd ~/ffmpeg_sourcescurl -O -L https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2 ; tar xjvf ffmpeg-snapshot.tar.bz2 ; cd ffmpegPATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure \ --prefix="$HOME/ffmpeg_build" \ --pkg-config-flags="--static" \ --extra-cflags="-I$HOME/ffmpeg_build/include" \ --extra-ldflags="-L$HOME/ffmpeg_build/lib" \ --extra-libs=-lpthread \ --extra-libs=-lm \ --bindir="$HOME/bin" \ --enable-gpl \ --enable-libfdk_aac \ --enable-libfreetype \ --enable-libmp3lame \ --enable-libopus \ --enable-libvpx \ --enable-libx264 \ --enable-libx265 \ --enable-nonfreemakemake installhash -d ffmpeg
yes | cp -rf ~/bin/* /usr/bin/yes | cp -rf ~/ffmpeg_build/bin/* /usr/bin/

#5 - start building nginx with rtmp module

cd ~/ffmpeg_sources ; git clone https://github.com/arut/nginx-rtmp-module.git ; wget http://nginx.org/download/nginx-1.21.6.tar.gz ; tar -xf nginx-1.21.6.tar.gz ; cd nginx-1.21.6/./configure --prefix=/usr/share/nginx \ --sbin-path=/usr/sbin/nginx \ --modules-path=/usr/lib/nginx/modules \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --http-log-path=/var/log/nginx/access.log \ --pid-path=/run/nginx.pid \ --lock-path=/var/lock/nginx.lock \ --user=nginx \ --group=nginx \ --http-client-body-temp-path=/var/lib/nginx/body \ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ --http-proxy-temp-path=/var/lib/nginx/proxy \ --http-scgi-temp-path=/var/lib/nginx/scgi \ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ --with-compat \ --with-file-aio \ --with-threads \ --with-http_addition_module \ --with-http_auth_request_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_mp4_module \ --with-http_random_index_module \ --with-http_realip_module \ --with-http_slice_module \ --with-http_ssl_module \ --with-http_sub_module \ --with-http_stub_status_module \ --with-http_v2_module \ --with-http_secure_link_module \ --add-module=../nginx-rtmp-module \ --with-mail \ --with-mail_ssl_module \ --with-stream \ --with-stream_realip_module \ --with-stream_ssl_module \ --with-stream_ssl_preread_module
make -j 1; make install
cd ~/ffmpeg_sourcesmkdir -p /etc/nginx/cat > /etc/nginx/nginx.conf <<EOF# automatically generate workers not limited to a fixed amount: http://nginx.org/en/docs/ngx_core_module.html#worker_processesworker_processes auto;
events { # maximum connections per worker process: http://nginx.org/en/docs/ngx_core_module.html#worker_connections worker_connections 1024;}
# RTMP configuration: https://github.com/arut/nginx-rtmp-module/wiki/Directivesrtmp { # a server can contain several applications server { listen 1935; # Listen on standard RTMP port chunk_size 4000; # the bigger the lower cpu-overhead: https://github.com/arut/nginx-rtmp-module/wiki/Directives#chunk_size
# application "live" results in a url like rtmp://localhost:1935/live application live { live on; # enable live-streaming record off; # disable recording to local drive # publish only from localhost allow publish 127.0.0.1; deny publish all;
# Push incoming streams to youtube # push rtmp://a.rtmp.youtube.com/live2/YOUR_UID;
# start a local vlc displaying the incoming stream as soon as one is published to this server: # exec_publish "export DISPLAY=:0.0 && vlc -f --video-on-top --no-video-title-show --mouse-hide-timeout 1 rtmp://localhost/live/$name"; } }}EOFln -s /usr/lib64/nginx/modules /etc/nginx/modulesuseradd -r -d /var/cache/nginx/ -s /sbin/nologin -U nginxmkdir -p /var/cache/nginx/chown -R nginx:nginx /var/cache/nginx/
cat > /lib/systemd/system/nginx.service <<EOF[Unit]Description=nginx - high performance web serverDocumentation=https://nginx.org/en/docs/After=network-online.target remote-fs.target nss-lookup.targetWants=network-online.target
[Service]Type=forkingPIDFile=/var/run/nginx.pidExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.confExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.confExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s TERM $MAINPID
[Install]WantedBy=multi-user.targetEOF
systemctl daemon-reloadsystemctl start nginx.servicesystemctl enable nginx.service

#6 - configure alsa

cat > /etc/asound.conf <<EOF#https://dt.iki.fi/record-system-output-alsa#https://alsa.opensrc.org/Dmix#https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.htmlpcm.!default { type asym playback.pcm "LoopAndReal" capture.pcm "hw:0,0"}pcm.LoopAndReal { type plug slave.pcm mdev route_policy "duplicate"}pcm.mdev { type multi slaves.a.pcm pcm.MixReale slaves.a.channels 2 slaves.b.pcm pcm.MixLoopback slaves.b.channels 2 bindings.0.slave a bindings.0.channel 0 bindings.1.slave a bindings.1.channel 1 bindings.2.slave b bindings.2.channel 0 bindings.3.slave b bindings.3.channel 1}pcm.MixReale { type dmix ipc_key 1024 slave { pcm "hw:Loopback,0,2" rate 48000 periods 128 period_time 0 period_size 1024 # must be power of 2 buffer_size 8192 }}pcm.MixLoopback { type dmix ipc_key 1025 slave { pcm "hw:Loopback,0,1" rate 48000 periods 128 period_time 0 period_size 1024 # must be power of 2 buffer_size 8192 }}EOF

#7 - build a custom alsa plugin for lebiniou https://biniou.net/ - not necessary in some versions

mkdir ~/ffmpeg_sourcesgit clone https://gitlab.com/lebiniou/lebiniou.gitcd lebiniou/sed -i -e "s/default/hw:0,1,2/" ./plugins/input/alsa/alsa.csed -i -e "s/44100/48000/" ./plugins/input/alsa/alsa.c./bootstrap./configuremakemake installyes | cp -rf ./plugins/input/alsa/alsa.so /usr/lib/aarch64-linux-gnu/lebiniou/plugins/input/alsa/

#8 - reboot

reboot now

#9 - start streaming

modprobe v4l2loopback # can be added "devices=3" to set the number of videodevicesmodprobe snd-aloop
AUDIODEV=LoopAndReal play 01.wav #one utility to play on mixed deviceaplay -D LoopAndReal -c2 01.wav #other utility to play on mixed device
LEBINIOU_V4L2LOOPBACK=/dev/video0 LEBINIOU_ALSA_PCM=hw:0,1,2 lebiniou -o v4l2loopback -i alsa #run visualiser with output in raw video and input from alsa hw:0,1,2
#LEBINIOU_V4L2LOOPBACK=/dev/video0 lebiniou -o v4l2loopback -i alsa #run visualiser with output in raw video and input from alsa hw:0,1,2ffmpeg -re -i /dev/video0 -thread_queue_size 4096 -f alsa -i hw:0,1,1 -c:v libx264 -filter:v fps=25 -strict experimental -c:a aac -b:a 192k -threads 4 -ar 44100 -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -rtsp_transport udp -f flv rtmp://localhost:1935/live/t1 #stream video+audio flows in 960x540 resolution by default

#useful commands

aplay -l #list of local sound devicessox # convertor utility from one audio format into anothercat /proc/asound/modules ; lsmod | grep -E "snd_|v4l2loopback" #check that kernel modules are loaded

Alternative visualizer case #1

#! /bin/shbgi="sunset2.jpg" #image is not so important. visualizer will draw on almost black screen!#ffmpeg -y -re -f alsa -i hw:0,1,1 -thread_queue_size 1024 -i "${bgi}" -filter_complex "[0:a]pan=stereo|c0=c0|c1=c0,showcqt=s=1280x360:basefreq=27.5:endfreq=4186.0[vcqt_L];[0:a]pan=stereo|c0=c1|c1=c1,showcqt=s=1280x360:basefreq=27.5:endfreq=4186.0,scale=1280:720,vflip,scale=1280:360[vcqt_R];[vcqt_L][vcqt_R]vstack[v]" -map '[v]' -map '0:a' -strict experimental -c:v libx264 -c:a aac -b:a 192k -threads 4 -ar 48000 -shortest -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -rtsp_transport udp -f flv rtmp://localhost:1935/live/t2

Alternative visualizer case #2

#! /bin/shbgi="sunset2.jpg" #ffmpeg will use image for overlay#ffmpeg -y -re -f alsa -i hw:0,1,1 -thread_queue_size 1024 -i "${bgi}" -filter_complex "[0:a]showcqt=s=1280x720:basefreq=27.5:endfreq=4186.0,split[alpha][bgbase];[1:v]scale=-1:720,pad='1280:720:(ow-iw)/2:(oh-ih)/2',loop=size=2:loop=-1[1v];[1v][alpha]alphamerge[fg];[bgbase]geq=r=0:g=0:b=0[bg];[bg][fg]overlay,fps=25,setsar=1[v]" -map '[v]' -map '0:a' -strict experimental -c:v libx264 -c:a aac -b:a 192k -threads 4 -ar 48000 -shortest -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -rtsp_transport udp -f flv rtmp://localhost:1935/live/t2