BigBlueButton v2.6 on Docker(v2.4からのアップデート、IPv6対応)

v2.4からv2.6へのアップデート

下記v2.4からv2.6.0.2へのアップデートメモです。

最新安定版のmainブランチ(サブモジュールオプション付)をダウンロード、bbb2602-dockerディレクトリへ移行

$ git clone [email protected]:bigbluebutton/docker.git -b main --recurse-submodules bbb2602-docker
$ cd bbb2602-docker

用意されたスクリプトから.envファイルを作成しますが、STUN/TURNサーバであるCOTURNを使用する場合にはTLS認証による証明書が必要となるため、予めLet’s Encryptによる証明書をCertbotコンテナ(スタンドアローンモード)により取得しておきます。

$ docker run -it --rm --name certbot -v "/etc/letsencrypt:/etc/letsencrypt" -p 80:80 certbot/certbot certonly --standalone -d www.example.com

用意されたスクリプトにより、Docker Composeファイルdocker-compose.ymlと環境変数設定ファイル.envが作成されます。

$ ./scripts/setup

Coturnのファイルパスには、上記Certbotコンテナから出力されたパスを指定して下さい。

COTURN_TLS_CERT_PATH=/etc/letsencrypt/live/www.example.com/fullchain.pem
COTURN_TLS_KEY_PATH=/etc/letsencrypt/live/www.example.com/privkey.pem

その他必要に応じて.envファイルを編集した場合は、その都度新たにdocke-compose.ymlを以下のコマンドで再構築して下さい。

$ ./scripts/generate-compose

システムの入口としてリバースプロキシサーバを設置します。設置すると言っても既存の nginx にリバースプロキシの設定ファイルを追加して読み込ませるだけです。また、HTTPS接続とするため、既に取得したSSL/TLS証明書をこのファイル内でも利用します。リバースプロキシの設定ファイルは以下に記載されています。

docker/docs/existing-web-server.md at develop · bigbluebutton/docker · GitHub

以下のDockerHubのページを参照して、上記の設定ファイルを、テンプレートファイル nginx_config_temp/reverse-proxy.conf.template として保存します。このテンプレートファイルがコンテナ起動時の設定ファイルとして /etc/nginx/conf.d ディレクトリにコピーされます。

https://hub.docker.com/_/nginx

nginx_config_temp/reverse-proxy.conf.template

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
map $remote_addr $endpoint_addr {
    "~:"    [::1];
    default    127.0.0.1;
}

server {
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;
  server_name ${NGINX_HOST};

  ssl_certificate /etc/letsencrypt/live/${NGINX_HOST}/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/${NGINX_HOST}/privkey.pem;
  
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
  ssl_prefer_server_ciphers on;
  # on the host machine, "$ sudo openssl dhparam -out ./letsencrypt/dhp-4096.pem 4096"
  ssl_dhparam /etc/letsencrypt/dhp-4096.pem;

  access_log  /var/log/nginx/bigbluebutton.access.log;
  error_log /var/log/nginx/bigbluebutton.error.log;

  location / {
    proxy_http_version 1.1;
    proxy_pass http://$endpoint_addr:48087;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_cache_bypass $http_upgrade;
  }
}

:bangbang: Note) ssl_ciphers, ssl_dhparam を追加。

Docker Composeファイルの nginx サービスの volumes: セクションには以下を追記します。

nginx:
      ......
      ......
    volumes:
      ......
      ......
      - /etc/letsencrypt:/etc/letsencrypt
      - ./nginx_config_temp:/etc/nginx/templates
    environment:
      # added below for variables in *.conf.template
      - NGINX_HOST=www.example.com

Network Configuration

Services as configured.

Service Network IP address Other Option
bbb-web bbb-net 10.7.7.2
bbb-pads bbb-net 10.7.7.18
html5-backend-{{$i}} bbb-net 10.7.7.{{add 100 $i}} Port {{ add 4000 $i }}
html5-frontend-{{$i}} bbb-net 10.7.7.{{add 200 $i}} Port {{ add 4100 $i }}
freeswitch network_mode: host
nginx network_mode: host extra_hosts:
- “host.docker.internal:10.7.7.1”
- “core:10.7.7.2”
- “etherpad:10.7.7.4”
- “webrtc-sfu:10.7.7.10”
- “html5:10.7.7.11”
etherpad bbb-net 10.7.7.4
redis bbb-net 10.7.7.5
mongodb bbb-net 10.7.7.6
kurento network-mode: host
webrtc-sfu bbb-net network_mode: host
fsesl-akka bbb-net 10.7.7.14
apps-akka bbb-net 10.7.7.15
libreoffice bbb-net 10.7.7.7
periodic bbb-net 10.7.7.12
recordings bbb-net 10.7.7.16
webhooks bbb-net 10.7.7.17
https_proxy bbb-net network_mode: host
coturn network_mode: host
greenlight ports: 10.7.7.1:5000:80
prometheus bbb-net 10.7.7.33
networks:  
  bbb-net:  
    ipam:  
      driver: default  
      config:  
        - subnet: "10.7.7.0/24"

ネットワークとの整合性に重要な設定ファイル

Docker Compose

  • docker-compose.yml
  • .env

WebRTC-SFU(Mediasoup)

  • mod/webrtc-sfu/bbb-webrtc-sfu/config/default.example.yml

Nginx

  • mod/nginx/bigbluebutton

FreeSwitch(Sofia Configuration files)

  • mod/freeswitch/conf/vars.xml.tmpl
  • mod/freeswitch/conf/sip_profiles/external.xml
  • mod/freeswitch/conf/sip_profiles/external-ipv6.xml
  • mod/freeswitch/conf/autoload_configs/event_socket.conf.xml
  • mod/freeswitch/conf/autoload_configs/acl.conf.xml

Coturn

  • mod/coturn/turnserver.conf

Greenlight v3

Greenlight SMTP設定(Gmail)

.envファイル内でGmail SMTPサーバの設定をします。
SMTPサーバの設定をしないとメール認証によるユーザ登録が出来ません。

.env

# ====================================
# GREENLIGHT CONFIGURATION
# ====================================

