Jitsi mit einem eigenen Open Source Streaming-Server betreiben

Jitsi Videokonferenzen funktionieren bei geeigneten Einstellungen mit über 100 Teilnehmern. Voraussetzung dafür ist, dass der erste Teilnehmer (der Veranstalter) dafür sorgt, dass der erste Teilnehmer (Moderator des Raumes) in den Settings „Everyone start muted“, „Everyone start hidden“ und „Everyone follows me“ anwählt. Dieses Szenario ist mit über 90 Teilnehmern erfolgreich getestet worden. Dann senden die Zuschauer nicht, können ihre Kamera und Mikrofon aber selber einschalten. Wenn das der Reihe nach getan wird, kann sehr einfach ermittelt werden, wie viele gleichzeitige Teilnehmer funktionieren, da der Teilnehmer mit der geringsten Bandbreite das Nadelöhr darstellt. Für reine Vorlesungsszenarien reicht das aber häufig schon aus.

Jitsi bietet aber auch die Möglichkeit externe Streaming Dienste anzubinden wie z.B, youtube oder einen eigenen nginx-Streamingserver. Wie das technisch realisiert werden kann, wird hier kurz beschrieben. Mir fehlt leider die Zeit das detailliert zu tun, da ich nicht mehr lange für die Uni Duisburg-Essen (UDE) arbeiten werde.

Die Komponente in Jitsi, die Aufzeichnungen und Stream ermöglicht heißt Jibri. https://github.com/jitsi/jibri

Bitte beachten, wenn mehrere Streams oder Aufzeichnung zur gleichen Zeit erfolgen sollen, benötigt man eine ganze Farm von Jibri-Maschinen, die mit einem unterschiedlichen „Nickname“ (siehe unten) konfiguriert werden müssen. Best Practice: Einen jibri konfigurieren und dann klonen. („Nickname“ anpassen nicht vergessen). Das schreit eigentlich nach dynamischen Instanzen, die per Docker oder ähnlich on the fly erzeugt werden können (hier nicht realisiert, nur als Idee angerissen).

Die Installation von Jibri funktioniert, anders als in der Anleitung angegeben, auch unter Ubuntu 18.4LTS (und sehr wahrscheinlich auch 20.4LTS). Wichtig ist aber, dass unbedingt Java8 konfiguriert wird:

$ sudo update-alternatives –config java

Achtung: Anders als in der Anleitung heißen die Jitsi-internen Virtual Hosts für die beiden Control-Kanäle (hier im Beispiel heißt der Jitsi-Server jitsi.zim.uni-due.de, dass muss individuell angepasst werden):

VirtualHost „auth.jitsi.zim.uni-due.de“ (in /etc/prosody/conf.avail )

und

Component „internal.auth.jitsi.zim.uni-due.de“ „muc“

Diese Namen müssen sich in der jibji config.json wiederspiegeln.

Unbedingt sind dann auch die Passwörter für die richtigen „virtuellen“ Domänen (mit Domain meint Jitsi ein eigenes virtuelles Subdomain-Konzept, dass gar nichts mit echten Subdomains zu tun hat) zu setzen:

$ prosodyctl register jibri auth.jitsi.zim.uni-due.de *********

$ prosodyctl register recorder recorder.jitsi.zim.uni-due.de ********

(Mit den vergebenen Passwörtern im Config-File.)

Damit alle Komponenten zusammenspielen, muss auch die Jicofo-Konfiguration angepasst werden:

