/owt-docker

Docker for https://github.com/open-webrtc-toolkit/owt-server

Primary LanguageDockerfileMIT LicenseMIT

OWT-DOCKER

Docker for owt-server from Ubuntu18(ubuntu:bionic).

  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:4.3:内网演示方式Usage: HostIP,配置好了docker-host域名。
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:config:修改了端口和脚本,参考Port RangeAuth Update
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:pack:完成了pack.js步骤,打包好了OWT。
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:build:完成了build.js步骤,编译好了OWT。
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:system:完成了installDepsUnattended.sh步骤,安装好了依赖。

Note: 针对Docker下部署和使用OWT,我们修改了一些代码和配置,参考Here

以MacPro为例,如何使用镜像搭建Demo,推荐使用Usage: HostIP,提供了几种常见的方式:

  • 在内网用镜像搭建OWT,使用脚本自动获取IP,自动修改OWT配置文件中的IP,参考Usage: HostIP
  • 在内网使用镜像快速搭建OWT,需要修改IP,参考Usage
  • 有公网IP或域名时,用镜像搭建OWT服务,参考Usage: Internet

下面是本文涉及的重点:

  • OWT开发环境搭建,本机快速部署,不用改IP,参考Usage: HostIP
  • OWT本机修改,可修改C++代码,在Docker中编译和运行,参考Develop
  • OWT调试js和WebRTC的C++代码,参考Debug
  • OWT几组性能数据,WebRTC性能不高,参考Performance
  • OWT存在的一些问题,参考Issues
  • OWT的程序结构,使用Nodejs调用C++代码,参考CodeNodejs
  • OWT的代码分析,参考CodeVideo

Usage

下面我们以MacPro为例,如何使用镜像搭建内网Demo,其他OS将命令替换就可以。

>>> Step 0: 当然你得有个Docker。

可以从docker.io下载一个,安装就好了。 执行docker version,应该可以看到Docker版本:

Mac:owt-docker chengli.ycl$ docker version
Client:
 Version:	17.12.0-ce
Server:
  Version:	17.12.0-ce

>>> Step 1: 通过Docker镜像,启动OWT环境。

docker run -it -p 3004:3004 -p 3300:3300 -p 8080:8080 -p 60000-60050:60000-60050/udp \
    registry.cn-hangzhou.aliyuncs.com/ossrs/owt:config bash

Note: Docker使用的版本是owt-server 4.3, owt-client 4.3, IntelMediaSDK 18.4.0.

Note: OWT需要开一系列范围的UDP端口,docker映射大范围端口会有问题,所以我们只指定了50个测试端口,已经在镜像中修改了配置,参考Port Range

Note: 针对Docker下部署和使用OWT,我们修改了一些代码和配置,参考Here

>>> Step 2: 设置OWT的IP信息,设置为Mac的IP地址。也可以自动获取和设置IP,参考Usage: HostIP

# vi dist/webrtc_agent/agent.toml
[webrtc]
network_interfaces = [{name="eth0",replaced_ip_address="192.168.1.4"}]  # default: []

# vi dist/portal/portal.toml
[portal]
ip_address = "192.168.1.4" #default: ""

>>> Step 3: 输入命令,初始化OWT和启动服务。

(cd dist && ./bin/init-all.sh && ./bin/start-all.sh)

>>> Step 4: 大功告成。

打开OWT的默认演示页面,私有证书需要选择Advanced => Proceed to xxx

由于证书问题,第一次需要在浏览器,先打开OWT信令服务(Portal)页面(后续就不用了):

访问管理后台,注意启动服务时有个sampleServiceId和sampleServiceKey,打开页面会要求输入这个信息:

Note: 我们也可以使用域名来访问OWT服务,这样就不用每次IP变更后修改配置文件,参考Usage: HostIP

Note: 目前提供OWT 4.3的镜像开发环境,若需要更新代码需要修改Dockerfile,或者参考Deubg重新编译。

还可以尝试其他方式,比如:

  • 在内网使用镜像快速搭建OWT,需要修改IP,参考Usage
  • 在内网用镜像搭建OWT,使用脚本自动获取IP,自动修改OWT配置文件中的IP,参考Usage: HostIP
  • 有公网IP或域名时,用镜像搭建OWT服务,参考Usage: Internet

Usage: HostIP