### SMTP CONFIGURATION
# Emails are required for the basic features of Greenlight to function.
# Please refer to your SMTP provider to get the values for the variables below
[email protected]
SMTP_SENDER_NAME=FICUSONLINE
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_DOMAIN=gmail.com
SMTP_USERNAME=google_account_name
SMTP_PASSWORD=password
SMTP_AUTH=plain
SMTP_STARTTLS_AUTO=true
SMTP_SSL_VERIFY=false

Note) サードパーティアプリへのパスワード付与の項目は、以下のメニュー階層に変更されたようです。アカウント画面から
Security → 2-Step-Verification → App password
にアクセスして専用パスワードを取得して下さい。


  • bbb-web – BigBlueButton APIを実装し、プレゼンテーション用のドキュメントの変換を行います。

  • akka-bbb-apps – サーバーサイドアプリケーションであり、サーバー上でのミーティングの状態を処理

  • bbb-html5 – ブラウザに読み込まれるHTML5クライアントです。MongoDBとReact.jsを活用したクライアントサーバーサイドのMeteorアプリケーション

  • bbb-learning-dashboard – モデレーターに利用可能なライブダッシュボードであり、インストラクターに役立つユーザーのアクティビティ情報を表示

  • bbb-fsesl-akka – FreeSWITCHにコマンドを送信するためのコンポーネント

  • bbb-playback-presentation – プレゼンテーションのレコードと再生スクリプトであり、プレゼンテーションのレイアウトを作成

  • bbb-export-annotations – ブレイクアウトコンテンツのキャプチャと注釈付きプレゼンテーションのダウンロードを処理

  • bbb-webrtc-sfu – クライアントからの受信リクエストをKurentoにブリッジするサーバー

  • kurento-media-server – ウェブカムとスクリーン共有のビデオの送受信と録画のためのWebRTCメディアサーバー

  • bbb-freeswitch-core – 音声の送受信と録音のためのWebRTCメディアサーバー

トラブルシュート


Configure mediasoup to use IPv6

mediasoup (bbb-webrtc-sfu) does not come with a IPv6 enabled by default when installed either via packages or bbb-install.

To configure IPv6, bbb-webrtc-sfu’s override configuration file (located in /etc/bigbluebutton/bbb-webrtc-sfu/production.yml) should be used.

See Updating mediasoup for instructions and examples on how to do so.

Notice the listenIps key is an array. If you need mediasoup to work with IPv6 as well, don’t forget to add another entry to that array with it. For example: in a BigBlueButton server with a public IPv4 192.0.2.0 and IPv6 2001:db8::, the configuration should be of the following format (YAML syntax, /etc/bigbluebutton/bbb-webrtc-sfu/production.yml):

mediasoup:
  webrtc:
    listenIps:
      - ip: 0.0.0.0
        announcedIp: 192.0.2.0
      - ip: 2001:db8::

Configure BigBluebutton/FreeSWITCH to support IPV6​

The HTML5 client now enables users on mobile devices to connect to a BigBlueButton server. However, on some cellular networks iOS devices only receive an IPV6 address.

To enable BigBlueButton (FreeSWITCH) to accept incoming web socket connections on IPV6, the BigBlueButton server must have an IPV6 address. You also need to make the following changes to the server.

First, create the file /etc/nginx/conf.d/bigbluebutton_sip_addr_map.conf with this content:

map $remote_addr $freeswitch_addr {
    "~:"    [2001:db8::1];
    default    192.0.2.1;
}

Docker Composeで運用する場合には、Docker NetworkにIPv6アドレスも付与し、そのアドレスを指定すること。

map $remote_addr $freeswitch_addr {
    "~:"    [fdxx:xx:xx::1];
    default    10.7.7.1;
}

replacing the ip addresses 192.0.2.1 with the system’s external IPV4 addresses, and replace 2001:db8::1 with the system’s external IPV6 address. Next, edit the file /etc/bigbluebutton/nginx/sip.nginx to have the following:

proxy_pass https://$freeswitch_addr:7443;

Next, ensure all of the following params are present in freeswitch’s sip_profiles/external-ipv6.xml:

  • ws-binding
  • wss-binding
  • rtcp-audio-interval-msec
  • rtcp-video-interval-msec
  • dtmf-type
  • liberal-dtmf
  • enable-3pcc

If any are missing, copy them from sip_profiles/external.xml, then restart BigBlueButton (sudo bbb-conf --restart).


mediasoup is the default in 2.5. Why is Kurento still around?

Because Kurento is still used for stream recording. It should be removed as a dependency as soon as this issue is addressed.

WebRTCではデフォルトでMediasoupが使用されているためKurentoの環境変数の設定は必要ありません。Kurentoは現時点でストリーミングのレコーディング用途でのみ使用されています。

:bangbang: Note) KurentoのDocker Composeファイルで設定できる環境変数は以下参照のこと。

Configuration — Kurento 7.0 documentation


WebRTC errors (1001, 1002,…)

  • 1001: WebSocket disconnected - The WebSocket had connected successfully and has now disconnected. Possible Causes:
    • Loss of internet connection
    • Nginx restarting can cause this
  • 1002: Could not make a WebSocket connection - The initial WebSocket connection was unsuccessful. Possible Causes:
    • Firewall blocking ws protocol
    • Server is down or improperly configured
    • See potential solution here.
  • 1003: Browser version not supported - Browser doesn’t implement the necessary WebRTC API methods. Possible Causes:
    • Out of date browser
  • 1004: Failure on call - The call was attempted, but failed. Possible Causes:
    • For a full list of causes refer here
    • There are 24 different causes so I don’t really want to list all of them
    • Solution for this issue outlined here.
  • 1005: Call ended unexpectedly - The call was successful, but ended without user requesting to end the session. Possible Causes:
    • Unknown
  • 1006: Call timed out - The library took too long to try and connect the call. Possible Causes:
    • Previously caused by Firefox 33-beta on Mac. We’ve been unable to reproduce since release of FireFox 34
  • 1007: ICE negotiation failed - The browser and FreeSWITCH try to negotiate ports to use to stream the media and that negotiation failed. Possible Causes:
    • NAT is blocking the connection
    • Firewall is blocking the UDP connection/ports
  • 1008: Call transfer failed - A timeout while waiting for FreeSWITCH to transfer from the echo test to the real conference. This might be caused by a misconfiguration in FreeSWITCH, or there might be a media error and the DTMF command to transfer didn’t go through (In this case, the voice in the echo test probably didn’t work either.)
  • 1009: Could not fetch STUN/TURN server information - This indicates either a BigBlueButton bug (or you’re using an unsupported new client/old server combination), but could also happen due to a network interruption.
  • 1010: ICE negotiation timeout - After the call is accepted the client’s browser and the server try and negotiate a path for the audio data. In some network setups this negotiation takes an abnormally long time to fail and this timeout is set to avoid the client getting stuck.
  • 1020: Media cloud could not reach the server - See how to solve this here.

