mediasoup 是一个强大的 WebRTC SFU 服务。mediasoup-demo 则是 mediasoup 的一个很不错的入门演示程序。这里记录把 mediasoup-demo 跑起来的过程。操作系统平台以 Ubuntu 20.04 为例。mediasoup 主要以 JavaScript 开发,运行环境为 Node.js,它一般作为 Node.js 模块运行于 Node.js 应用中。
mediasoup v3 的 安装指南 中有安装要求:
对于 Linux,OSX 和任何 *NIX 系统,还有额外的要求:
当系统软件库中的 node 版本不符合要求时,需要自己安装适当版本的 Node。Node 版本不合适,很有可能 demo 就运行不起来。笔者试了多个版本都没能把 mediasoup 跑起来,包括 v13.1.0
、v13.10.0
和最新的长期支持版 v16.13.1
,不过在 sequelize 的 GitHub issue 12419 ,看到有人提到用 v12.18.3
解决了笔者遇到的一些问题,笔者也选择了 v12.18.3
版。
下载 node 的预编译压缩包:
https://nodejs.org/dist/
如果系统中已经安装了其它版本的 node,在安装之前,还需要先移除之前安装的版本:
sudo rm -rf /usr/local/bin/npm /usr/local/share/man/man1/node* ~/.npm
sudo rm -rf /usr/local/lib/node*
sudo rm -rf /usr/local/bin/node*
sudo rm -rf /usr/local/include/node*
sudo apt-get purge nodejs npm
sudo apt autoremove
解压并安装 node:
tar -xf node-v12.18.3-linux-x64.tar.xz
sudo mv node-v12.18.3-linux-x64/bin/* /usr/local/bin/
sudo mv node-v12.18.3-linux-x64/lib/node_modules/ /usr/local/lib/
下载 mediasoup-demo,克隆 mediasoup-demo 工程
$ git clone https://github.com/versatica/mediasoup-demo.git
$ cd mediasoup-demo
$ git checkout v3
设置 mediasoup-demo server:
mediasoup-demo$ cd server
server$ npm install
拷贝 config.example.js
到 config.js
,并对它做一些定制化的修改:
$ cp config.example.js config.js
这一步是必须的,否则 mediasoup-demo 运行将出错。所需要做的配置包括域名,监听的 IP 地址,HTTPS 证书和私钥的路径
域名、监听 HTTPS 的 IP/端口、证书路径及私钥路径:
domain : process.env.DOMAIN || 'localhost',
// Signaling settings (protoo WebSocket server and HTTP API server).
https :
{
listenIp : '0.0.0.0',
// NOTE: Don't change listenPort (client app assumes 4443).
listenPort : process.env.PROTOO_LISTEN_PORT || 4443,
// NOTE: Set your own valid certificate files.
tls :
{
cert : process.env.HTTPS_CERT_FULLCHAIN || `${__dirname}/certs/fullchain.pem`,
key : process.env.HTTPS_CERT_PRIVKEY || `${__dirname}/certs/privkey.pem`
}
},
这些配置可以通过修改 config.js
实现,也可以通过设置环境变量实现。这里不修改这些配置,将 TLS 证书和私钥放进 mediasoup-demo/server/certs/
并按照这里的配置重命名。如果已经有网站域名,网站已经开了 HTTPS,且打算将 mediasoup-demo 跑在网站同一台机器上,可以将证书和私钥拷贝过来,或者用环境变量 HTTPS_CERT_FULLCHAIN
和 HTTPS_CERT_PRIVKEY
分别指向证书和私钥的路径:
export HTTPS_CERT_FULLCHAIN="XXX"
export HTTPS_CERT_PRIVKEY="YYY"
否则,可以用工具 https://github.com/aggresss/playground-cpp/blob/master/certs/autogen.sh 生成临时的自签名证书,运行这个脚本生成如下文件:
playground-cpp/certs$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: ca.crt
修改: ca.csr
修改: ca.key
修改: ca.srl
修改: client.crt
修改: client.csr
修改: client.key
修改: md5.txt
修改: server.crt
修改: server.csr
修改: server.key
server.key
是私钥,server.crt
是证书。将这两个文件拷贝到 mediasoup-demo 下:
playground-cpp/certs$ mkdir -p ~/mediasoup-demo/server/certs/
playground-cpp/certs$ mv server.key ~/mediasoup-demo/server/certs/privkey.pem
playground-cpp/certs$ mv server.crt ~/mediasoup-demo/server/certs/fullchain.pem
没有私钥和证书,运行服务器应用时会报错找不到证书:
mediasoup-demo-server:INFO running an HTTPS server... +6ms
(node:396580) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '~/mediasoup-demo/server/certs/fullchain.pem'
at Object.openSync (fs.js:462:3)
at Object.readFileSync (fs.js:364:35)
at runHttpsServer (~/mediasoup-demo/server/server.js:431:13)
at run (~/mediasoup-demo/server/server.js:74:8)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:396580) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:396580) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
运行浏览器应用时会报错找不到私钥:
[16:02:19] Finished '' after 27 ms
[16:02:19] Finished 'live' after 14 s
internal/fs/utils.js:269
throw err;
^
Error: ENOENT: no such file or directory, open '~/mediasoup-demo/server/certs/privkey.pem'
at Object.openSync (fs.js:462:3)
at Object.readFileSync (fs.js:364:35)
at getKey (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/utils.js:38:15)
at getHttpsServerDefaults (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/utils.js:45:14)
at Object.getHttpsOptions (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/utils.js:67:41)
at ~/mediasoup-demo/app/node_modules/browser-sync/dist/server/utils.js:81:44
at Object.getServer (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/utils.js:85:15)
at createServer (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/static-server.js:71:24)
at createServer (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/index.js:72:42)
at module.exports.plugin (~/mediasoup-demo/app/node_modules/browser-sync/dist/server/index.js:12:20) {
errno: -2,
syscall: 'open',
code: 'ENOENT',
path: '~/mediasoup-demo/server/certs/privkey.pem'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `gulp live`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! ~/.npm/_logs/2021-12-24T08_02_19_135Z-debug.log
还有一个必须要配置的是 RTC 传输选项中的监听 IP MEDIASOUP_LISTEN_IP
:
webRtcTransportOptions :
{
listenIps :
[
{
ip : process.env.MEDIASOUP_LISTEN_IP || '1.2.3.4',
announcedIp : process.env.MEDIASOUP_ANNOUNCED_IP
}
],
initialAvailableOutgoingBitrate : 1000000,
minimumAvailableOutgoingBitrate : 600000,
maxSctpMessageSize : 262144,
// Additional options that are not part of WebRtcTransportOptions.
maxIncomingBitrate : 1500000
},
这个选项同样既可以通过修改 config.js
配置,也可以通过设置环境变量配置。监听的 IP 地址需要设置为机器本地 IP 地址。否则,浏览器应用运行和服务器通信时,服务器将报错:
mediasoup:Router createWebRtcTransport() +3s
mediasoup:Channel request() [method:router.createWebRtcTransport, id:5] +3s
mediasoup:ERROR:Channel [pid:396733 RTC::PortManager::Bind() | throwing MediaSoupError: port bind failed due to address not available [transport:udp, ip:'1.2.3.4', port:42251, attempt:1/10000] +0ms
mediasoup:ERROR:Channel [pid:396733 Worker::OnChannelRequest() | throwing MediaSoupError: port bind failed due to address not available [transport:udp, ip:'1.2.3.4', port:42251, attempt:1/10000] [method:router.createWebRtcTransport] +4ms
mediasoup:WARN:Channel request failed [method:router.createWebRtcTransport, id:5]: [method:router.createWebRtcTransport] +0ms
mediasoup-demo-server:ERROR:Room request failed:Error: [method:router.createWebRtcTransport] at Channel.processMessage (/home/hanpfei/data/opensource/mediasoup-demo/server/node_modules/mediasoup/node/lib/Channel.js:195:37) at Socket. (/home/hanpfei/data/opensource/mediasoup-demo/server/node_modules/mediasoup/node/lib/Channel.js:69:34) at Socket.emit (events.js:315:20) at Socket.EventEmitter.emit (domain.js:483:12) at addChunk (_stream_readable.js:295:12) at readableAddChunk (_stream_readable.js:271:9) at Socket.Readable.push (_stream_readable.js:212:10) at Pipe.onStreamRead (internal/stream_base_commons.js:186:23) +0ms
mediasoup-demo-server:Room protoo Peer "close" event [peerId:ucayshrc] +27ms
mediasoup-demo-server:INFO:Room last Peer in the room left, closing the room [roomId:rvpgogc7] +3s
mediasoup-demo-server:Room close() +2ms
mediasoup:Router close() +28ms
mediasoup:Channel request() [method:router.close, id:6] +27ms
mediasoup:Transport routerClosed() +3s
mediasoup:DataProducer transportClosed() +3s
mediasoup:RtpObserver routerClosed() +3s
mediasoup:Channel request succeeded [method:router.close, id:6] +4ms
报错提示,由于 IP 地址不可用,绑定端口失败。
设置 mediasoup-demo 浏览器应用:
$ cd app
$ npm install
在终端中运行 Node.js 服务器应用:
mediasoup-demo$ cd server
mediasoup-demo/server$ npm start
在一个不同的终端中编译并运行浏览器应用程序:
mediasoup-demo$ cd app
mediasoup-demo/app$ npm start
然后就可以通过浏览器访问 mediasoup 了,如:
https://192.168.217.129:3000/?info=true&roomId=rvpgogc7
mediasoup-demo 这个运行起来之后,网络拓扑是这样的:
mediasoup-demo/server
是 WebRTC 的 SFU 服务器,mediasoup-demo/app
是客户端浏览器应用服务器,用于提供网页和 JS 文件等资源。
Npm can’t find module “semver” error in Ubuntu 19.04
mediasoup-demo 实践
https://github.com/versatica/mediasoup-demo/blob/v3/README.md
https://mediasoup.discourse.group/t/mediasouperror-port-bind-failed-due-to-address-not-available-udp-1-2-3-4-attempt-1/32/6
https://github.com/mkhahani/mediasoup-sample-app/issues/1