在之前Usage中,我们说明了如何在内网用镜像快速搭建OWT服务,但需要修改OWT的配置文件。 这里我们说明如何使用脚本自动获取IP,自动修改OWT配置文件中的IP。

下面我们以MacPro为例,如何使用镜像搭建内网Demo,其他OS将命令替换就可以。

>>> Step 0: 当然你得有个Docker。

可以从docker.io下载一个,安装就好了。 执行docker version,应该可以看到Docker版本:

Mac:owt-docker chengli.ycl$ docker version
Client:
 Version:	17.12.0-ce
Server:
  Version:	17.12.0-ce

>>> Step 1: 先获取宿主机的IP,该IP需要在访问的机器上能Ping通。

HostIP=`ifconfig en0 inet| grep inet|awk '{print $2}'` &&
echo "" && echo "" && echo "" && echo "OWT演示页面:" && echo "  https://$HostIP:3004/" &&
echo "第一次需要先访问信令(Portal):" && echo "  https://$HostIP:8080/socket.io/?EIO=3&transport=polling&t=N2UmsIn"

或者直接设置为自己的IP:

HostIP="192.168.1.4" &&
echo "" && echo "" && echo "" && echo "OWT演示页面:" && echo "  https://$HostIP:3004/" &&
echo "第一次需要先访问信令(Portal):" && echo "  https://$HostIP:8080/socket.io/?EIO=3&transport=polling&t=N2UmsIn"

>>> Step 2: 通过Docker镜像,启动OWT环境。

HostIP=`ifconfig en0 inet| grep inet|awk '{print $2}'` &&
docker run -it -p 3004:3004 -p 3300:3300 -p 8080:8080 -p 60000-60050:60000-60050/udp \
    --env DOCKER_HOST=$HostIP registry.cn-hangzhou.aliyuncs.com/ossrs/owt:4.3 bash

Note: Docker使用的版本是owt-server 4.3, owt-client 4.3, IntelMediaSDK 18.4.0.

Note: OWT需要开一系列范围的UDP端口,docker映射大范围端口会有问题,所以我们只指定了50个测试端口,已经在镜像中修改了配置,参考Port Range

Note: OWT对外提供了信令和媒体服务,所以需要返回可外部访问的IP地址,我们通过环境变量传递给Docker,参考Docker Host IP

Note: 针对Docker下部署和使用OWT,我们修改了一些代码和配置,参考Here

>>> Step 3: 输入命令,初始化OWT和启动服务。

(cd dist && ./bin/init-all.sh && ./bin/start-all.sh)

>>> Step 4: 大功告成。

打开OWT的默认演示页面,私有证书需要选择Advanced => Proceed to 192.168.1.4 (unsafe)

由于证书问题,第一次需要在浏览器,先打开OWT信令服务(Portal)页面(后续就不用了):

访问管理后台,注意启动服务时有个sampleServiceId和sampleServiceKey,打开页面会要求输入这个信息:

Note: 我们使用域名来访问OWT服务,这样宿主机IP变更后,只需要执行脚本就可以,参考Docker Host IP

Note: 目前提供OWT 4.3的镜像开发环境,若需要更新代码需要修改Dockerfile,或者参考Deubg重新编译。

还可以尝试其他方式,比如:

  • 在内网使用镜像快速搭建OWT,需要修改IP,参考Usage
  • 在内网用镜像搭建OWT,使用脚本自动获取IP,自动修改OWT配置文件中的IP,参考Usage: HostIP
  • 有公网IP或域名时,用镜像搭建OWT服务,参考Usage: Internet

Usage: Internet

Remark: 下面说明公网IP或域名搭建OWT环境,若在内网或本机使用Docker快速搭建OWT开发环境,参考Usage:

>>> Step 1: 通过Docker镜像,启动OWT环境。

docker run -it -p 3004:3004 -p 3300:3300 -p 8080:8080 -p 60000-60050:60000-60050/udp \
    registry.cn-hangzhou.aliyuncs.com/ossrs/owt:config bash

Note: Docker使用的版本是owt-server 4.3, owt-client 4.3, IntelMediaSDK 18.4.0.

Note: OWT需要开一系列范围的UDP端口,docker映射大范围端口会有问题,所以我们只指定了50个测试端口,已经在镜像中修改了配置,参考Port Range

Note: 针对Docker下部署和使用OWT,我们修改了一些代码和配置,参考Here

>>> Step 2: 配置公网IP或域名,参考Use Internet Name