I’m having troubles seeing webcams or screen sharing in Firefox

Firefoxでの1020エラーに対する対応策だが効果なし。Chrome、Braveでは問題なし。アンドロイド版Braveでは 設定->サイトの設定->マイクとカメラの使用許可 を与えること。

mod/html5/bbb-html5.yml

public:
  media:
    forceRelayOnFirefox: true

Let’t EncryptによるSSL/TLS証明書の利用について

Certbotで取得したLet’s EncryptによるTLS証明書は以下のケースで利用します。

  • Coturn:TLS listening ポート443
  • Nginx:HTTPS ポート443
  • FreeSwitch:SIPS ポート5081
  • FreeSwitch:WSS ポート7443

CoturnとNginxについては、Certbotで取得したファイルをそのまま利用できますが、FreeSwitchについては、取得したファイル群から新たなファイルを作成する必要があります。

Let's Encryptからの取得ファイル

# ls /etc/letsencrypt/live/www.example.com
cert.pem  chain.pem  fullchain.pem  privkey.pem

wss.pemの作成

# cd /etc/letsencrypt/live/www.example.com
# cat cert.pem privkey.pem fullchain.pem > wss.pem

agent.pemの作成

# cat cert.pem privkey.pem > agent.pem

Note) Let’s Encryptの認証ファイルから作成した wss.pem は、SIPのTLS認証にも利用できますが、別ファイル名で指定する必要があるようです。wss.pem からのリンクファイルを agent.pem, tls.pem, dtls-srtp.pem として作成して下さい。


FreeSwitchセキュアウェブソケットWSS、SIPS対応

下記Docker Composeファイルの FreeSwitch サービスの volumes: セクションに wss に必要なTLS/SSL証明書ディレクトリを追加

freeswitch:
      ......
      ......
    volumes:
      - ./conf/sip_profiles:/etc/freeswitch/sip_profiles/external
      - ./conf/dialplan_public:/etc/freeswitch/dialplan/public_docker
      - vol-freeswitch:/var/freeswitch/meetings
      ## added for wss: secure web socket. see the below "nginx" service "volumes"
      - /etc/letsencrypt/live/${DOMAIN}:/etc/freeswitch/tls
      ......
      ......

mod/freeswitch/conf/sip_profiles/external.xml

.....
.....
    <!-- Location of the agent.pem and cafile.pem ssl certificates (needed for TLS server) -->
    <param name="tls-cert-dir" value="/etc/freeswitch/tls"/>
.....
.....

mod/freeswitch/conf/sip_profiles/external-ipv6.xml

.....
.....
    <!-- Location of the agent.pem and cafile.pem ssl certificates (needed for TLS server) -->
    <param name="tls-cert-dir" value="/etc/freeswitch/tls"/>
.....
.....    

mod/freeswitch/conf/vars.xml.tmpl

  <!-- external_rtp_ip
       Can be an one of:
           ip address: "12.34.56.78"
           a stun server lookup: "stun:stun.server.com"
           a DNS name: "host:host.server.com"
       where fs.mydomain.com is a DNS A record-useful when fs is on
       a dynamic IP address, and uses a dynamic DNS updater.
       If unspecified, the bind_server_ip value is used.
       Used by: sofia.conf.xml dingaling.conf.xml
  -->
  <X-PRE-PROCESS cmd="set" data="external_rtp_ip={{ .Env.EXTERNAL_IPv4 }}"/>

  <!-- external_sip_ip
      Used as the public IP address for SDP.
       Can be an one of:
           ip address: "12.34.56.78"
           a stun server lookup: "stun:stun.server.com"
           a DNS name: "host:host.server.com"
       where fs.mydomain.com is a DNS A record-useful when fs is on
       a dynamic IP address, and uses a dynamic DNS updater.
       If unspecified, the bind_server_ip value is used.
       Used by: sofia.conf.xml dingaling.conf.xml
  -->
  <X-PRE-PROCESS cmd="set" data="external_sip_ip={{ .Env.EXTERNAL_IPv4 }}"/>
.....
.....
  <X-PRE-PROCESS cmd="set" data="external_ssl_enable=true"/>
.....
.....

FreeSwitchステータス確認

FreeSwitchのコマンドラインツール fs_cli を利用してFreeSwitchの設定事項などを確認。以下freeswitchのコンソールで各イベントも表示されます。

$ docker exec -ti bbb-freeswitch bash
# /opt/freeswitch/bin/fs_cli -H 10.7.7.1 -P 8021 -p "FSESL_PASSWORD in .env file"
freeswitch >

イベントソケットの設定ファイル
mod/freeswitch/conf/autoload_configs/event_socket.conf.xml
によって、アクセスできるデフォルトのIPアドレスとポートなどを指定できます。

<configuration name="event_socket.conf" description="Socket Client">
  <settings>
    <param name="nat-map" value="false"/>
    <param name="listen-ip" value="$${local_ip_v4}"/>
    <param name="listen-port" value="8021"/>
    <param name="password" value="$${esl_password}"/>
    <param name="apply-inbound-acl" value="loopback.custom"/>
    <!--<param name="stop-on-bind-error" value="true"/>-->
  </settings>
</configuration>

また mod/freeswitch/conf/autoload_configs/acl.conf.xml によりアクセス可能なIPを細かく指定できます。


