WebRTC流媒体开源服务器Janus
1. Janus介绍
Janus:通用WebRTC服务器
Janus是Meetecho开发的一个WebRTC服务器,它被认为是一个通用服务器。因此,除了实现与浏览器建立WebRTC媒体通信、与浏览器交换JSON消息以及在浏览器和它们所连接的服务器端应用程序逻辑之间中继RTP/RTCP和消息之外,它本身不提供任何功能。任何特定的特性/应用程序都是由服务器端插件提供的,浏览器可以通过Janus联系这些插件来利用它们提供的功能。此类插件的示例可以是应用程序的实现,如回声测试、会议桥、媒体记录器、SIP网关等。
2. Janus安装
我基于CentOS 7.9 环境来安装
2.1 部署环境与依赖
$ sudo yum install epel-release
$ sudo yum update
# CentOS 7.9
$ sudo yum install libmicrohttpd-devel jansson-devel \
openssl-devel glib2-devel \
opus-devel libogg-devel libcurl-devel pkgconfig gengetopt \
libconfig-devel libtool autoconf automake
# CentOS 8.3
$ sudo yum install libmicrohttpd jansson-devel \
openssl-devel glib2-devel \
opus-devel libogg-devel libcurl-devel pkgconfig gengetopt \
libconfig libtool autoconf automake
$ sudo yum install doxygen graphviz
libnice
# CentOS 7.9
$ sudo yum install meson
# CentOS 8.3
$ sudo yum install python3 python3-pip
$ sudo pip3 install meson ninja
$ git clone https://gitlab.freedesktop.org/libnice/libnice
$ cd libnice
$ meson --prefix=/usr build && ninja -C build && sudo ninja -C build install
libsrtp
$ wget https://github.com/cisco/libsrtp/archive/v2.2.0.tar.gz
$ tar xfv v2.2.0.tar.gz
$ cd libsrtp-2.2.0
$ sudo yum install lua-devel
$ ./configure --prefix=/usr --enable-openssl
$ make && sudo make install
# CentOS 8.3
$ sudo yum install libsrtp
sofia-sip-devel
$ wget https://sourceforge.net/projects/sofia-sip/files/sofia-sip/1.12.11/sofia-sip-1.12.11.tar.gz
$ tar zxf sofia-sip-1.12.11.tar.gz
$ cd sofia-sip-1.12.11
$ ./configure --prefix=/usr CFLAGS=-fno-aggressive-loop-optimizations
$ make
$ sudo make install
usrsctp
$ git clone https://github.com/sctplab/usrsctp
$ cd usrsctp
$ ./bootstrap
$ ./configure --prefix=/usr --disable-programs --disable-inet --disable-inet6
$ make
$ sudo make install
libwebsockets
$ git clone https://libwebsockets.org/repo/libwebsockets
$ cd libwebsockets
# If you want the stable version of libwebsockets, uncomment the next line
# git checkout v3.2-stable
$ mkdir build
$ cd build
# See https://github.com/meetecho/janus-gateway/issues/732 re: LWS_MAX_SMP
# See https://github.com/meetecho/janus-gateway/issues/2476 re: LWS_WITHOUT_EXTENSIONS
$ cmake -DLWS_MAX_SMP=1 -DLWS_WITHOUT_EXTENSIONS=0 -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_C_FLAGS="-fpic" ..
$ make
$ sudo make install
paho.mqtt
$ git clone https://github.com/eclipse/paho.mqtt.c.git
$ cd paho.mqtt.c
$ make && sudo make install
libmicrohttpd
$ wget https://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.59.tar.gz
$ tar -xvf libmicrohttpd-0.9.59.tar.gz
$ cd libmicrohttpd-0.9.59
$ sudo yum install gnutls-devel
$ ./configure --prefix=/usr --enable-https
$ make
$ sudo make install
rabbitmq-c
OpenSSL >= 1.1.1
$ git clone https://github.com/alanxz/rabbitmq-c
$ cd rabbitmq-c
$ git submodule init
$ git submodule update
$ mkdir build && cd build
$ sudo yum install xmlto
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make && sudo make install
2.2 编译安装
$ git clone https://github.com/meetecho/janus-gateway.git
$ cd janus-gateway
$ sh autogen.sh
$ export PKG_CONFIG_PATH=/usr/lib64/pkgconfig/:$PKG_CONFIG_PATH
$ ./configure --prefix=/opt/janus --enable-plugin-lua --disable-rabbitmq
Compiler: gcc
libsrtp version: 2.x
SSL/crypto library: OpenSSL
DTLS set-timeout: not available
Mutex implementation: GMutex (native futex on Linux)
DataChannels support: yes
Recordings post-processor: no
TURN REST API client: yes
Doxygen documentation: no
Transports:
REST (HTTP/HTTPS): yes
WebSockets: yes
RabbitMQ: no
MQTT: yes
Unix Sockets: yes
Nanomsg: no
Plugins:
Echo Test: yes
Streaming: yes
Video Call: yes
SIP Gateway: yes
NoSIP (RTP Bridge): yes
Audio Bridge: yes
Video Room: yes
Voice Mail: yes
Record&Play: yes
Text Room: yes
Lua Interpreter: no
Duktape Interpreter: no
Event handlers:
Sample event handler: yes
WebSocket ev. handler: yes
RabbitMQ event handler:no
MQTT event handler: yes
Nanomsg event handler: no
GELF event handler: yes
External loggers:
JSON file logger: no
JavaScript modules: no
$ make
$ sudo mkdir -p /opt/janus
$ sudo chown wii:wii /opt/janus -R
$ make install
# ./configure --disable-websockets --disable-data-channels --disable-rabbitmq --disable-mqtt
# ./configure --enable-docs
3. 配置与运行
<installdir>/etc/janus/janus.jcfg
$ make configs
$ /opt/janus/bin/janus --help
Janus commit: 216d70c63f1fea629daf7b3c491f06e0d9448ffd
Compiled on: 2021? 04? 12? ??? 11:05:02 CST
Usage: janus [OPTION]...
-h, --help Print help and exit
-V, --version Print version and exit
-b, --daemon Launch Janus in background as a daemon
(default=off)
-p, --pid-file=path Open the specified PID file when starting Janus
(default=none)
-N, --disable-stdout Disable stdout based logging (default=off)
--log-stdout Log to stdout, even when the process is
daemonized (default=off)
-L, --log-file=path Log to the specified file (default=stdout only)
-H, --cwd-path=path Working directory for Janus daemon process
(default=/)
-i, --interface=ipaddress Interface to use (will be the public IP)
-P, --plugins-folder=path Plugins folder (default=./plugins)
-C, --config=filename Configuration file to use
-F, --configs-folder=path Configuration files folder (default=./conf)
-c, --cert-pem=filename DTLS certificate
-k, --cert-key=filename DTLS certificate key
-K, --cert-pwd=text DTLS certificate key passphrase (if needed)
-S, --stun-server=ip:port STUN server(:port) to use, if needed (e.g.,
Janus behind NAT, default=none)
-1, --nat-1-1=ips Comma-separated list of public IPs to put in
all host candidates, assuming a 1:1 NAT is in
place (e.g., Amazon EC2 instances,
default=none)
-2, --keep-private-host When nat-1-1 is used (e.g., Amazon EC2
instances), don't remove the private host,
but keep both to simulate STUN (default=off)
-E, --ice-enforce-list=list Comma-separated list of the only interfaces to
use for ICE gathering; partial strings are
supported (e.g., eth0 or eno1,wlan0,
default=none)
-X, --ice-ignore-list=list Comma-separated list of interfaces or IP
addresses to ignore for ICE gathering;
partial strings are supported (e.g.,
vmnet8,192.168.0.1,10.0.0.1 or
vmnet,192.168., default=vmnet)
-6, --ipv6-candidates Whether to enable IPv6 candidates or not
(experimental) (default=off)
-l, --libnice-debug Whether to enable libnice debugging or not
(default=off)
-f, --full-trickle Do full-trickle instead of half-trickle
(default=off)
-I, --ice-lite Whether to enable the ICE Lite mode or not
(default=off)
-T, --ice-tcp Whether to enable ICE-TCP or not (warning: only
works with ICE Lite) (default=off)
-Q, --min-nack-queue=number Minimum size of the NACK queue (in ms) per user
for retransmissions, no matter the RTT
-t, --no-media-timer=number Time (in s) that should pass with no media
(audio or video) being received before Janus
notifies you about this
-W, --slowlink-threshold=number
Number of lost packets (per s) that should
trigger a 'slowlink' Janus API event to users
-r, --rtp-port-range=min-max Port range to use for RTP/RTCP
-B, --twcc-period=number How often (in ms) to send TWCC feedback back to
senders, if negotiated (default=200ms)
-n, --server-name=name Public name of this Janus instance
(default=MyJanusInstance)
-s, --session-timeout=number Session timeout value, in seconds (default=60)
-m, --reclaim-session-timeout=number
Reclaim session timeout value, in seconds
(default=0)
-d, --debug-level=1-7 Debug/logging level (0=disable debugging,
7=maximum debug level; default=4)
-D, --debug-timestamps Enable debug/logging timestamps (default=off)
-o, --disable-colors Disable color in the logging (default=off)
-M, --debug-locks Enable debugging of locks/mutexes (very
verbose!) (default=off)
-a, --apisecret=randomstring API secret all requests need to pass in order
to be accepted by Janus (useful when wrapping
Janus API requests in a server, none by
default)
-A, --token-auth Enable token-based authentication for all
requests (default=off)
--token-auth-secret=randomstring
Secret to verify HMAC-signed tokens with, to be
used with -A
-e, --event-handlers Enable event handlers (default=off)
-w, --no-webrtc-encryption Disable WebRTC encryption, so no DTLS or SRTP
(only for debugging!) (default=off)
<installdir>/bin/janus
错误处理
$ /opt/janus/bin/janus
# 报错找不到libpaho-mqtt3as.so.1
[ERR] [janus.c:main:5391] Couldn't load transport plugin 'libjanus_mqtt.so': libpaho-mqtt3as.so.1: cannot open shared object file: No such file or directory
# 配置路径/usr/local/lib到/etc/ld.so.conf
$ sudo ldconfig
4. 安装demo
$ openssl req -new -newkey rsa:4096 -nodes -keyout key.pem -out cert.csr
$ openssl x509 -req -sha256 -days 365 -in cert.csr -signkey key.pem -out cert.pem
$ chmod 600 cert.csr
$ chmod 600 cert.pem
$ chmod 600 key.pem
$ cd /opt/janus
$ vi etc/janus/janus.transport.http.jcfg
https = true
$ sudo yum install nginx
$ sudo vi /etc/nginx/nginx.conf
server {
# listen 80 default_server;
# listen [::]:80 default_server;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name _;
# root /usr/share/nginx/html;
root /opt/janus/share/janus/demos;
ssl_certificate "/opt/janus/share/janus/certs/cert.pem";
ssl_certificate_key "/opt/janus/share/janus/certs/cert.key";
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
index index.html index.htm index.php;
}
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
$ sudo systemctl start nginx
# 错误 Probably a network error, is the server down?: [object Object] 8089的问题,浏览器访问一下。
$ sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
$ sudo firewall-cmd --zone=public --add-port=443/tcp --permanent
$ sudo firewall-cmd --zone=public --add-port=8088/tcp --permanent
$ sudo firewall-cmd --zone=public --add-port=8089/tcp --permanent
$ sudo firewall-cmd --reload
url: https://192.168.56.110
5. janus-mobile-sdk
# Makefile中定义了依赖包的下载
$ make
# 编译 4.8.5在编译时报错,用7编译通过
$ mkdir build
$ cd build
$ cmake ../
$ make
5. 抓包分析
Echo Test 演示的是发送给服务器网关的音频和视频,服务器会回传给你。
janus_http_handler(1979): url=/janus method=POST data={
"janus": "create",
"transaction": "gVpVzKJzlO91"
}
janus_http_send_message(1159): message={
"janus": "success",
"transaction": "gVpVzKJzlO91",
"data": {
"id": 4975254805420424
}
}
Creating new session: 4975254805420424; 0x7f9fb0001320
janus_http_handler(1798): url=/janus/4975254805420424 method=GET data={
"janus": "keepalive",
"session_id": 4975254805420424,
"transaction": "z5XlM4jxvsK"
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "z5XlM4jxvsK"
}
janus_http_handler(1979): url=/janus/4975254805420424 method=POST data={
"janus": "attach",
"plugin": "janus.plugin.echotest",
"opaque_id": "echotest-Uk6M0r69TdGf",
"transaction": "TRDHLwgzs4A2",
"session_id": 4975254805420424
}
janus_http_send_message(1159): message={
"janus": "success",
"session_id": 4975254805420424,
"transaction": "TRDHLwgzs4A2",
"data": {
"id": 5490525012024748
}
}
Creating new handle in session 4975254805420424: 5490525012024748; 0x7f9fb0001320 0x7f9fb0001b10
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "message",
"body": {
"audio": true,
"video": true
},
"transaction": "90sobcSPmKXl",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "90sobcSPmKXl",
"hint": "I'm taking my time!"
}
janus_http_send_message(1159): message={
"janus": "event",
"session_id": 4975254805420424,
"transaction": "90sobcSPmKXl",
"sender": 5490525012024748,
"plugindata": {
"plugin": "janus.plugin.echotest",
"data": {
"echotest": "event",
"result": "ok"
}
}
}
janus_http_handler(1798): url=/janus/4975254805420424 method=GET data={
"janus": "keepalive",
"session_id": 4975254805420424,
"transaction": "SZTzatRlHn1"
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "SZTzatRlHn1"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "message",
"body": {
"audio": true,
"video": true
},
"transaction": "2inURksqLqGW",
"jsep": {
"type": "offer",
"sdp": "v=0\r\no=- 1298443446168106922 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1 2\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:OBYC\r\na=ice-pwd:glOqF413EKEaXEl5OKxkZoev\r\na=ice-options:trickle\r\na=fingerprint:sha-256 2E:A3:59:0D:6D:5A:65:5C:F8:12:B7:46:F5:3A:6A:72:33:BA:46:BF:03:2E:9E:2E:C2:FD:5B:52:B2:C2:85:A2\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=sendrecv\r\na=msid:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8 7ecac6bd-b288-4510-97c7-887efc669c86\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:112 telephone-event/32000\r\na=rtpmap:113 telephone-event/16000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:4085458354 cname:jnfRqpPdTG61m2cF\r\na=ssrc:4085458354 msid:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8 7ecac6bd-b288-4510-97c7-887efc669c86\r\na=ssrc:4085458354 mslabel:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8\r\na=ssrc:4085458354 label:7ecac6bd-b288-4510-97c7-887efc669c86\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:OBYC\r\na=ice-pwd:glOqF413EKEaXEl5OKxkZoev\r\na=ice-options:trickle\r\na=fingerprint:sha-256 2E:A3:59:0D:6D:5A:65:5C:F8:12:B7:46:F5:3A:6A:72:33:BA:46:BF:03:2E:9E:2E:C2:FD:5B:52:B2:C2:85:A2\r\na=setup:actpass\r\na=mid:1\r\na=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:13 urn:3gpp:video-orientation\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\na=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\na=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=sendrecv\r\na=msid:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8 aebcd30c-df7a-4e68-9376-1db7884fa47e\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:98 VP9/90000\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-fb:98 transport-cc\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=fmtp:98 profile-id=0\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=98\r\na=rtpmap:100 red/90000\r\na=rtpmap:101 rtx/90000\r\na=fmtp:101 apt=100\r\na=rtpmap:102 ulpfec/90000\r\na=ssrc-group:FID 2080201980 334907082\r\na=ssrc:2080201980 cname:jnfRqpPdTG61m2cF\r\na=ssrc:2080201980 msid:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8 aebcd30c-df7a-4e68-9376-1db7884fa47e\r\na=ssrc:2080201980 mslabel:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8\r\na=ssrc:2080201980 label:aebcd30c-df7a-4e68-9376-1db7884fa47e\r\na=ssrc:334907082 cname:jnfRqpPdTG61m2cF\r\na=ssrc:334907082 msid:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8 aebcd30c-df7a-4e68-9376-1db7884fa47e\r\na=ssrc:334907082 mslabel:bY3S0cVTCSQ1MEKrGLjFeoJZ6bvOnXpsAox8\r\na=ssrc:334907082 label:aebcd30c-df7a-4e68-9376-1db7884fa47e\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:OBYC\r\na=ice-pwd:glOqF413EKEaXEl5OKxkZoev\r\na=ice-options:trickle\r\na=fingerprint:sha-256 2E:A3:59:0D:6D:5A:65:5C:F8:12:B7:46:F5:3A:6A:72:33:BA:46:BF:03:2E:9E:2E:C2:FD:5B:52:B2:C2:85:A2\r\na=setup:actpass\r\na=mid:2\r\na=sctp-port:5000\r\na=max-message-size:262144\r\n"
},
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "2inURksqLqGW",
"hint": "I'm taking my time!"
}
[5490525012024748] Creating ICE agent (ICE Full mode, controlled)
janus_http_send_message(1159): message={
"janus": "event",
"session_id": 4975254805420424,
"transaction": "2inURksqLqGW",
"sender": 5490525012024748,
"plugindata": {
"plugin": "janus.plugin.echotest",
"data": {
"echotest": "event",
"result": "ok"
}
},
"jsep": {
"type": "answer",
"sdp": "v=0\r\no=- 1298443446168106922 2 IN IP4 10.0.2.15\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1 2\r\na=msid-semantic: WMS janus\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 10.0.2.15\r\na=sendrecv\r\na=mid:0\r\na=rtcp-mux\r\na=ice-ufrag:XX/Y\r\na=ice-pwd:0KDtjKboxthTph4TmYfpHh\r\na=ice-options:trickle\r\na=fingerprint:sha-256 5E:1D:14:4C:B8:ED:EA:3B:C8:E1:D7:C2:7E:5F:68:43:4A:2E:2B:56:0E:1D:AE:5D:34:C0:A2:D2:A2:C0:41:E4\r\na=setup:active\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 useinbandfec=1\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=msid:janus janusa0\r\na=ssrc:141121634 cname:janus\r\na=ssrc:141121634 msid:janus janusa0\r\na=ssrc:141121634 mslabel:janus\r\na=ssrc:141121634 label:janusa0\r\na=candidate:1 1 udp 2015363327 10.0.2.15 36058 typ host\r\na=candidate:2 1 udp 2015363583 192.168.56.110 54571 typ host\r\na=end-of-candidates\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97\r\nc=IN IP4 10.0.2.15\r\na=sendrecv\r\na=mid:1\r\na=rtcp-mux\r\na=ice-ufrag:XX/Y\r\na=ice-pwd:0KDtjKboxthTph4TmYfpHh\r\na=ice-options:trickle\r\na=fingerprint:sha-256 5E:1D:14:4C:B8:ED:EA:3B:C8:E1:D7:C2:7E:5F:68:43:4A:2E:2B:56:0E:1D:AE:5D:34:C0:A2:D2:A2:C0:41:E4\r\na=setup:active\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=extmap:13 urn:3gpp:video-orientation\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=ssrc-group:FID 2027724451 3485931207\r\na=msid:janus janusv0\r\na=ssrc:2027724451 cname:janus\r\na=ssrc:2027724451 msid:janus janusv0\r\na=ssrc:2027724451 mslabel:janus\r\na=ssrc:2027724451 label:janusv0\r\na=ssrc:3485931207 cname:janus\r\na=ssrc:3485931207 msid:janus janusv0\r\na=ssrc:3485931207 mslabel:janus\r\na=ssrc:3485931207 label:janusv0\r\na=candidate:1 1 udp 2015363327 10.0.2.15 36058 typ host\r\na=candidate:2 1 udp 2015363583 192.168.56.110 54571 typ host\r\na=end-of-candidates\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 10.0.2.15\r\na=sendrecv\r\na=sctp-port:5000\r\na=mid:2\r\na=ice-ufrag:XX/Y\r\na=ice-pwd:0KDtjKboxthTph4TmYfpHh\r\na=ice-options:trickle\r\na=fingerprint:sha-256 5E:1D:14:4C:B8:ED:EA:3B:C8:E1:D7:C2:7E:5F:68:43:4A:2E:2B:56:0E:1D:AE:5D:34:C0:A2:D2:A2:C0:41:E4\r\na=setup:active\r\na=candidate:1 1 udp 2015363327 10.0.2.15 36058 typ host\r\na=candidate:2 1 udp 2015363583 192.168.56.110 54571 typ host\r\na=end-of-candidates\r\n"
}
}
janus_http_handler(1798): url=/janus/4975254805420424 method=GET data={
"janus": "keepalive",
"session_id": 4975254805420424,
"transaction": "1Vz9OOAz2Re"
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "1Vz9OOAz2Re"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:2999745851 1 udp 2122194687 192.168.56.1 55007 typ host generation 0 ufrag OBYC network-id 2",
"sdpMid": "0",
"sdpMLineIndex": 0
},
"transaction": "ucnZdHetH1U5",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "ucnZdHetH1U5"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:3304467501 1 udp 2122260223 192.168.1.71 54297 typ host generation 0 ufrag OBYC network-id 1",
"sdpMid": "1",
"sdpMLineIndex": 1
},
"transaction": "UgUvMHjiuvOB",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "UgUvMHjiuvOB"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:2999745851 1 udp 2122194687 192.168.56.1 48592 typ host generation 0 ufrag OBYC network-id 2",
"sdpMid": "2",
"sdpMLineIndex": 2
},
"transaction": "bU8EW4N0FDhz",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "bU8EW4N0FDhz"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:3304467501 1 udp 2122260223 192.168.1.71 42957 typ host generation 0 ufrag OBYC network-id 1",
"sdpMid": "0",
"sdpMLineIndex": 0
},
"transaction": "d6KUaFs8hwgt",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "d6KUaFs8hwgt"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:2999745851 1 udp 2122194687 192.168.56.1 37981 typ host generation 0 ufrag OBYC network-id 2",
"sdpMid": "1",
"sdpMLineIndex": 1
},
"transaction": "6RmMZX6GIH2D",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "6RmMZX6GIH2D"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:2322976989 1 tcp 1518280447 192.168.1.71 9 typ host tcptype active generation 0 ufrag OBYC network-id 1",
"sdpMid": "0",
"sdpMLineIndex": 0
},
"transaction": "8LNCIXu22B82",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "8LNCIXu22B82"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:4233069003 1 tcp 1518214911 192.168.56.1 9 typ host tcptype active generation 0 ufrag OBYC network-id 2",
"sdpMid": "0",
"sdpMLineIndex": 0
},
"transaction": "E9Z8uurLdMXh",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "E9Z8uurLdMXh"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:3304467501 1 udp 2122260223 192.168.1.71 34860 typ host generation 0 ufrag OBYC network-id 1",
"sdpMid": "2",
"sdpMLineIndex": 2
},
"transaction": "ZMVPHzJ6xxpE",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "ZMVPHzJ6xxpE"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"candidate": "candidate:853956089 1 udp 1686052607 111.197.112.16 14710 typ srflx raddr 192.168.1.71 rport 42957 generation 0 ufrag OBYC network-id 1",
"sdpMid": "0",
"sdpMLineIndex": 0
},
"transaction": "YfKAdIvNaSLW",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "YfKAdIvNaSLW"
}
janus_http_handler(1979): url=/janus/4975254805420424/5490525012024748 method=POST data={
"janus": "trickle",
"candidate": {
"completed": true
},
"transaction": "ZSzNu1IgG124",
"session_id": 4975254805420424,
"handle_id": 5490525012024748
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "ZSzNu1IgG124"
}
janus_http_send_message(1159): message={
"janus": "webrtcup",
"session_id": 4975254805420424,
"sender": 5490525012024748
}
[5490525012024748] The DTLS handshake has been completed
[janus.plugin.echotest-0x7f9fb0001650] WebRTC media is now available
janus_http_handler(1798): url=/janus/4975254805420424 method=GET data={
"janus": "keepalive",
"session_id": 4975254805420424,
"transaction": "9RXXrLweqvj"
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "9RXXrLweqvj"
}
janus_http_send_message(1159): message={
"janus": "media",
"session_id": 4975254805420424,
"sender": 5490525012024748,
"type": "audio",
"receiving": true
}
janus_http_handler(1798): url=/janus/4975254805420424 method=GET data={
"janus": "keepalive",
"session_id": 4975254805420424,
"transaction": "nIW8K8sosKC"
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "nIW8K8sosKC"
}
janus_http_send_message(1159): message={
"janus": "media",
"session_id": 4975254805420424,
"sender": 5490525012024748,
"type": "video",
"receiving": true
}
janus_http_handler(1798): url=/janus/4975254805420424 method=GET data={
"janus": "keepalive",
"session_id": 4975254805420424,
"transaction": "HbMuqSQtaqn"
}
janus_http_send_message(1159): message={
"janus": "ack",
"session_id": 4975254805420424,
"transaction": "HbMuqSQtaqn"
}
janus_http_handler(1979): url=/janus/4975254805420424 method=POST data={
"janus": "destroy",
"transaction": "9xiRaxbm1cU7",
"session_id": 4975254805420424
}
janus_http_send_message(1159): message={
"janus": "success",
"session_id": 4975254805420424,
"transaction": "9xiRaxbm1cU7"
}
Destroying session 4975254805420424; 0x7f9fb0001320
Detaching handle from JANUS EchoTest plugin; 0x7f9fb0001b10 0x7f9fb0001650 0x7f9fb0001b10 0x7f9fb0001760
[janus.plugin.echotest-0x7f9fb0001650] No WebRTC media anymore
[5490525012024748] WebRTC resources freed; 0x7f9fb0001b10 0x7f9fb0001320
[5490525012024748] Handle and related resources freed; 0x7f9fb0001b10 0x7f9fb0001320
6. 源码分析
6.1 在 Janus 中默认支持的插件包括以下几个
- SIP:这个插件使得 Janus 成了 SIP 用户的代理,从而允许 WebRTC 终端在 SIP 服务器(如 Asterisk)上注册,并向 SIP 服务器发送或接收音视频流。
- TextRoom:该插件使用 DataChannel 实现了一个文本聊天室应用。
- Streaming:它允许 WebRTC 终端观看 / 收听由其他工具生成的预先录制的文件或媒体。
- VideoRoom:它实现了视频会议的 SFU 服务,实际就是一个音 / 视频路由器。
- VideoCall:这是一个简单的视频呼叫的应用,允许两个 WebRTC 终端相互通信,它与 WebRTC 官网的例子相似(https://apprtc.appspot.com),不同点是这个插件要经过服务端进行音视频流中转,而 WebRTC 官网的例子走的是 P2P 直连。
- RecordPlay:该插件有两个功能,一是将发送给 WebRTC 的数据录制下来,二是可以通过 WebRTC 进行回放。