# vi dist/webrtc_agent/agent.toml
[webrtc]
network_interfaces = [{name="eth0",replaced_ip_address="182.28.12.12"}]  # default: []

# vi dist/portal/portal.toml
[portal]
ip_address = "182.28.12.12" #default: ""

>>> Step 3: 输入命令,初始化OWT和启动服务。

(cd dist && ./bin/init-all.sh && ./bin/start-all.sh)

Remark: 注意会有个提示是否添加MongoDB账号,可以忽略或写No(默认5秒左右就会忽略)。

>>> Step 4: 大功告成。

打开OWT的默认演示页面,私有证书需要选择Advanced => Proceed to xxx

由于证书问题,第一次需要在浏览器,先打开OWT信令服务(Portal)页面(后续就不用了):

访问管理后台,注意启动服务时有个sampleServiceId和sampleServiceKey,打开页面会要求输入这个信息:

Note: 目前提供OWT 4.3的镜像开发环境,若需要更新代码需要修改Dockerfile,或者参考Deubg重新编译。

还可以尝试其他方式,比如:

  • 在内网使用镜像快速搭建OWT,需要修改IP,参考Usage
  • 在内网用镜像搭建OWT,使用脚本自动获取IP,自动修改OWT配置文件中的IP,参考Usage: HostIP
  • 有公网IP或域名时,用镜像搭建OWT服务,参考Usage: Internet

Develop

如果需要修改代码后编译:

  1. 可以先将Docker代码拷贝出来。不用每次都拷贝,只有第一次需要拷贝。
  2. 接着在本地修改OWT代码。可以用IDE等编辑器编辑代码,比较方便。
  3. 然后挂载本地目录到Docker。挂载后,会以本地目录的文件,代替Docker中的文件。
  4. 最后在Docker编译和运行OWT。如果修改的是JS,可以直接重启服务生效。

>>> Step 1: 将Docker代码拷贝出来。

首先,需要先运行Docker:

docker run -it registry.cn-hangzhou.aliyuncs.com/ossrs/owt:4.3 bash

然后,可以用docker ps,获取运行的容器ID,比如是75647b1a7b16

Mac:owt-docker chengli.ycl$ docker ps
CONTAINER ID        IMAGE
75647b1a7b16        registry.cn-hangzhou.aliyuncs.com/ossrs/owt:4.3

接着,从运行的容器75647b1a7b16中拷贝OWT代码:

mkdir -p ~/git/owt-docker && cd ~/git/owt-docker &&
docker cp 75647b1a7b16:/tmp/git/owt-docker/owt-client-javascript-4.3 .
docker cp 75647b1a7b16:/tmp/git/owt-docker/owt-server-4.3 .

这样,我们就有了OWT 4.3的代码,接下来就可以在本地修改代码了。

Note: 当然,也可以直接从OWT下载最新代码,映射到Docker后编译执行,注意只映射source目录到Docker。

>>> Step 2: 在本地修改OWT代码。

我们举个栗子,我们修改OWT,支持使用环境变量DOCKER_HOST来指定portal.ip_address

Note: Docker运行时,可以通过--env DOCKER_HOST='192.168.1.4'设置环境变量,这样在Docker中就可以通过获取环境变量来获取IP。

修改文件source/portal/index.js,参考118d225f

if (config.portal.ip_address.indexOf('$') == 0) {
    config.portal.ip_address = process.env[config.portal.ip_address.substr(1)];
    log.info('ENV: config.portal.ip_address=' + config.portal.ip_address);
}

修改文件source/portal/portal.toml,将信令Portal的ip_address引用环境变量:

[portal]
ip_address = "$DOCKER_HOST" #default: ""

Remark: 这里我们只修改了JS代码,也可以修改c++代码。

>>> Step 3: 挂载本地目录到Docker。

启动Docker,并将本地目录挂载到Docker:

cd ~/git/owt-docker/owt-server-4.3 &&
HostIP=`ifconfig en0 inet| grep inet|awk '{print $2}'` &&
docker run -it -p 3004:3004 -p 3300:3300 -p 8080:8080 -p 60000-60050:60000-60050/udp \
    --env DOCKER_HOST=$HostIP -v `pwd`:/tmp/git/owt-docker/owt-server-4.3 \
    registry.cn-hangzhou.aliyuncs.com/ossrs/owt:4.3 bash

