云计算百科
云计算领域专业知识百科平台

WebRTC 服务器之Janus视频会议插件信令交互

 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, // 房间 ID
ptype: "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
};

// 准备 PeerConnection
pluginHandle.createOffer({
media: mediaConfig,
success: function(sdp) {
// 发送 SDP Offer
pluginHandle.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

     

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » WebRTC 服务器之Janus视频会议插件信令交互
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!