mod/freeswitch/conf/sip_profiles で設定した外部からのアクセス設定の確認

外部IPv4のステータス確認

freeswitch > sofia status profile external
=================================================================================================
Name             	external
Domain Name      	N/A
Auto-NAT         	false
DBName           	sqlite://memory://file:external?mode=memory&cache=shared
Pres Hosts       	
Dialplan         	XML
Context          	public
Challenge Realm  	auto_to
RTP-IP           	xx.xx.xx.xx
Ext-RTP-IP       	xx.xx.xx.xx
SIP-IP           	10.7.7.1
Ext-SIP-IP       	xx.xx.xx.xx
URL              	sip:[email protected]:5060
BIND-URL         	sip:[email protected]:5060;maddr=10.7.7.1;transport=udp,tcp
WS-BIND-URL     	sip:[email protected]:5066;transport=ws
WSS-BIND-URL     	sips:[email protected]:7443;transport=wss
HOLD-MUSIC       	local_stream://moh
OUTBOUND-PROXY   	N/A
CODECS IN        	OPUS,speex@16000h@20i,speex@8000h@20i,G722,PCMU,PCMA
CODECS OUT       	OPUS,speex@16000h@20i,G722,PCMU,PCMA
TEL-EVENT        	101
DTMF-MODE        	info
CNG              	13
SESSION-TO       	0
MAX-DIALOG       	0
MAX-RECV-RPS     	1000
NOMEDIA          	false
LATE-NEG         	true
PROXY-MEDIA      	false
AGGRESSIVENAT    	false
CALLS-IN         	0
FAILED-CALLS-IN  	0
CALLS-OUT        	0
FAILED-CALLS-OUT 	0
REGISTRATIONS    	0

外部IPv6のステータス確認

freeswitch > sofia status profile external-ipv6

内部のステータス確認は、上記の externalをinternal に置換えて下さい。

sofia コマンドについては以下も参照のこと(xmlファイル変更後の再読込)。


ログをファイル保存するモジュールmod_logfileは以下のautoload_configs/modules.conf.xmlで読み込む設定になっていないため追加します。

<configuration name="modules.conf" description="Modules">
  <modules>
    <!-- Loggers (I'd load these first) -->
    <load module="mod_console"/>
    
    <!-- save Log -->
    <load module="mod_logfile"/>

    <!-- Event Handlers -->
    <load module="mod_event_socket"/>

    <!-- Endpoints -->
    <load module="mod_sofia"/>

    <!-- Applications -->
    <load module="mod_commands"/>
    <load module="mod_conference"/>
    <load module="mod_dptools"/>

    <!-- Dialplan Interfaces -->
    <load module="mod_dialplan_xml"/>

    <!-- Codec Interfaces -->
    <load module="mod_spandsp"/>
    <load module="mod_opus"/>
    <load module="mod_opusfile"/>

    <!-- File Format Interfaces -->
    <load module="mod_sndfile"/>
    <load module="mod_native_file"/>

  </modules>
</configuration>

:bangbang: 重要) BBBを構成する各サービス(コンテナ)は、ホストネームを参照してアクセスするため、ホストマシンの /etc/hostsホストネームとコンテナネットワークのIPv4, IPv6のゲートウェイアドレスを追加すること。

/etc/hosts

.....
.....
10.7.7.1 www.example.com
.....
fdxx.xx.::1 www.example.com

Testing the firewall

netcat によるオープンポートの確認( IPv6 の場合は -6 を付与)

BigBlueButtonがインストールされているホストマシン側(BBBは停止すること

$ sudo netcat -6 -l 443

他の端末からBBBの外部アドレスを指定してnetcatを実行し任意のテキスト (test) を入力

上記のコンソールに test と表示されます。

$ sudo netcat -6 2001.xx.xx::1 443
test

:bangbang: Dockerネットワークに割当てたIPv4,IPv6アドレス範囲をファイヤーウォールルールから除外

$ sudo ufw allow from 10.7.7.0/24
$ sudo ufw allow from fdxx:xxxx:xxxx:xxxx::/64

カスタマイズ

Docker Compose内の各サービスのカスタマイズについては下記参照のこと。

UDPポート変更

Change UDP ports

By default, BigBlueButton uses the UDP ports 16384-32768 which are used by FreeSWITCH, mediasoup and Kurento to send real-time packets (RTP).

FreeSWITCH uses the range 16384 - 24576, which is defined in /opt/freeswitch/etc/freeswitch/autoload_configs/switch.conf.xml

    <!-- RTP port range -->
    <param name="rtp-start-port" value="16384"/>
    <param name="rtp-end-port" value="24576"/>

mediasoup also uses 24577 - 32768 by default (defined in /usr/local/bigbluebutton/bbb-webrtc-sfu/config/default.yml). If it needs to be changed, bbb-webrtc-sfu’s override configuration file (located in /etc/bigbluebutton/bbb-webrtc-sfu/production.yml) should be used. For example, setting the range to 50000 - 51999 should be of the following format (YAML syntax, /etc/bigbluebutton/bbb-webrtc-sfu/production.yml):

mediasoup:
  worker:
    rtcMinPort: 50000
    rtcMaxPort: 51999

coturn
mod/coturn/turnserver.conf

# Lower and upper bounds of the UDP relay endpoints:
# (default values are 49152 and 65535)
#
min-port=60000
max-port=60500

レコーディングフォーマットの変更

Recording

BBBデフォルトのビデオレコーディングフォーマットは webm のため iOS では非対応のようですが、webrtc-sfcの環境変数ファイル mod/webrtc-sfu/bbb-webrtc-sfu/config/custom-environment-variables.yml で定義されているレコーディングフォーマット RECORDING_FORMATiOSにも対応している mp4 に変更することが出来ます。

この場合、レコーダである ffmpeg のアウトプットオプション FFMPEG_OUTPUT_OPTS も変更します。

以下Docker Composeファイルで RECORDING_FORMATFFMPEG_OUTPUT_OPTS を指定します。

エラー)変更してもwebmフォーマットで出力。原因調査中。

recordingイメージ作成時に以下の設定ファイルを上書きすることも必要(結果については未確認)。

docker-compose.yml