Man editiere dazu  /etc/jitsi/jicofo/sip-communicator.properties, um für die MUC (Multi User Conference, Jitsi basiert ja auf Jabber) die Jitsi-Stream-Brauerei (ein Bier für den Admin 😉 , also die Jibri-Worker bekannt zu  machen. Das muss mit dem Eintrag in der jibri’s config.json korrespondieren. Jicofo muss nach der Änderung neu gestartet werden.

org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.yourdomain.com

muss bei unserem Beispiel

org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.jitsi.zim.uni-due.de

sein!

Dann müssen noch in /usr/share/jitsi-meet/interface_config.js
die  TOOLBAR_BUTTONS für ‘livestreaming’ aktiviert werden.

Damit kann man dann z.B. schon zu youtube streamen, wenn man dort einen Streaming Account hat. Ich denke auch Wowza würde  mit dem /etc/hosts Hack gehen, ich hatte aber keine Zeit mehr das an der UDE auszuprobieren.

Damit die Jibri-Instanzen (Farm) nicht zu youtube sondern auf einen eigenen Server streamen, einfach in /etc/hosts für youtube.com die IP des Streamingservers einsetzen. D.h. die Rechner der Jibri-Farm halten den eigenen Streamingserver für youtube. Das hat den Vorteil, dass nichts im Jitsi-Quellcode geändert werden muss.

Im Beispiel an der UDE heißt der Streamingserver livestream.zim.uni-due.de .

Die  JIBRI Farm

Jeweils in /etc/jitsi/jibri/config.json die Nicknamen ändern.

„nickname“: „jibri-nickname2“

siehe: https://github.com/jitsi/jibri/issues/187 (ganz unten)

dann

$ sudo systemctl restart jibri

In den Logfiles sieht man dann, ob sich die Jibri-Instanzen ordentlich am jicofo anmelden.

Ein eigener Open-Source Streaming-Server

Der Streaming-Server, hier ganz in Open Source ist ein nginx mit selbst kompilierten Modul realisiert worden:

Prinzipiell bin ich nach dieser Anleitung vorgegangen

https://obsproject.com/forum/resources/how-to-set-up-your-own-private-rtmp-server-using-nginx.50/

Zum Bauen des nginx:

$ sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev

$ wget http://nginx.org/download/nginx-1.15.1.tar.gz

Das RTMP-Modul:

$ wget https://github.com/sergey-dryabzhinsky/nginx-rtmp-module/archive/dev.zip

$ tar -zxvf nginx-1.15.1.tar.gz

$ unzip dev.zip

$ cd nginx-1.15.1

Build  nginx:

$ ./configure –with-http_ssl_module –add-module=../nginx-rtmp-module-dev

$ make

$ sudo make install

Als Ergänzung habe ich auch ein LDAP-Modul in den nginx einkompiliert:

hiermit:

https://github.com/kvspb/nginx-auth-ldap/blob/master/example.conf

mit

$ ./configure –with-http_ssl_module –add-module=../nginx-rtmp-module-dev  –add-module=../nginx_ldap_module/nginx-auth-ldap-master

$ make

$ sudo make install

Restart nginx:

$ sudo /usr/local/nginx/sbin/nginx -s stop

$ sudo /usr/local/nginx/sbin/nginx

Der nginx ist selbstkompiliert und lebt nach install unter /usr/local/nginx.

Gestartet wird er bei einem Reboot über einen cronjob:

@reboot root /usr/local/nginx/sbin/nginx

@reboot root /home/zim026/worker.sh 2>/dev/null (für die Playlist, siehe unten)

So wie er nun ist, kann man den Streaming-Server mit OBS-Studio oder Videolan per RTMP füttern und sich den Stream in einem vlc medial player anschauen.

Per OBS-Studio geht das so:

Ein Profil erzeugen mit:

Streaming Service: Custom
Server: rtmp://<your server ip>/live
Play Path/Stream Key: test

Und dann mit VLC (Videolan):

rtmp://<your server ip>/live/test

Das war der einfache Teil ;-).

Ich will aber mehr und den Stream im Browser per Javascript als HLS anschauen. Dazu habe ich folgendes getan:

Der Job /home/zim026/worker.sh (landet in der Crotab) erzeugt eine dynamische Übersichtsseite:

 



#!/bin/bash

#erst mal dauernd
while :
do
cd /tmp/hls
#alias proj="cd /tmp/hls"

#jhome () {
#  cd /tmp/hls

#touch test.m3u8
#touch test2.m3u8
echo > /usr/local/nginx/html/playlist_cont.html
j=0
x=X
for i in *.m3u8 ; do
if [ "${i:0:1}" != "X" ]
then
echo "<a href='https://livestream.zim.uni-due.de/stream5.html?s=$i'>Stream: [$i] <base target="_parent"></a><br><br>" >> /usr/local/nginx/html/playlist_cont.html
fi
j=$(( $j + 1 ))
done

OUTPUT=`cat /usr/local/nginx/html/playlist_cont.html`
if echo "$OUTPUT" | grep -q "*"; then
echo "<p>Kein öffentlicher Livestream verfübar.</p>" >/usr/local/nginx/html/playlist_cont.html
j=$(( $j - 1 ))
fi

echo  "<p>"  >>/usr/local/nginx/html/playlist_cont.html
echo $j >> /usr/local/nginx/html/playlist_cont.html
echo ' aktive Streams.'  >> /usr/local/nginx/html/playlist_cont.html

