?1.基礎知識回顧
WebRTC 服務器之Janus概述和環境搭建-CSDN博客
WebRTC 服務器之Janus架構分析-CSDN博客
2.插件使用流程
我們要使?janus的功能時,通常要執?以下操作:
1. 在你的??引入 Janus.js 庫,即是包含janus.js;
<!-- 網頁頭部或 body 底部引入 -->
<script src="https://your-cdn-path/janus.js"></script>
2. 初始化 Janus 庫,并傳遞其依賴項(依賴項可選);
Janus.init({debug: true, // 開啟調試日志dependencies: {// 可選依賴(如 adapter.js 用于跨瀏覽器兼容)adapter: adapter},callback: function() {console.log("Janus 庫初始化完成");// 初始化完成后連接服務器connectToJanusServer();}
});
3. 連接服務器 & 創建會話?連接到janus server并創建create?個會話session;
let janusSession;function connectToJanusServer() {const serverUrl = "wss://your-janus-server:8989/janus";new Janus({server: serverUrl,success: function(session) {janusSession = session;console.log("會話創建成功:", session.getId());// 創建插件句柄(例如加入視頻房間)attachToVideoRoomPlugin();},error: function(error) {console.error("連接失敗:", error);}});
}
4. 創建插件句柄 (Handle)?創建?個或多個handle 以attach到插件(plugin)(例如videoroom、videocall等插件);
let pluginHandle;function attachToVideoRoomPlugin() {janusSession.attach({plugin: "janus.plugin.videoroom", // 插件名稱success: function(handle) {pluginHandle = handle;console.log("成功附加到插件,句柄 ID:", handle.getId());// 發送加入房間請求joinVideoRoom();},error: function(error) {console.error("附加插件失敗:", error);}});
}
5. 信令交互示例 - 加入視頻房間?與創建交互(發送/接收消息,協商PeerConnection);
function joinVideoRoom() {const joinMsg = {request: "join",room: 1234, // 房間 IDptype: "publisher",display: "用戶1"};pluginHandle.send({ message: joinMsg });
}// 處理服務器事件(如房間通知)
pluginHandle.on("message", (msg, jsep) => {console.log("收到服務器消息:", msg);if (msg["videoroom"] === "joined") {console.log("成功加入房間!");// 處理媒體協商(如創建 Offer)startWebRTCNegotiation();}
});
6. ?WebRTC 協商流程?
function startWebRTCNegotiation() {// 創建媒體配置const mediaConfig = {audio: true,video: true,data: true};// 準備 PeerConnectionpluginHandle.createOffer({media: mediaConfig,success: function(sdp) {// 發送 SDP OfferpluginHandle.sendSDP(sdp);},error: function(error) {console.error("創建 Offer 失敗:", error);}});
}// 處理 ICE Candidate
pluginHandle.on("ice", (candidate) => {// 發送候選到服務器pluginHandle.trickle(candidate);
});
7. 最后 關閉所有的handles并關閉相關的相應的PeerConnections;?消耗destroy會話session。
function cleanup() {// 1. 銷毀插件句柄if (pluginHandle) {pluginHandle.detach();pluginHandle = null;}// 2. 銷毀會話if (janusSession) {janusSession.destroy();janusSession = null;}
}// 頁面關閉時觸發清理
window.onbeforeunload = cleanup;
?完整流程總結
-
初始化
Janus.init({ debug: true, callback: createJanusSession }); -
會話與插件
創建會話 (createSession
) → 創建插件句柄 (createHandle
) → 注冊事件監聽 (eventHandler
).? -
信令交互
發送消息 (sendMessage
) 加入房間 → 接收服務器事件 (handleEvent
). -
WebRTC 協商
創建 Offer (createOffer
) → 發送 SDP (sendSDP
) → 接收 Answer → 創建 Answer (createAnswer
). -
收發av數據
-
資源釋放
銷毀句柄 (destroyHandle
) → 銷毀會話 (destroySession
).
2.1 協議基本類型
"janus"后?的字符串代表消息的類型,主要有:
- create:創建?個Janus會話命令;
- attach:attach?個插件到Janus會話命令;
- success:?個命令的成功結果;
- error:?個命令的失敗結果;
- ack:?個命令的ack,因為不能直接返回結果,先回ack,后續的結果通過event返回;
- event:推送給客戶端的異步事件,主要由插件發出,這些事件需要插件來??定義;
- message:客戶端發給插件的消息,message和event就構成了應?協議;
- trickle:客戶端發送的candidate,會傳遞給ICE句柄;
- keepalive:客戶端發送的?跳;
- webrtcup:ICE成功建?通道的通知;
- media:?頻、視頻媒體的接收通知;
- slowlink:鏈路惡化通知;
- hangup:掛斷通知;
- detached:插件從Janus會話detach的通知,釋放了?個插件句柄。
3.videoroom插件信令交互分析
Janus中所有插件都遵循以下基本數據流程:
- 客戶端發送create創建一個Janus會話;
- Janus回復success返回Janus會話句柄;
- 客戶端發送attach命令在Janus會話上attach指定插件;
- Janus回復success返回插件的句柄;
- 客戶端給指定的插件發送message進行信令控制;
- Janus上的插件發送event通知事件給客戶端;
- 客戶端收集candidate并通過trickle消息發送給插件綁定的ICE通道;
- Janus發送webrtcup通知ICE通道建立;
- 客戶端發送媒體數據;
- Janus發送media消息通知媒體數據的第一次到達;
- Janus進行媒體數據轉發。
3.總體邏輯
?
- Janus客戶端->Janus網關: Janus客戶端發送create請求創建會話
-
JBJ-WEBSOCKET RECV: {"janus":"create","transaction":"aAMxRfsTsVVQ"}
-
- Janus網關–>>Janus客戶端: Janus網關創建會話,并返回會話IDJ
-
JBJ-WEBSOCKET responses:Sending Janus API response( {"janus": "success","transaction": "aAMxRfsTsVVQ","data": {"id": 5109637625847547} } ) to janus.transport.websockets (0x7f5cd400d3c0)
-
- Janus客戶端->Janus網關: 客戶端發送attach命令在Janus會話上attach指定插件;
-
JBJ-WEBSOCKET RECV: {"janus": "attach","plugin": "janus.plugin.videoroom","opaque_id": "videoroomtest-jIAzP2ZkPUrn","transaction": "2kuM6pzfX4Zh","session_id": 5109637625847547 }
-
- Janus網關–>>Janus客戶端:? Janus回復success返回插件的句柄;
-
JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "success","session_id": 5109637625847547,"transaction": "2kuM6pzfX4Zh","data": {"id": 6769547295474368} }) to janus.transport.websockets (0x7f5cd400d3c0)
-
- Janus客戶端->Janus網關:?Janus客戶端給指定插件發送message(Join)
-
JBJ-WEBSOCKET RECV: {"janus": "message","body": {"request": "join","room": 1234,"ptype": "publisher","display": "321"},"transaction": "11JDzIbsbK91","session_id": 5109637625847547,"handle_id": 6769547295474368 }
JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "11JDzIbsbK91" }) to janus.transport.websockets (0x7f5cd400d3c0)
-
- Janus網關–>>Janus客戶端: Janus網關處理message,并將結果通過event事件發送給客戶端
-
Preparing JSON event as a reply JBJ-video.room.event: [6769547295474368] Sending event is: {"janus": "event","session_id": 5109637625847547,"transaction": "11JDzIbsbK91","sender": 6769547295474368,"plugindata": {"plugin": "janus.plugin.videoroom","data": {"videoroom": "joined","room": 1234,"description": "Demo Room","id": 6538458198168085,"private_id": 1041514801,"publishers": []}}} to transport... Sending event to janus.transport.websockets (0x7f5cd400d3c0)>> 0 (Success)
-
- Janus客戶端->Janus網關: Janus客戶端給指定插件發送message(Configure & offer)
-
####################Configure & offer JBJ-WEBSOCKETRECV: {"janus": "message","body": {"request": "configure","audio": true,"video": true},"transaction": "MHTliRCHqY5y","jsep": {"type": "offer","sdp": "v=0\r\no=- 6239954181818903803 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS 900722df-7e7a-4951-b1bf-1902cb7fe6e0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:eHT/\r\na=ice-pwd:aAxznBSHiqTXMkZAzJs5xv3B\r\na=ice-options:trickle\r\na=fingerprint:sha-256 75:80:26:F9:EB:47:14:51:61:D3:CD:8B:60:C1:5E:F5:67:53:1E:88:9C:45:53:3B:32:D0:D2:5F:CD:45:63:7F\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=sendonly\r\na=msid:900722df-7e7a-4951-b1bf-1902cb7fe6e0 9718362e-2152-4c32-9e79-da62a8a609c5\r\na=rtcp-mux\r\na=rtcp-rsize\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:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:2665350871 cname:+3Rknr5a6Axa5msx\r\na=ssrc:2665350871 msid:900722df-7e7a-4951-b1bf-1902cb7fe6e0 9718362e-2152-4c32-9e79-da62a8a609c5\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97 103 104 107 108 109 114 115 116 117 118 39 40 45 46 98 99 100 101 119 120 123 124 125\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:eHT/\r\na=ice-pwd:aAxznBSHiqTXMkZAzJs5xv3B\r\na=ice-options:trickle\r\na=fingerprint:sha-256 75:80:26:F9:EB:47:14:51:61:D3:CD:8B:60:C1:5E:F5:67:53:1E:88:9C:45:53:3B:32:D0:D2:5F:CD:45:63:7F\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:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=extmap:6 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:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=sendonly\r\na=msid:900722df-7e7a-4951-b1bf-1902cb7fe6e0 d97d2702-4a0f-4693-8499-71c66f8c03fe\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:103 H264/90000\r\na=rtcp-fb:103 goog-remb\r\na=rtcp-fb:103 transport-cc\r\na=rtcp-fb:103 ccm fir\r\na=rtcp-fb:103 nack\r\na=rtcp-fb:103 nack pli\r\na=fmtp:103 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\na=rtpmap:104 rtx/90000\r\na=fmtp:104 apt=103\r\na=rtpmap:107 H264/90000\r\na=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=rtcp-fb:107 ccm fir\r\na=rtcp-fb:107 nack\r\na=rtcp-fb:107 nack pli\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f\r\na=rtpmap:108 rtx/90000\r\na=fmtp:108 apt=107\r\na=rtpmap:109 H264/90000\r\na=rtcp-fb:109 goog-remb\r\na=rtcp-fb:109 transport-cc\r\na=rtcp-fb:109 ccm fir\r\na=rtcp-fb:109 nack\r\na=rtcp-fb:109 nack pli\r\na=fmtp:109 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:114 rtx/90000\r\na=fmtp:114 apt=109\r\na=rtpmap:115 H264/90000\r\na=rtcp-fb:115 goog-remb\r\na=rtcp-fb:115 transport-cc\r\na=rtcp-fb:115 ccm fir\r\na=rtcp-fb:115 nack\r\na=rtcp-fb:115 nack pli\r\na=fmtp:115 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f\r\na=rtpmap:116 rt }JBJ-video.room.event: [6769547295474368] Sending event is: {"janus": "event","session_id": 5109637625847547,"transaction": "MHTliRCHqY5y","sender": 6769547295474368,"plugindata": {"plugin": "janus.plugin.videoroom","data": {"videoroom": "event","room": 1234,"configured": "ok","audio_codec": "opus","video_codec": "vp8"}},"jsep": {"type": "answer","sdp": "v=0\r\no=- 6239954181818903803 2 IN IP4 117.72.13.81\r\ns=VideoRoom 1234\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=msid-semantic: WMS janus\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 117.72.13.81\r\na=recvonly\r\na=mid:0\r\na=rtcp-mux\r\na=ice-ufrag:0pFN\r\na=ice-pwd:wGcEE+9WQLoHULcXGP8KFn\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C7:C1:43:EE:67:07:A0:6D:42:33:6A:CA:4E:EA:95:E0:55:17:03:CB:88:FD:B6:37:AB:1C:7B:85:65:9A:95:C6\r\na=setup:active\r\na=rtpmap:111 opus/48000/2\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=msid:janus janusa0\r\na=ssrc:3338353236 cname:janus\r\na=ssrc:3338353236 msid:janus janusa0\r\na=ssrc:3338353236 mslabel:janus\r\na=ssrc:3338353236 label:janusa0\r\na=candidate:1 1 udp 2013266431 117.72.13.81 53576 typ host\r\na=candidate:2 1 udp 1677722111 117.72.13.81 53576 typ srflx raddr 172.16.0.4 rport 53576\r\na=candidate:3 1 udp 503316991 172.16.0.4 54497 typ relay raddr 172.16.0.4 rport 53576\r\na=end-of-candidates\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97\r\nc=IN IP4 117.72.13.81\r\na=recvonly\r\na=mid:1\r\na=rtcp-mux\r\na=ice-ufrag:0pFN\r\na=ice-pwd:wGcEE+9WQLoHULcXGP8KFn\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C7:C1:43:EE:67:07:A0:6D:42:33:6A:CA:4E:EA:95:E0:55:17:03:CB:88:FD:B6:37:AB:1C:7B:85:65:9A:95:C6\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:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=msid:janus janusv0\r\na=ssrc:3467980833 cname:janus\r\na=ssrc:3467980833 msid:janus janusv0\r\na=ssrc:3467980833 mslabel:janus\r\na=ssrc:3467980833 label:janusv0\r\na=ssrc:3074101642 cname:janus\r\na=ssrc:3074101642 msid:janus janusv0\r\na=ssrc:3074101642 mslabel:janus\r\na=ssrc:3074101642 label:janusv0\r\na=candidate:1 1 udp 2013266431 117.72.13.81 53576 typ host\r\na=candidate:2 1 udp 1677722111 117.72.13.81 53576 typ srflx raddr 172.16.0.4 rport 53576\r\na=candidate:3 1 udp 503316991 172.16.0.4 54497 typ relay raddr 172.16.0.4 rport 53576\r\na=end-of-candidates\r\n"} }#######################trickle JBJ-WEBSOCKET RECV: {"janus": "trickle","candidate": {"candidate": "candidate:980779705 1 udp 2122260223 192.168.126.1 60475 typ host generation 0 ufrag eHT/ network-id 2","sdpMid": "0","sdpMLineIndex": 0},"transaction": "0WWSoZXbCxhA","session_id": 5109637625847547,"handle_id": 6769547295474368 }JBJ-WEBSOCKET RECV: {"janus": "trickle","candidate": {"candidate": "candidate:3993315412 1 udp 2122194687 192.168.248.1 60476 typ host generation 0 ufrag eHT/ network-id 3","sdpMid": "0","sdpMLineIndex": 0},"transaction": "DddvxlDlWuZN","session_id": 5109637625847547,"handle_id": 6769547295474368 } JBJ-WEBSOCKET RECV: {"janus": "trickle","candidate": {"candidate": "candidate:2923601174 1 udp 2122129151 172.25.0.107 60477 typ host generation 0 ufrag eHT/ network-id 1 network-cost 10","sdpMid": "0","sdpMLineIndex": 0},"transaction": "HCdvw7ihHaaZ","session_id": 5109637625847547,"handle_id": 6769547295474368 }
-
- Janus網關–>>Janus客戶端: Janus網關返回ack消息
-
JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "MHTliRCHqY5y" }) to janus.transport.websockets (0x7f5cd400d3c0)JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "0WWSoZXbCxhA" }) to janus.transport.websockets (0x7f5cd400d3c0)JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "DddvxlDlWuZN" }) to janus.transport.websockets (0x7f5cd400d3c0)
-
學習資料分享
0voice · GitHub
?