webrtc-sfu:
    build: 
      context: mod/webrtc-sfu
      args:
        BBB_BUILD_TAG: v2022-12-29-grails-524
    image: alangecker/bbb-docker-webrtc-sfu:v2.9.10
    restart: unless-stopped
    depends_on:
      - redis
      - freeswitch
    environment:
      CLIENT_HOST: 10.7.7.1
      REDIS_HOST: 10.7.7.5
      FREESWITCH_IP: 10.7.7.1
      FREESWITCH_SIP_IP: ${EXTERNAL_IPv6}
      ESL_IP: 10.7.7.1
      ESL_PASSWORD: ${FSESL_PASSWORD:-ClueCon}
      #Recording format webm(default) or mp4(iOS)
      RECORDING_FORMAT: mp4
      FFMPEG_OUTPUT_OPTS: '["-f mp4", "-flags +global_header"]'

レコーディング時のCPU負荷軽減のため、WEBカメラ共有画面は記録されないようです。

:bangbang: —> 訂正:デフォルトで記録されます。

レコーダーは Kurento から bbb-webrtc-recorder へ移行するようです。

webrtc-sfu(mediasoup, kurento)

webrtc関連の設定

mod/webrtc-sfu/bbb-webrtc-sfu/config/default.example.yml

node-config Environment Variables


ドキュメント(mediasoup)

mod/webrtc-sfu/bbb-webrtc-sfu/docs/mediasoup.md

1. If your server is behind NAT or the public IPs are in a network interface other than the default (**otherwise skip to step 2**):
   * Let $SERVER_IPv4 be your server's **public IPv4**
   * `yq w -i /etc/bigbluebutton/bbb-webrtc-sfu/production.yml mediasoup.webrtc.listenIps[0].ip "0.0.0.0"`
   * `yq w -i /etc/bigbluebutton/bbb-webrtc-sfu/production.yml mediasoup.webrtc.listenIps[0].announcedIp $SERVER_IPv4`
   * `yq w -i /etc/bigbluebutton/bbb-webrtc-sfu/production.yml mediasoup.plainRtp.listenIp.ip "0.0.0.0"`
   * `yq w -i /etc/bigbluebutton/bbb-webrtc-sfu/production.yml mediasoup.plainRtp.listenIp.announcedIp $SERVER_IPv4`