#cat /usr/local/nginx/html/playlist_head.html /usr/local/nginx/html/playlist.txt /usr/local/nginx/html/playlist_footer.html > /usr/local/nginx/html/playlist.html

#} # fuer das cd

sleep 5
done

Optional können auch verborgene Streams erzeugt werden, denen im Namen ein „X“ vorangestellt wird. Diese tauchen dann nicht auf der Playlist auf, sind aber, wie unten in der Anleitung beschreienen, aufrufbar.

Das passende stream5.html HTML dazu, dass die hls-Videos mit Hilfe des  videojs-contrib-hls.js javascript-Library abspielt:
Siehe auch (es klappt bei mir mit Version 7):
https://videojs.com/getting-started/

Da es Umbruchprobleme im Blog gibt, hier die Datei stream5_html.txt auch zum Herunterladen.


<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>ZIM Livestream</title>

<!--

Uses the latest versions of video.js and videojs-contrib-hls.

To use specific versions, please change the URLs to the form:

<link href="video-js.css" rel="stylesheet">
<script src="video.js"></script>
<script src="videojs-contrib-hls.js"></script>

-->

<link href="videojs7/video-js.css" rel="stylesheet">
<script src="videojs7/video.min.js"></script>

</head>
<body>
<!-- onload="myFunction()"> -->
<img src="UDE-logo-claim.svg" height="100"><img src="zim.jpg" height="100">
<h1>Livestream - Play Button klicken fC<r Start</h1>

<video id="myvideo1" controls preload="auto"
width="640"
height="360"
data-setup='{}'>

<!--   <source src="https://livestream.zim.uni-due.de/hls/novideo.m3u8" type="applicat
-->

</video>
<br>
<a href="playlist.html">zurC<ck zur Live-Stream Auswahl</a>

<script>
window.onload = myFunction();

function myFunction(){

function getUrlParam(parameter, defaultvalue){
var urlparameter = defaultvalue;
if(window.location.href.indexOf(parameter) > -1){
urlparameter = getUrlVars()[parameter];
}
return urlparameter;
}

function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,
vars[key] = value;
});
return vars;
}

var mystream = getUrlParam('s','Empty');
var video = document.getElementById('myvideo1');
/*alert(video);*/
var source = document.createElement('source');
var streambase = 'https://livestream.zim.uni-due.de/hls/';
var streamurl = streambase.concat(mystream);
source.setAttribute('src', streamurl);
source.setAttribute('type', "application/x-mpegURL");
video.appendChild(source);
/*let insertedNode = myvideo.insertBefore(insertedNode, source);*/
/*
video.play();
*/
}
</script>

</body>
</html>

Der Datei wird der Stream-URL durch die Playlist per Parameter übergeben und es wird ein bisschen mit Javascript gezaubert. Die Geschichte mit dem Parameter und der Playlist stammt von mir. Das kann man sicher mit einem beliebigen Framework viel eleganter machen, ist wollte allerdings eine rasche Lösung, da ich erwartet hatte, dass so etwas in Corona-Zeiten an der UDE benötigt wird.
Dieses ist meine fertige nginx.conf. Wer auf LDAP verzichten will, lässt die entsprechenden Konfigurationseintrage füe LDAP und das Modul einfach weg. Das Streaming geht auch ganz ohne LDAP. Das war nur ein Feature für die Uni Duisburg-Essen, um auch hochschulöffentliche Streams zu realisieren, die erst nach Anmeldung angeschaut werden können:

Da es Umbruchprobleme im Blog gibt, hier die Datei nginx_conf.txt auch zum Herunterladen.


#user  nobody;
worker_processes  10;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
worker_connections  2048;
}