>>> Step 4: 在Docker编译和运行OWT。

若修改了c++代码,则需要编译OWT(若没有修改则可以忽略):

./scripts/build.js -t all --check --debug

Remark: 注意我们加了debug参数,编译出调试版本。

这个例子中,我们只修改了JS文件,所以直接打包就可以:

./scripts/pack.js -t all --debug --addon-debug --install-module --sample-path $CLIENT_SAMPLE_PATH

Remark: 注意我们加了debug参数,打包调试版本。

为了保持命令行一直,我们将dist-debug链接为dist

ln -sf dist-debug dist

可以看到,dist/portal/portal.tomldist/portal/index.js这两个文件都更新了。

启动服务后,就可以看到修改生效了:

(cd dist && ./bin/init-all.sh && ./bin/start-all.sh)

Debug

供Debug用的镜像:

  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:debug:内网演示方式Usage: HostIP,配置好了docker-host域名。
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:config-debug:修改了端口和脚本,参考Port RangeAuth Update
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:build-debug:完成了build.js步骤,编译好了OWT。
  • registry.cn-hangzhou.aliyuncs.com/ossrs/owt:system:完成了installDepsUnattended.sh步骤,安装好了依赖。

Note: 针对Docker下调试OWT,我们修改了一些代码和配置,参考Here

我们举个例子,调试OWT中的WebRTC Agent的代码。

>>> Step 0: 一定要使用调试版本的镜像。

为了方便调试,请使用调试镜像registry.cn-hangzhou.aliyuncs.com/ossrs/owt:debug,我们修改了几个地方:

  • 设置WebRTC Agent的进程数目为1,可以直接调试这个进程,不然多个进程每个进程都要设置断点,会比较麻烦(也可以调试)。
  • 设置nodeManager的超时时间为3000秒(即50分钟),默认是3秒,所以很容易在设置断点或单步运行时进程被杀掉,导致调试失败。
  • 日志级别全部设置为DEBUG,会显示更多的详细的日志,文件为log4js_configuration.json
  • 设置amqp_client的超时时间为2000秒(即33分钟),默认是2秒,所以会在调试时导致各种超时。

推荐使用debug镜像启动,若不使用debug镜像,你也可以手动修改你的OWT,改动参考这里

# 编译OWT时使用特殊的参数
./scripts/build.js -t all --check --debug

# vi dist/webrtc_agent/agent.toml +3
[agent]
maxProcesses = 1 #default: 13
prerunProcesses = 1 #default: 2

# vi dist/webrtc_agent/log4js_configuration.json +13
"ClusterWorker": "DEBUG",
"LoadCollector": "DEBUG",
"AmqpClient": "DEBUG",
"WorkingJS": "DEBUG",
"NodeManager": "DEBUG",
"WebrtcNode": "DEBUG",
"WrtcConnection": "DEBUG",
"Connection": "DEBUG",
"Sdp": "DEBUG",
"WorkingAgent": "DEBUG",
"Connections": "DEBUG",
"InternalConnectionFactory": "DEBUG"

# vi dist/webrtc_agent/nodeManager.js +125
child.check_alive_interval = setInterval(function() {
}, 3000000);

# vi dist/webrtc_agent/amqp_client.js +10
var TIMEOUT = 2000000;

Note: 如果需要调试其他模块,也需要修改这些参数。

Remark: 页面超时是由后端决定的,只需要修改后端的amqp_client.js的超时就可以。

>>> Step 1: 启动debug镜像。

启动Docker,添加gdb需要的参数--privileged

HostIP=`ifconfig en0 inet| grep inet|awk '{print $2}'` &&
docker run -it -p 3004:3004 -p 3300:3300 -p 8080:8080 -p 60000-60050:60000-60050/udp \
    --privileged --env DOCKER_HOST=$HostIP \
    registry.cn-hangzhou.aliyuncs.com/ossrs/owt:debug bash

当然,若需要挂载本地目录,可以按照上一步修改本地代码,或者从debug镜像将代码拷贝出来,然后执行:

cd ~/git/owt-docker/owt-server-4.3 &&
HostIP=`ifconfig en0 inet| grep inet|awk '{print $2}'` &&
docker run -it -p 3004:3004 -p 3300:3300 -p 8080:8080 -p 60000-60050:60000-60050/udp \
    --privileged --env DOCKER_HOST=$HostIP -v `pwd`:/tmp/git/owt-docker/owt-server-4.3 \
    registry.cn-hangzhou.aliyuncs.com/ossrs/owt:debug bash