2. If you wish to enable IPv6 in mediasoup (**otherwise you're done**):
   * Let $SERVER_IPv6 be your server's IPv6
   * `yq w -i /etc/bigbluebutton/bbb-webrtc-sfu/production.yml mediasoup.webrtc.listenIps[1].ip $SERVER_IPv6`

mediasoup:GitHub

WebRtcServer

A WebRTC server brings the ability to listen on a single UDP/TCP port to WebRtcTransports. Instead of passing listenIps to router.createWebRtcTransport() pass webRtcServer with an instance of a WebRtcServer so the new WebRTC transport will not listen on its own IP:port(s) but will have its network traffic handled by the WebRTC server instead.

  • A WebRTC server exists within the context of a Worker, meaning that if your app launches N workers it also needs to create N WebRTC servers listening on different ports (to not collide).
  • The WebRTC transport implementation of mediasoup is ICE Lite, meaning that it does not initiate ICE connections but expects ICE Binding Requests from endpoints.

Dictionaries

WebRtcServerListenInfo

Field Type Description Required Default
protocol String Protocol (“udp” / “tcp”). Yes
ip String Listening IPv4 or IPv6. Yes
announcedIp String Announced IPv4 or IPv6 (useful when running mediasoup behind NAT with private IP). No
port Number Listening port. No If not given, a random available port from the Worker’s port range will be used.

WebRtcServerOptions

Field Type Description Required Default
listenInfos Array<WebRtcServerListenInfo> Listening protocol, IP and port objects in order of preference (first one is the preferred one). Yes
appData AppData Custom application data. No { }
  • The IP in each entry in listenInfos must be a bindable IP in the host.
  • If you use “0.0.0.0” or “::” in an entry in listenInfos, then you need to also provide announcedIp in the corresponding entry in listenInfos.

動作確認

AZURE, AWS, GCPなどのクラウドサーバにインストールすることで、個人でのオンライン学習塾や教育機関でのオンラインクラスの開催も十分可能です。

モバイルユーザとnftablesのドキュメントを共有して会議(背景オプション使用)

デスクトップ画面

モバイル画面

レコーディング

recordingsコンテナ内でbbb-recordコマンドが使用できます。

$ docker compose exec recordings bash

# bbb-record -h
BigBlueButton Recording Diagnostic Utility (BigBlueButton Version v2.6.0)

   bbb-record [options]

Reporting:
   --list                             List all recordings
   --list-recent                      List recent recordings
   --list-recent --withDesc           List recent recordings and show their description
   --list-workflows                   List available recording workflows

Monitoring:
   --watch                            Watch processing of recordings
   --watch --withDesc                 Watch processing of recordings and show their description

Administration:
   --rebuild <internal meetingID>     rebuild the output for the given internal meetingID
   --rebuildall                       rebuild every recording
   --delete <internal meetingID>      delete one meeting and recording
   --deleteall                        delete all meetings and recordings
   --debug                            check for recording errors
   --check                            check for configuration errors
   --enable <workflow>                enable a recording workflow
   --disable <workflow>               disable a recording workflow
   --tointernal <external meetingId>  get the internal meeting ids for the given external meetingId
   --toexternal <internal meetingId>  get the external meeting id for the given internal meetingId
   --republish <internal meetingID>   republish the recording for meetingID. (Only for Matterhorn Integration)

パブリッシュデータは/var/bigbluebutton/publishedに保存されます。

# tree /var/bigbluebutton
/var/bigbluebutton
├── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769
│   └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769
│       └── 90de8952b35eb08aab73139a5a031a63cf363538-1684978837234
│           ├── 90de8952b35eb08aab73139a5a031a63cf363538-1684978837234.pdf
│           ├── svgs
│           │   ├── slide10.svg
│           │   ├── slide11.svg
│           │   ├── slide12.svg
│           │   ├── slide13.svg
│           │   ├── slide14.svg
│           │   ├── slide15.svg
│           │   ├── slide1.svg
│           │   ├── slide2.svg
│           │   ├── slide3.svg
│           │   ├── slide4.svg
│           │   ├── slide5.svg
│           │   ├── slide6.svg
│           │   ├── slide7.svg
│           │   ├── slide8.svg
│           │   └── slide9.svg
│           ├── textfiles
│           │   ├── slide-10.txt
│           │   ├── slide-11.txt
│           │   ├── slide-12.txt
│           │   ├── slide-13.txt
│           │   ├── slide-14.txt
│           │   ├── slide-15.txt
│           │   ├── slide-1.txt
│           │   ├── slide-2.txt
│           │   ├── slide-3.txt
│           │   ├── slide-4.txt
│           │   ├── slide-5.txt
│           │   ├── slide-6.txt
│           │   ├── slide-7.txt
│           │   ├── slide-8.txt
│           │   └── slide-9.txt
│           └── thumbnails
│               ├── thumb-10.png
│               ├── thumb-11.png
│               ├── thumb-12.png
│               ├── thumb-13.png
│               ├── thumb-14.png
│               ├── thumb-15.png
│               ├── thumb-1.png
│               ├── thumb-2.png
│               ├── thumb-3.png
│               ├── thumb-4.png
│               ├── thumb-5.png
│               ├── thumb-6.png
│               ├── thumb-7.png
│               ├── thumb-8.png
│               └── thumb-9.png
├── captions
│   ├── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769
│   │   └── captions.json
│   └── inbox
├── deleted
│   └── presentation
├── learning-dashboard
│   ├── 45394bf3ecfa715d80ef4ca623b848b753fd8270-1684196396384
│   │   └── 9odn8jky5v8f
│   │       └── learning_dashboard_data.json
│   ├── 45394bf3ecfa715d80ef4ca623b848b753fd8270-1684202875900
│   │   └── 8tpp3fosaq73
│   │       └── learning_dashboard_data.json
│   ├── 45394bf3ecfa715d80ef4ca623b848b753fd8270-1684238098259
│   │   └── myicuczg3ara
│   │       └── learning_dashboard_data.json
│   ├── 45394bf3ecfa715d80ef4ca623b848b753fd8270-1684392902836
│   │   └── akidz4b0ojet
│   │       └── learning_dashboard_data.json
│   ├── 45394bf3ecfa715d80ef4ca623b848b753fd8270-1684456686270
│   │   └── kkn20rwahv0o
│   │       └── learning_dashboard_data.json
│   └── 45394bf3ecfa715d80ef4ca623b848b753fd8270-1684475929539
│       └── rzhn16zg53fk
│           └── learning_dashboard_data.json
├── published
│   ├── notes
│   └── presentation
│       └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769
│           ├── captions.json
│           ├── cursor.xml
│           ├── deskshare
│           │   └── deskshare.webm
│           ├── deskshare.xml
│           ├── metadata.xml
│           ├── panzooms.xml
│           ├── presentation
│           │   ├── 90de8952b35eb08aab73139a5a031a63cf363538-1684978837234
│           │   │   ├── svgs
│           │   │   │   ├── slide10.svg
│           │   │   │   ├── slide11.svg
│           │   │   │   ├── slide12.svg
│           │   │   │   ├── slide13.svg
│           │   │   │   ├── slide14.svg
│           │   │   │   ├── slide15.svg
│           │   │   │   ├── slide1.svg
│           │   │   │   ├── slide2.svg
│           │   │   │   ├── slide3.svg
│           │   │   │   ├── slide4.svg
│           │   │   │   ├── slide5.svg
│           │   │   │   ├── slide6.svg
│           │   │   │   ├── slide7.svg
│           │   │   │   ├── slide8.svg
│           │   │   │   └── slide9.svg
│           │   │   ├── textfiles
│           │   │   │   ├── slide-10.txt
│           │   │   │   ├── slide-11.txt
│           │   │   │   ├── slide-12.txt
│           │   │   │   ├── slide-13.txt
│           │   │   │   ├── slide-14.txt
│           │   │   │   ├── slide-15.txt
│           │   │   │   ├── slide-1.txt
│           │   │   │   ├── slide-2.txt
│           │   │   │   ├── slide-3.txt
│           │   │   │   ├── slide-4.txt
│           │   │   │   ├── slide-5.txt
│           │   │   │   ├── slide-6.txt
│           │   │   │   ├── slide-7.txt
│           │   │   │   ├── slide-8.txt
│           │   │   │   └── slide-9.txt
│           │   │   └── thumbnails
│           │   │       ├── thumb-10.png
│           │   │       ├── thumb-11.png
│           │   │       ├── thumb-12.png
│           │   │       ├── thumb-13.png
│           │   │       ├── thumb-14.png
│           │   │       ├── thumb-15.png
│           │   │       ├── thumb-1.png
│           │   │       ├── thumb-2.png
│           │   │       ├── thumb-3.png
│           │   │       ├── thumb-4.png
│           │   │       ├── thumb-5.png
│           │   │       ├── thumb-6.png
│           │   │       ├── thumb-7.png
│           │   │       ├── thumb-8.png
│           │   │       └── thumb-9.png
│           │   ├── deskshare.png
│           │   └── logo.png
│           ├── presentation_text.json
│           ├── shapes.svg
│           ├── slides_new.xml
│           ├── tldraw.json
│           └── video
│               └── webcams.webm
├── recording
│   ├── process
│   │   └── presentation
│   ├── publish
│   │   └── presentation
│   ├── raw
│   │   └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769
│   │       ├── audio
│   │       │   └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769-473130975.opus
│   │       ├── deskshare
│   │       │   └── 95288-1684978941445.webm
│   │       ├── events.xml
│   │       ├── notes
│   │       ├── presentation
│   │       │   └── 90de8952b35eb08aab73139a5a031a63cf363538-1684978837234
│   │       │       ├── 90de8952b35eb08aab73139a5a031a63cf363538-1684978837234.pdf
│   │       │       ├── svgs
│   │       │       │   ├── slide10.svg
│   │       │       │   ├── slide11.svg
│   │       │       │   ├── slide12.svg
│   │       │       │   ├── slide13.svg
│   │       │       │   ├── slide14.svg
│   │       │       │   ├── slide15.svg
│   │       │       │   ├── slide1.svg
│   │       │       │   ├── slide2.svg
│   │       │       │   ├── slide3.svg
│   │       │       │   ├── slide4.svg
│   │       │       │   ├── slide5.svg
│   │       │       │   ├── slide6.svg
│   │       │       │   ├── slide7.svg
│   │       │       │   ├── slide8.svg
│   │       │       │   └── slide9.svg
│   │       │       ├── textfiles
│   │       │       │   ├── slide-10.txt
│   │       │       │   ├── slide-11.txt
│   │       │       │   ├── slide-12.txt
│   │       │       │   ├── slide-13.txt
│   │       │       │   ├── slide-14.txt
│   │       │       │   ├── slide-15.txt
│   │       │       │   ├── slide-1.txt
│   │       │       │   ├── slide-2.txt
│   │       │       │   ├── slide-3.txt
│   │       │       │   ├── slide-4.txt
│   │       │       │   ├── slide-5.txt
│   │       │       │   ├── slide-6.txt
│   │       │       │   ├── slide-7.txt
│   │       │       │   ├── slide-8.txt
│   │       │       │   └── slide-9.txt
│   │       │       └── thumbnails
│   │       │           ├── thumb-10.png
│   │       │           ├── thumb-11.png
│   │       │           ├── thumb-12.png
│   │       │           ├── thumb-13.png
│   │       │           ├── thumb-14.png
│   │       │           ├── thumb-15.png
│   │       │           ├── thumb-1.png
│   │       │           ├── thumb-2.png
│   │       │           ├── thumb-3.png
│   │       │           ├── thumb-4.png
│   │       │           ├── thumb-5.png
│   │       │           ├── thumb-6.png
│   │       │           ├── thumb-7.png
│   │       │           ├── thumb-8.png
│   │       │           └── thumb-9.png
│   │       └── video
│   │           └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769
│   └── status
│       ├── archived
│       │   └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769.done
│       ├── ended
│       ├── processed
│       │   └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769-presentation.done
│       ├── published
│       │   └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769-presentation.done
│       ├── recorded
│       └── sanity
│           └── 664d21c0511590aac2e23da4a5e4dc4d99de8147-1684978836769.done
└── unpublished

レコーディングディレクトリストラクチャ

mod/bbb-web/entrypoint.sh

# create recording directory structure if it doesn't exist yet
mkdir -p /var/bigbluebutton/recording/raw
mkdir -p /var/bigbluebutton/recording/process
mkdir -p /var/bigbluebutton/recording/publish
mkdir -p /var/bigbluebutton/recording/status/recorded
mkdir -p /var/bigbluebutton/recording/status/archived
mkdir -p /var/bigbluebutton/recording/status/processed
mkdir -p /var/bigbluebutton/recording/status/sanity
mkdir -p /var/bigbluebutton/recording/status/ended
mkdir -p /var/bigbluebutton/recording/status/published
mkdir -p /var/bigbluebutton/captions/inbox
mkdir -p /var/bigbluebutton/published
mkdir -p /var/bigbluebutton/published/notes
mkdir -p /var/bigbluebutton/deleted
mkdir -p /var/bigbluebutton/unpublished
chown -R bigbluebutton:bigbluebutton /var/bigbluebutton

動作確認エラー


Recording processing status and deskshare recording issue

解決策

会議終了後のレコーディングのリストがアップデートされない

mod/recordings/bbb-web.properties

bigbluebutton.web.serverURL=https://{{ .Env.DOMAIN }}
securitySalt={{ .Env.SHARED_SECRET }}

docker-compose.ymlrecordingサービスのenvironmentに変数追加

environment:
      DOMAIN: ${DOMAIN}
      SHARED_SECRET: ${SHARED_SECRET}

deskshareのレコーディングエラー

画面共有が記録されない。ブランク画面で音声は記録。

slides, chat, audio, whiteboard events, shared notes :OK
deskshare, webcam : NG


kurento/kurento-media-server:7.0.1 へバージョンアップ

バージョンアップに伴い下記修正(mod/webrtc-sfu/bbb-webrtc-sfu/lib/ 内の該当する.jsファイル。バージョンアップ以前から存在していたバグ)

Media Events(6.18.0 (September 2022) — Kurento 6.18.0 documentation)

A series of deprecations and renamings that normalize all events into the same naming convention.

  • Old: MediaFlowOutStateChange event
    New: MediaFlowOutStateChanged event
  • Old: MediaFlowInStateChange event
    New: MediaFlowInStateChanged event
  • Old: MediaTranscodingStateChange event
    New: MediaTranscodingStateChanged event

ICE Events(6.18.0 (September 2022) — Kurento 6.18.0 documentation)

A series of deprecations and renamings that normalize all events into the same naming convention.

  • Old: OnIceCandidate event
    New: IceCandidateFound event

コンテナkurentoログ(エラー箇所のみ抜粋)

bbb2602-docker-kurento-1  | 0:00:03.744960117     1 0x55afae78e150 INFO      KurentoMediaServer main.cpp:259:main: Kurento Media Server started
bbb2602-docker-kurento-1  | 0:05:03.239013465     1 0x7fc98c00a210 INFO             rtpendpoint kmsrtpendpoint.c:982:kms_rtp_endpoint_start_transport_send:<kmsrtpendpoint0> COMEDIA: Media 'video' doesn't use COMEDIA
bbb2602-docker-kurento-1  | 0:05:03.239079303     1 0x7fc98c00a210 INFO        kmsrtpconnection kmsrtpconnection.c:81:kms_rtp_connection_set_remote_info:<KmsRtpConnection@0x7fc974020400> Set remote host: xxxx:xxxx:xxxx:xxxx:xxxx:xxxx, RTP: 25010, RTCP: 25019
bbb2602-docker-kurento-1  | 0:05:03.239145458     1 0x7fc98c00a210 WARN            multiudpsink gstmultiudpsink.c:1285:gst_multiudpsink_configure_client:<multiudpsink0> error: Invalid address family (got 10)
bbb2602-docker-kurento-1  | 0:05:03.239483371     1 0x55afae78e150 ERROR   KurentoMediaElementImpl MediaElementImpl.cpp:456:processBusMessage:<kmsrtpendpoint0> Error code 13: Could not get/set settings from/on resource., source: multiudpsink0, element: kmsrtpendpoint0, debug info: gstmultiudpsink.c(1285): gst_multiudpsink_configure_client (): /GstPipeline:pipeline0/KmsRtpEndpoint:kmsrtpendpoint0/KmsRtpSession:kmsrtpsession0/GstMultiUDPSink:multiudpsink0:
bbb2602-docker-kurento-1  | Invalid address family (got 10)
bbb2602-docker-kurento-1  | 0:05:03.239668779     1 0x7fc98c00a210 WARN            multiudpsink gstmultiudpsink.c:1285:gst_multiudpsink_configure_client:<multiudpsink1> error: Invalid address family (got 10)
bbb2602-docker-kurento-1  | 0:05:03.239829382     1 0x55afae78e150 ERROR   KurentoMediaElementImpl MediaElementImpl.cpp:456:processBusMessage:<kmsrtpendpoint0> Error code 13: Could not get/set settings from/on resource., source: multiudpsink1, element: kmsrtpendpoint0, debug info: gstmultiudpsink.c(1285): gst_multiudpsink_configure_client (): /GstPipeline:pipeline0/KmsRtpEndpoint:kmsrtpendpoint0/KmsRtpSession:kmsrtpsession0/GstMultiUDPSink:multiudpsink1:
bbb2602-docker-kurento-1  | Invalid address family (got 10)
bbb2602-docker-kurento-1  | 0:05:03.242332597     1 0x7fc98c00a210 INFO                kmsutils kmsutils.c:522:kms_utils_pad_monitor_gaps:<'':sink_video_default> Add probe: DISCONT buffers and GAP events
bbb2602-docker-kurento-1  | 0:05:03.243126784     1 0x7fc98c00ff00 WARN                  udpsrc gstudpsrc.c:1015:gst_udpsrc_fill:<udpsrc1> error: select error: Socket is already closed
bbb2602-docker-kurento-1  | 0:05:03.243863527     1 0x55afae78e150 ERROR   KurentoMediaElementImpl MediaElementImpl.cpp:456:processBusMessage:<kmsrtpendpoint0> Error code 9: Could not read from resource., source: udpsrc1, element: kmsrtpendpoint0, debug info: gstudpsrc.c(1015): gst_udpsrc_fill (): /GstPipeline:pipeline0/KmsRtpEndpoint:kmsrtpendpoint0/KmsRtpSession:kmsrtpsession0/GstUDPSrc:udpsrc1:
bbb2602-docker-kurento-1  | select error: Socket is already closed
bbb2602-docker-kurento-1  | 0:05:03.257338739     1 0x7fc98c00ff00 WARN                 basesrc gstbasesrc.c:3072:gst_base_src_loop:<udpsrc1> error: Internal data stream error.
bbb2602-docker-kurento-1  | 0:05:03.257614280     1 0x7fc98c00ff00 WARN                 basesrc gstbasesrc.c:3072:gst_base_src_loop:<udpsrc1> error: streaming stopped, reason error (-5)
bbb2602-docker-kurento-1  | 0:05:03.257976554     1 0x55afae78e150 ERROR   KurentoMediaElementImpl MediaElementImpl.cpp:456:processBusMessage:<kmsrtpendpoint0> Error code 1: Internal data stream error., source: udpsrc1, element: kmsrtpendpoint0, debug info: gstbasesrc.c(3072): gst_base_src_loop (): /GstPipeline:pipeline0/KmsRtpEndpoint:kmsrtpendpoint0/KmsRtpSession:kmsrtpsession0/GstUDPSrc:udpsrc1:
bbb2602-docker-kurento-1  | streaming stopped, reason error (-5)

FFmpeg does not support "RTP and RTCP multiplexing " (rtcp-mux in SDP files):

webrtc-sfuとkurentoの設定を修正

webrtc-sfu, kurento 共にアップデート・再ビルド

docker-compose.yml

.....

kurento:
  image: kurento/kurento-media-server:7.0.1
  restart: unless-stopped
  environment:
    KMS_EXTERNAL_IPV4: 10.7.7.1
    #KMS_EXTERNAL_IPV6: ${EXTERNAL_IPv6}
    KMS_MIN_PORT: 10000
    KMS_MAX_PORT: 10030
  network_mode: host
  volumes:
    - vol-kurento:/var/kurento


webrtc-sfu:
  .....
  .....

  environment:
    .....
    .....
    # changed from default in default.example.yml
    MCS_HOST: 10.7.7.1
    MCS_ADDRESS: 10.7.7.1
    KURENTO: '[{"ip": "0.0.0.0", "url": "ws://10.7.7.1:8888/kurento"}]'
    .....
    MS_WEBRTC_LISTEN_IPS: '[{"ip":"0.0.0.0", "announcedIp":"${EXTERNAL_IPv4}"}, {"ip":"${EXTERNAL_IPv6}"}]'
    MS_RTP_LISTEN_IP: '{"ip":"0.0.0.0", "announcedIp":"${EXTERNAL_IPv4}"}'
    .....

設定ファイル一覧

/etc/bigbluebutton ディレクトリ内に上書き用設定ファイルを作成

BBBの上書き用設定ファイルは、下記の mod/xxxxx イメージで提供されています。

BBB Docker 設定ファイル

  • mod/ apps-akka /bbb-apps-akka.conf

  • mod/bbb-pads/bbb-pads/config/settings.json.template

  • mod/fsesl-akka/bbb-fsesl-akka.conf

  • mod/bbb-web/bbb-web.properties

  • mod/recordings/bbb-web.properties

  • mod/recordings/recording.yml

WebRTC-SFU 設定ファイル

WebRTC-SFUに関してはdocker-compose.ymlファイル内で環境変数を指定・変更すること。

  • mod/webrtc-sfu/bbb-webrtc-sfu/config/default.example.yml