http {
include       mime.types;
default_type  application/octet-stream;

#log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
#                  '$status $body_bytes_sent "$http_referer" '
#                  '"$http_user_agent" "$http_x_forwarded_for"';

#access_log  logs/access.log  main;

sendfile        on;
#tcp_nopush     on;

#keepalive_timeout  0;
keepalive_timeout  65;

#gzip  on;
#LDAP
ldap_server test1 {
url ldaps://ldap2.uni-duisburg-essen.de/ou=people,dc=uni-duisburg-essen,dc=de?uid?sub?(objectClass=inetOrgPerson);
binddn "cn=*******,ou=admin,dc=uni-duisburg-essen,dc=de";
binddn_passwd "********";
#group_attribute uniquemember;
group_attribute_is_dn on;
require valid_user;
}
#bis hier LDAP

server {
#listen 80;
listen 443 ssl;
#ssl on;
##  livestream.zim.uni-due.de-key.pem  livestream_cert.pem root@stream:/usr/local/nginx/certs
ssl_certificate /usr/local/nginx/certs/livestream_cert.pem;
ssl_certificate_key /usr/local/nginx/certs/livestream.zim.uni-due.de-key.pem;
server_name livestream.zim.uni-due.de;
#  server_name  localhost;
#LDAP
# auth_ldap "Anmeldung mit UDE-Unikennung/Passwort";
# auth_ldap_servers test1;
#LDAP bis hier
#charset koi8-r;

#access_log  logs/host.access.log  main;

location / {
root   html;
index  index.html index.htm;
}

#error_page  404              /404.html;

# redirect server error pages to the static page /50x.html
#
error_page   500 502 503 504  /50x.html;
location = /50x.html {
root   html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
#    proxy_pass   http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
#    root           html;
#    fastcgi_pass   127.0.0.1:9000;
#    fastcgi_index  index.php;
#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
#    include        fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
#    deny  all;
#}
# Andreas
location /hls/ {
root /tmp;
add_header Cache-Control no-cache;
}
# Andreas
location ~  /hls/U {
root /tmp;
add_header Cache-Control no-cache;
auth_ldap "Anmeldung mit UDE-Unikennung/Passwort";
auth_ldap_servers test1;

}

}

# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
#    listen       8000;
#    listen       somename:8080;
#    server_name  somename  alias  another.alias;

#    location / {
#        root   html;
#        index  index.html index.htm;
#    }
#}

# HTTPS server
#
#server {
#    listen       443 ssl;
#    server_name  localhost;

#    ssl_certificate      cert.pem;
#    ssl_certificate_key  cert.key;

#    ssl_session_cache    shared:SSL:1m;
#    ssl_session_timeout  5m;

#    ssl_ciphers  HIGH:!aNULL:!MD5;
#    ssl_prefer_server_ciphers  on;

#    location / {
#        root   html;
#        index  index.html index.htm;
#    }
#}

}

rtmp {
server {
listen 1935;
chunk_size 4096;

application live2 {
live on;
record off;
#Andreas
meta copy;
wait_key on;
wait_video on;
idle_streams off;
hls on;
hls_path /tmp/hls;

}
}
}

Aus der Nutzeranleitung, die ich für die UDE erstellt habe:

Livestreams bzw. Webcasts
Wählen Sie zur Nutzung von Livestreams bzw. Webcasts auf dem Server
jitsi.zim.uni-due.de  (nicht auf jitsi.uni-due.de) die Funktion „start live stream“ im Menue rechts unten (drei Punkte übereinander). Vergeben Sie dann im Dialog einen aussagekräftigen Namen für den Stream (ohne Leerzeichen). Der Stream wird nicht zu youtube gesendet, wie es im GUI angezeigt wird, sondern zum Server https://livestream.zim.uni-due.de . Wenn Sie ein großes „X“ dem Stream-Namen voranstellen, wird dieser nicht in der Streamübersicht angezeigt. Ihre Studierenden können den Stream dann unter der URL
https://livestream.zim.uni-due.de/stream5.html?s=X<ihrstreamname>.m3u8
betrachten. Sie müssen Ihren Studierenden den URL dann per Mail oder in Moodle mitteilen. Bitte keine Leer- oder Sonderzeichen im Stream-Namen verwenden.
Wenn Sie dem Stream-Namen mit einem großen „U“ beginnen, z.B. „Utest“, können nur NutzerInnnen der UDE erst nach Eingabe der Unikennung und dem Passwort den Stream ansehen.
Stoppen können Sie den Livetream ebenfalls über das Menue.
Der Livestream kann dann über die Seite livestream.zim.uni-due.de von vielen hundert Teilnehmern angeschaut werden,

Den Streamingserver livestream.zim.uni-due.de können Sie beispielsweise aber auch mit Hilfe von OBS-Studio produzierten Livestreams aus dem Uninetz auf den Port 1935 beschicken. Die beiden Optionen mit dem vorangestellten „X“ bzw. „U“ gelten dann ebenfalls.

Ich hoffe diese Zusammenstellung hilft dem einen oder anderen Admin weiter. Insbesondere für große Veranstaltungen kann das sehr hilfreich sein.