启动OWT服务:

(cd dist && ./bin/init-all.sh && ./bin/start-all.sh)

>>> Step 2: 启动GDB调试WebRTC Agent进程。

启动GDB调试,并Attach调试WebRTC Agent的进程:

gdb --pid `ps aux|grep webrtc|grep workingNode|awk '{print $2}'`

然后设置断点,比如可以设置在函数上,或者在文件的某一行:

b WebRtcConnection.cc:346
c

Note: 设置断点后,执行continue或者c,继续跑程序。

>>> Step 3: 打开页面,命中断点。

打开页面,就会进入断点了:

Thread 1 "node" hit Breakpoint 1, WebRtcConnection::addRemoteCandidate (info=...) at ../WebRtcConnection.cc:313
313	  WebRtcConnection* obj = Nan::ObjectWrap::Unwrap<WebRtcConnection>(info.Holder());
(gdb) bt
#0  0x00007fe00d7affac in WebRtcConnection::addRemoteCandidate(Nan::FunctionCallbackInfo<v8::Value> const&) (info=...) at ../WebRtcConnection.cc:313
#1  0x00007fe00d7a9243 in Nan::imp::FunctionCallbackWrapper(v8::FunctionCallbackInfo<v8::Value> const&) (info=...)
    at ../../../../../node_modules/nan/nan_callbacks_12_inl.h:179
#2  0x000055c7c8302d0f in v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) ()
#3  0x000055c7c836bb62 in  ()

GDB是非常强大的调试工具,详细命令参考Debugging with GDB

Note: 也可以使用node inspect xxx.js调试js程序。

Remark: OWT使用Nodejs的NAN调用C++代码,调试原理参考CodeNodejs,代码结构参考CodeNodejs

Dependencies

OWT会安装很多依赖的库,详细可以参考Dockerfile中安装的依赖。

这些代码和依赖都会在docker中,下载代码包括:

  • node_modules/nan
  • build, 734M
    • build/libdeps/ffmpeg-4.1.3.tar.bz2
    • build/libdeps/libnice-0.1.4.tar.gz
    • build/libdeps/openssl-1.0.2t.tar.gz
    • build/libdeps/libsrtp-2.1.0.tar.gz
  • third_party, 561M
    • third_party/quic-lib, 8.4M
    • third_party/licode, 34M
    • third_party/openh264, 33M
    • third_party/SVT-HEVC, 39M
    • third_party/webrtc, 448M

Port Range

Docker由于需要映射端口,所以如果需要开特别多的UDP端口会有问题,Mac下的Docker能开50个左右的UDP端口,测试是够用了。

我们在镜像中已经修改了配置文件,将端口范围改成了60000-60050/udp,如果有需要可以自己改:

# vi dist/webrtc_agent/agent.toml
[webrtc]
maxport = 60050 #default: 0
minport = 60000 #default: 0

Note: 注意别改错了,还有另外个地方也有这个配置,[internal]这个是配置集群的(内部传输的端口比如webrtc-agent和video-agent之间的传输端口),单个Docker不用修改。

Docker Host IP

OWT在Docker中运行时,Docker就相当于一个局域网,OWT获取到地址是个内网IP,在外面是无法访问的,所以需要修改配置。

比如,我们在Docker中查看OWT的IP,可以发现是eth0 172.17.0.2

root@d3041e7dd80d:/tmp/git/owt-docker/owt-server-4.3# ifconfig eth0| grep inet
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255

我们在Host机器(也就是运行Docker的机器上)查看IP,可以发现是en0 192.168.1.4(以Mac为例):

Mac:owt-docker chengli.ycl$ ifconfig en0 inet|grep inet
	inet 192.168.1.4 netmask 0xffffff00 broadcast 192.168.1.255

那么我们就需要修改OWT的配置,让它知道自己应该对外使用192.168.1.4这个宿主机的地址(当然如果有公网IP也可以)。

  • dist/webrtc_agent/agent.toml,修改[webrtc]中的network_interfaces,是媒体流的服务地址。
  • dist/portal/portal.toml,修改[portal]中的ip_address,是信令的服务地址。

Docker提供了更好的办法,可以将宿主机的IP(192.168.1.4)通过--env传给OWT,通过读取环境变量获取IP:

HostIP=`ifconfig en0 inet| grep inet|awk '{print $2}'` &&
docker run -it --env DOCKER_HOST=$HostIP \
    registry.cn-hangzhou.aliyuncs.com/ossrs/owt:4.3 bash

Remark: 注意应该映射端口,这里为了强调域名就没有把端口映射写上。

这样在Docker中就可以知道宿主机的IP地址了(或者公网IP也可以):

root@d5a5bc41169e:/tmp/git/owt-docker/owt-server-4.3# echo $DOCKER_HOST
192.168.1.4

我们就可以将OWT对外暴露的服务,修改为环境变量$DOCKER_HOST,避免每次启动都要改配置:

# vi dist/webrtc_agent/agent.toml
[webrtc]
network_interfaces = [{name="eth0",replaced_ip_address="$DOCKER_HOST"}]  # default: []

# vi dist/portal/portal.toml
[portal]
ip_address = "$DOCKER_HOST" #default: ""

启动OWT时,会被替换成IP地址。

Remark: 注意我们在Docker中修改了OWT,提交了PR参考

当然,若有公网可以访问的域名,或者公网IP,直接修改为IP或域名也可以:

# vi dist/webrtc_agent/agent.toml
[webrtc]
network_interfaces = [{name="eth0",replaced_ip_address="192.168.1.4"}]  # default: []

# vi dist/portal/portal.toml
[portal]
ip_address = "192.168.1.4" #default: ""

Auth Update

若使用镜像registry.cn-hangzhou.aliyuncs.com/ossrs/owt:pack,没有修改UDP端口,也没有修改脚本,启动OWT时会提示:

Update RabbitMQ/MongoDB Account? [No/Yes]

会在10秒后默认选择No,我们修改了这个脚本dist/bin/init-all.sh

if ${HARDWARE}; then
  echo "Initializing with hardware msdk"
  init_hardware
  #init_auth
else
  echo "Initializing..."
  init_software
  #init_auth
fi

这样就默认不会更新MongoDB和RabbitMQ的认证信息,直接启动服务了。

Performance

MacPro信息:

  • macOS Mojave
  • Version 10.14.6 (18G3020)
  • MacBook Pro (Retina, 15-inch, Mid 2015)
  • Processor: 2.2 GHz Intel Core i7
  • Memory: 16 GB 1600 MHz DDR3

Docker信息:

  • Docker Desktop 2.2.0.3(42716)
  • Engine: 19.03.5
  • Resources: CPUs 4, Memory 4GB, Swap 1GB

下面是不同人数和模式的测试数据:

模式 参数 人数 CPU CPU
(webrtc)
CPU
(video)
CPU
(audio)
Memory Memory
(webrtc)
Memory
(video)
Memory
(audio)
Load
MCU 2 72% 28% 32% 12% 238MB 93MB 85MB 56MB 2.38
MCU 4 119% 44% 57% 18% 267MB 98MB 94MB 75MB 3.41
SFU forward=true 2 35% 35% 0% 0% 64MB 64MB 0MB 0MB 2.38
SFU forward=true 4 87% 87% 0% 0% 95MB 95MB 0MB 0MB 9.29
MCU forward=true 2 61% 39% 22% 0% 121MB 64MB 57MB 0MB 4.81
MCU forward=true 4 130% 91% 24% 14% 230MB 93MB 65MB 72MB 9.83

Note: 默认是MCU+SFU模式,SFU模式参考SFU Mode

Note: 删除Views是指在管理后台,例如 https://192.168.1.4:3300/console/ 的房间设置中,删除Views然后Apply。

Note: Intel的朋友反馈,在一台8核3.5GHZUbuntu机器(台式机)上,给Docker分配4核4GB内存,跑4个SFU,CPU占用率38%左右(WebRTC)。同样条件,4个MCU页面,CPU使用87%,其中WebRTC占用22%,MCU占用65%(视频46%、音频19%)。

下面是WebRTC这个Agent的性能指标:

模式 人数 CPU Memory Threads 线程增长
MCU 0 0% 47MB 36 -
MCU 1 11% 58MB 46 -
MCU 2 24% 62MB 55 19%
MCU 3 37% 67MB 64 16%
MCU 4 62% 72MB 73 14%

Note: 可以看到,由于直接使用了WebRTC,使用的线程数和人数正关联,限制了能支持的并发数目。由于WebRTC不涉及编解码运算,属于IO密集型,个人觉得这里有可以提升的空间。

下面是Video这个Agent的性能指标:

模式 人数 CPU Memory Threads 线程增长
MCU 0 0% 46MB 10 -
MCU 1 18% 66MB 20 -
MCU 2 22% 71MB 21 5%
MCU 3 33% 78MB 22 4%
MCU 4 74% 83MB 23 4%

下面是Audio这个Agent的性能指标:

模式 人数 CPU Memory Threads 线程增长
MCU 0 0.3% 43MB 10 -
MCU 1 4.7% 47MB 16 -
MCU 2 9.3% 53MB 20 25%
MCU 3 13% 63MB 24 20%
MCU 4 26% 73MB 28 16%

Note: Video和Audio都是编解码运算,是CPU密集型,线程随人数正向增长符合预期。

SFU Mode

OWT默认是MCU+SFU模式,比如打开两个页面:

其实上面的forward=true,并不是仅仅只有SFU模式,只是拉了转发的流。若需要关闭MCU模式,只开启SFU:

  1. 打开管理后台:https://192.168.1.4:3300/console
  2. 输入ServiceID和Key,在启动OWT服务时,会打印出来,比如:sampleServiceIdsampleServiceKey
  3. 进入后台后,可以看到Room,View Count默认是1,选择Detail,看到详细的房间配置。
  4. 在Room配置中,Views那一节,点按钮Remove,删除views配置,在页面最底下点对勾提交。
  5. Apply按钮应用房间配置,这时候可以看到View Count是0。

这样操作后,再打开页面就只有SFU模式了,不再有video和audio的进程消耗CPU:https://192.168.1.4:3004/?forward=true

Note: SFU模式下,可以停止掉video-agent和audio-agent,流直接通过webrtc-agent中转。

Note: MCU模式下,推流先到webrt-agent,video会新建TCP连接从webrtc-agent拉流。每个人的订阅,都会从webrtc-agent拉合屏的流,webrtc-agent从video新建个TCP连接拉流。

Notes

  • CodeNodejs 关于程序结构,调用关系,Nodejs和C++代码如何组合。
  • CodeVideo 关于MCU的video处理部分,如果获取原始视频流,合屏,转码。
    • Scripts 启动脚本,调试nodejs部分。
    • Heartbeat nodejs子进程和心跳消息。
    • Load Balancer nodejs子进程负载均衡。
    • Schedule 调度到video进程处理视频。
    • VideoNode video的处理流程,和webrtc-agent的通信,mixer和transcoder。
    • GDB Debug 如何使用GDB调试C++代码。
    • VideoMixer In mixer输入部分,从webrtc-agent读取数据,读取Frame,解码。
    • VideoMixer Out mixer输出部分,合流,编码,输出到webrtc-agent。

Note: VideoNode video的处理流程,和webrtc-agent的通信,mixer和transcoder。

Note: VideoMixer In mixer输入部分,从webrtc-agent读取数据,读取Frame,解码。

Note: VideoMixer Out mixer输出部分,合流,编码,输出到webrtc-agent。

Issues

  1. OWT UDP端口没有复用,导致需要开一系列端口,参考Port Range
  2. OWT对外的服务发现,也就是返回给客户端的信令和UDP的IP,是通过配置文件,参考Docker Host IP
  3. OWT的webrtc_agent,使用WebRTC收发流,线程数会随着用户数增多而增多,导致SFU模式下也不能有很高并发,参考Performance: webrtc-agent
  4. OWT实现了完整的集群,包括Node的负载管理和调度,服务发现和路由,而实际中比较复杂的业务系统会有自己的调度和服务管理,这导致OWT集成到现有系统比较复杂,参考OWT Schedule
  5. OWT选择的是多PC,MCU下就是2个PC,SFU下会有很多个PC。人数比较多时OWT的SFU工作不太好,不过可以选择MCU模式。
  6. OWT每个进程一个日志文件,日志没有业务逻辑区分,全链路排查问题时会比较困难,比如一个进程服务了30个人,一般有问题的是几个人,怎么把有问题的人的日志分离出来。
  7. OWT依赖nodejs,每次打包都会下载N多个依赖,而且是从网络上下载,有时候就是会一直失败。
  8. OWT每个模块都使用了进程间通信,比如webrtc和video之间走的是TCP,要求部署在一个机房,无法支持跨区域级联。