当前位置: 首页 > news >正文

janus客户端源码分析

1 客户端源码框架

一、流程总览

网页(HTML) janus.js 库 Janus 服务端 插件(Videoroom/Videocall 等) 加载 janus.js(<script src="janus.js">) Janus.init({ dependencies: ... }) Janus.create({ server: "ws://..." }) 发送 create 信令,建立会话 janus.attach({ plugin: "videoroom" }) 发送 attach 信令,绑定插件 handle.send({ message: ... }) // 信令交互 转发信令(如 join 房间、publish 流) 协商 WebRTC(PeerConnection) handle.destroy() // 关闭 Handle 发送 detach 信令 关闭 RTCPeerConnection janus.destroy() // 销毁会话 发送 destroy 信令 网页(HTML) janus.js 库 Janus 服务端 插件(Videoroom/Videocall 等)

二、分步详细解释

  1. 在网页中包含 janus.js
  • 作用:引入 Janus 客户端核心库,提供与 Janus 服务端交互的 API(如 Janus.create()handle.send() 等)。
  • 代码示例
    <script src="janus.js"></script> <!-- 加载 Janus 客户端库 -->
    
  • 关键janus.js 封装了 WebSocket 通信、信令处理、WebRTC 协商等底层逻辑,让开发者无需关注复杂细节。
  1. 初始化 janus.js 库
  • 作用:配置 Janus 客户端的依赖(如 WebRTC polyfill、日志级别),确保浏览器兼容性。
  • 代码示例
    Janus.init({dependencies: {// 可选:加载 WebRTC polyfill(旧浏览器兼容)webrtcAdapter: "/path/to/adapter.js"},debug: "all" // 开启调试日志,方便排查问题
    });
    
  • 关键dependencies 可按需加载依赖(如 adapter.js 处理浏览器 WebRTC 差异),debug 控制日志输出(开发阶段建议开启)。
  1. 连接 Janus Server 并创建会话(Session)
  • 作用:与 Janus 服务端建立长连接(WebSocket/HTTP),生成唯一 session_id 标识通信上下文。
  • 代码示例
    Janus.create({server: "ws://your-janus-server:8188", // Janus 服务端地址success: (janus) => {console.log("会话创建成功,session_id:", janus.getSessionId());// 后续操作(绑定插件、交互)},error: (error) => {console.error("会话创建失败:", error);}
    });
    
  • 关键
    • server 指定 Janus 服务端的 WebSocket 地址(默认端口 8188)。
    • success 回调返回 janus 实例,用于后续操作(绑定插件、发送信令)。
  1. 创建 Handle 并绑定插件(Plugin)
  • 作用:通过 attach 操作绑定具体插件(如 videoroom 实现视频房间、videocall 实现一对一通话),生成 handle_id 标识插件实例。
  • 代码示例(绑定 Videoroom 插件)
    janus.attach({plugin: "janus.plugin.videoroom", // 插件名称success: (handle) => {console.log("插件绑定成功,handle_id:", handle.getId());// 后续操作(加入房间、发布流)},error: (error) => {console.error("插件绑定失败:", error);}
    });
    
  • 关键
    • 一个 session 可绑定多个 handle(如同时使用 videoroomaudiobridge 插件)。
    • handle 是与插件交互的核心对象(发送信令、协商 WebRTC)。
  1. 与插件交互(信令 + PeerConnection)
  • 作用:通过 handle.send() 发送信令(如 join 房间、publish 流),并通过 WebRTC 协商(RTCPeerConnection)传输音视频数据。
  • 代码示例(加入视频房间并发布流)
    // 加入房间(作为发布者)
    handle.send({message: {request: "join",room: 1234, // 房间 IDptype: "publisher", // 角色:发布者display: "MyPublisher" // 显示名称},success: (response) => {console.log("加入房间成功:", response);// 发布音视频流navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then((stream) => {// 协商 WebRTC(创建 Offer)handle.createOffer({media: { audio: true, video: true },stream: stream,success: (jsep) => {// 发送 Offer 给 Janus 服务端handle.send({message: { request: "publish" },jsep: jsep});}});});}
    });
    
  • 关键
    • 信令(如 join/publish)通过 handle.send() 发送,由插件处理业务逻辑(如房间管理、媒体转发)。
    • createOffer/createAnswer 触发 WebRTC 协商,建立 PeerConnection 传输音视频流。
  1. 关闭 Handles 和 PeerConnection
  • 作用:释放资源(关闭 WebRTC 连接、通知服务端销毁插件实例),避免内存泄漏。
  • 代码示例
    // 关闭 Handle(解绑插件)
    handle.destroy({success: () => {console.log("Handle 已关闭");// 关闭 PeerConnection(可选,Janus.js 会自动处理)handle.getPeerConnection().close();}
    });
    
  • 关键
    • handle.destroy() 会发送 detach 信令给服务端,销毁插件实例。
    • 需确保所有 handle 都关闭后,再销毁会话。
  1. 销毁会话(Session)
  • 作用:通知 Janus 服务端销毁会话,释放服务端资源(如WebSocket连接、会话上下文)。
  • 代码示例
    janus.destroy({success: () => {console.log("会话已销毁");}
    });
    
  • 关键:会话销毁后,无法再通过 janus 实例进行任何操作,需重新创建会话。

总结
使用 Janus 的核心流程是 “引入库 → 初始化 → 建会话 → 绑插件 → 交互 → 关资源 → 毁会话”,每个步骤都围绕 “控制信令(WebSocket)” 和 “媒体流(WebRTC)” 两大通道展开。

通过 janus.js 封装的 API,开发者无需关注底层细节,只需按流程调用 create()attach()send() 等方法,即可实现复杂的音视频交互(如视频会议、直播)。

2 API

一、Session 相关 API(会话生命周期)

1. createSession(创建会话)

  • 作用
    与 Janus 服务端建立 WebSocket/HTTP 连接,创建全局会话(janus_session),生成唯一 session_id
  • 调用时机
    应用初始化时,首次连接 Janus 服务端。
  • 关联代码(简化)
    Janus.create({server: "ws://janus-server:8188", // 服务端地址success: (janus) => { /* 会话创建成功,返回 janus 实例 */ },error: (error) => { /* 处理错误 */ }
    });
    
  • 关键逻辑
    • 建立 WebSocket 长连接,监听 message 事件。
    • 发送 create 信令,服务端返回 session_id

2. destroySession(销毁会话)

  • 作用
    通知 Janus 服务端销毁会话,关闭 WebSocket 连接,释放服务端资源。
  • 调用时机
    应用退出、用户登出或会话不再使用时。
  • 关联代码(简化)
    janus.destroy({success: () => { /* 会话销毁成功 */ },error: (error) => { /* 处理错误 */ }
    });
    
  • 关键逻辑
    • 发送 destroy 信令给服务端。
    • 关闭 WebSocket 连接,清理客户端 pendingRequests

二、Plugin 相关 API(插件生命周期)

1. createHandle(绑定插件)

  • 作用
    通过 attach 信令绑定 Janus 插件(如 videoroom),生成 handle_id,创建插件句柄(PluginHandle)。
  • 调用时机
    会话创建后,需要使用具体插件功能时(如加入视频房间)。
  • 关联代码(简化)
    janus.attach({plugin: "janus.plugin.videoroom", // 插件名称success: (handle) => { /* 返回 PluginHandle 实例 */ },
    });
    
  • 关键逻辑
    • 发送 attach 信令,服务端返回 handle_id
    • 创建 PluginHandle 实例,管理插件交互。

2. destroyHandle(解绑插件)

  • 作用
    通知 Janus 服务端销毁插件句柄,关闭关联的 WebRTC 连接,释放资源。
  • 调用时机
    插件功能使用完毕(如退出视频房间)。
  • 关联代码(简化)
    handle.destroy({success: () => { /* 插件句柄销毁成功 */ },
    });
    
  • 关键逻辑
    • 发送 detach 信令给服务端。
    • 关闭关联的 RTCPeerConnection,清理插件状态。

三、WebRTC 相关 API(媒体协商)

1. prepareWebrtc(初始化 WebRTC 环境)

  • 作用
    初始化 WebRTC 相关资源(如 RTCPeerConnection、ICE 服务器配置),为媒体协商做准备。
  • 调用时机
    绑定插件后,需要传输媒体流时(如发布/订阅流)。
  • 关联代码(简化)
    PluginHandle.prototype.prepareWebrtc = function(options) {this.pc = new RTCPeerConnection({ iceServers: options.iceServers });this.pc.onicecandidate = (event) => { /* 处理 ICE 候选 */ };this.pc.ontrack = (event) => { /* 处理媒体流 */ };
    };
    
  • 关键逻辑
    • 创建 RTCPeerConnection,配置 ICE 服务器(STUN/TURN)。
    • 监听 icecandidate(候选交换)和 ontrack(媒体流接收)事件。

2. prepareWebrtcPeer(准备 Peer 连接)

  • 作用
    为插件句柄关联 RTCPeerConnection,确保信令与媒体通道绑定。
  • 调用时机
    prepareWebrtc 之后,创建 Offer/Answer 之前。
  • 关联代码(简化)
    PluginHandle.prototype.prepareWebrtcPeer = function() {this.webrtcStuff = { pc: this.pc }; // 关联 PeerConnection
    };
    
  • 关键逻辑
    • RTCPeerConnection 存入插件句柄的 webrtcStuff,方便后续操作(如 createOffer)。

3. createOffer(创建 SDP Offer)

  • 作用
    生成 WebRTC 的 SDP Offer,触发媒体协商流程(发布者视角)。
  • 调用时机
    发布者需要推流时(如加入房间后调用 publish)。
  • 关联代码(简化)
    PluginHandle.prototype.createOffer = function(options, success, error) {this.pc.createOffer().then((offer) => {this.pc.setLocalDescription(offer);success(offer); // 返回 SDP Offer}).catch(error);
    };
    
  • 关键逻辑
    • 调用 pc.createOffer() 生成 SDP。
    • 设置本地描述,通过 success 回调返回 Offer。

4. createAnswer(创建 SDP Answer)

  • 作用
    生成 WebRTC 的 SDP Answer,响应发布者的 Offer(订阅者视角)。
  • 调用时机
    订阅者收到 SDP Offer 后,需要回复 Answer 时。
  • 关联代码(简化)
    PluginHandle.prototype.createAnswer = function(options, success, error) {this.pc.createAnswer().then((answer) => {this.pc.setLocalDescription(answer);success(answer); // 返回 SDP Answer}).catch(error);
    };
    
  • 关键逻辑
    • 调用 pc.createAnswer() 生成 SDP。
    • 设置本地描述,通过 success 回调返回 Answer。

5. sendSDP(发送 SDP 信令)

  • 作用
    将 SDP Offer/Answer 封装为 jsep 信令,发送给 Janus 服务端,完成媒体协商。
  • 调用时机
    createOffer/createAnswer 后,需要将 SDP 发送给服务端时。
  • 关联代码(简化)
    PluginHandle.prototype.sendSDP = function(jsep, success, error) {this.send({message: { request: "start" }, // 或 publish/join 等jsep: jsep,success: success,error: error});
    };
    
  • 关键逻辑
    • 将 SDP 放入 jsep 字段,通过 handle.send() 发送信令。
    • 服务端转发 SDP 给对端(发布者/订阅者)。

6. streamDone(媒体流结束处理)

  • 作用
    处理媒体流结束事件(如用户挂断、流停止),关闭 PeerConnection 并通知服务端。
  • 调用时机
    检测到 RTCPeerConnectiononended 事件,或主动停止流时。
  • 关联代码(简化)
    PluginHandle.prototype.streamDone = function() {this.pc.close(); // 关闭 PeerConnectionthis.send({ message: { request: "unpublish" } }); // 通知服务端停止转发
    };
    
  • 关键逻辑
    • 关闭 RTCPeerConnection,释放媒体资源。
    • 发送 unpublish 信令,通知 Janus 服务端停止流转发。

Offer:由发起连接的一方(通常是发布者) 生成的 SDP 提议,包含自己支持的媒体参数,发给对端(订阅者或 Janus 服务端)协商。

3 信令交互

在这里插入图片描述

服务端

一、信令处理的核心流程(分层视角)

  1. 传输层:接收信令(WebSocket/HTTP)
    Janus 支持 WebSocket(实时信令)和 HTTP(非实时信令)传输,所有客户端信令(如 create/attach/message )先由传输层接收:
  • WebSocket:基于 libwebsockets 库,建立长连接后,客户端与 Janus 实时双向通信。
  • HTTP:基于 libmicrohttpd 库,客户端通过 POST 请求发信令,Janus 同步返回响应。

代码映射(Janus 服务端):

// 传输层回调,统一接收信令
static janus_transport_callbacks janus_handler_transport = {.transport_gone = janus_transport_gone,.notify_event = janus_transport_notify_event,.incoming_request = janus_transport_incoming_request  // 核心回调
};// 信令入站时,触发此函数
void janus_transport_incoming_request(janus_transport *plugin, janus_transport_session *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {// 将信令放入请求队列,等待处理g_async_queue_push(requests, request); 
}
  1. Janus Core:分发与初步处理
    Janus 核心层从队列中取出信令,根据信令类型(create/attach/message 等)分发到对应模块:
  • create 信令:创建全局会话(janus_session ),分配 session_id
  • attach 信令:绑定插件(如 videoroom ),创建插件句柄(janus_plugin_session ),分配 handle_id
  • message 信令:转发给已绑定的插件处理(需携带 handle_id 标识插件实例 )。

核心逻辑(Janus 插件框架):

// 信令处理线程从队列取信令
janus_request *request = g_async_queue_pop(requests);
json_t *message = request->message;
const char *janus_type = json_string_value(json_object_get(message, "janus"));if (g_strcmp0(janus_type, "create") == 0) {// 创建会话逻辑janus_session *session = janus_session_create();json_t *reply = json_pack("{s:s, s:s, s:i}", "janus", "success", "id", session->id);janus_transport_send_response(request->transport, request->transport_session, reply);
} else if (g_strcmp0(janus_type, "attach") == 0) {// 绑定插件逻辑janus_plugin *plugin = janus_plugin_find(json_string_value(json_object_get(message, "plugin")));janus_plugin_session *plugin_session = janus_plugin_session_create(session, plugin);json_t *reply = json_pack("{s:s, s:s, s:i}", "janus", "success", "id", plugin_session->id);janus_transport_send_response(request->transport, request->transport_session, reply);
}
  1. 插件层:业务逻辑处理
    插件(如 videoroom/videocall )是信令处理的“业务层”,根据信令内容执行具体操作(如房间管理、媒体协商 ):
  • join 信令(videoroom 插件):验证房间 ID,记录发布者/订阅者身份,返回房间内其他用户信息。
  • publish 信令(videoroom 插件):触发 WebRTC 媒体协商,生成 SDP Offer 并转发给订阅者。

插件处理示例videoroom 插件伪代码):

// 插件接收 message 信令
void janus_videoroom_handle_message(janus_plugin_session *plugin_session, json_t *message) {const char *request = json_string_value(json_object_get(message, "request"));if (g_strcmp0(request, "join") == 0) {// 解析参数:room、ptype(publisher/subscriber)、feed 等int room = json_integer_value(json_object_get(message, "room"));const char *ptype = json_string_value(json_object_get(message, "ptype"));// 业务逻辑:加入房间,记录角色janus_videoroom_room *room = janus_videoroom_find_room(room);janus_videoroom_participant *participant = janus_videoroom_add_participant(room, ptype);// 回复 event 信令,通知客户端加入成功json_t *event = json_pack("{s:s, s:i, s:o}", "janus", "event", "room", room->id, "participants", janus_videoroom_get_participants(room));janus_plugin_send_event(plugin_session, event);}
}
  1. 响应与异步通知
    处理完成后,Janus 通过以下方式回复客户端:
  • 同步响应:如 create/attach 信令,直接返回 success 响应(含 session_id/handle_id )。
  • 异步事件:如 join/publish 信令,通过 event 消息异步通知(如房间状态变更、媒体协商结果 )。

客户端接收示例(Janus.js):

janusSession.attach({plugin: "janus.plugin.videoroom",success: (pluginHandle) => {// 绑定插件成功,获取 handle_idpluginHandle.on("message", (msg, jsep) => {// 处理异步 event 信令if (msg.janus === "event" && msg.plugindata.data.request === "joined") {console.log("加入房间成功:", msg);}});}
});

二、典型信令交互时序(以 videoroom 为例)
以下是“客户端加入视频房间”的完整信令流程,体现 Janus 分层处理逻辑:

阶段客户端信令(Janus.js)Janus 处理逻辑服务端响应/事件
创建会话Janus.create({ server: "ws://..." })核心层创建 janus_session,分配 session_id返回 success(含 session_id
绑定插件session.attach({ plugin: "videoroom" })核心层绑定 videoroom 插件,分配 handle_id返回 success(含 handle_id
加入房间handle.send({ request: "join", room: 123, ptype: "publisher" })插件层验证房间,记录发布者身份返回 event(含房间参与者信息 )
媒体协商handle.createOffer() 生成 SDP Offer插件层转发 SDP Offer 给订阅者,触发协商返回 event(含 SDP Answer )
ICE 候选交换handle.trickle(candidate) 发送候选核心层转发 ICE 候选到插件返回 ack 确认接收
连接建立-核心层检测 ICE 连接状态返回 webrtcup 事件(连接建立 )
媒体传输-插件层转发音视频流返回 media 事件(媒体数据到达 )

三、关键设计:插件化与异步事件

  1. 插件化解耦
    Janus 核心不处理具体业务,通过插件(如 videoroom )实现音视频房间、通话等功能。不同插件可独立开发、动态加载,降低耦合性。

  2. 异步事件驱动
    除了简单的“请求-响应”(如 create/attach ),复杂操作(如媒体协商、房间状态变更 )通过 event 异步通知客户端,避免阻塞信令通道。


四、总结:Janus 信令处理的本质
Janus 处理信令的核心逻辑是 “分层转发 + 插件业务处理”

  • 传输层负责“收发包”,核心层负责“会话/插件管理”,插件层负责“业务逻辑”。
  • 信令类型(create/attach/message )决定了处理流程,异步事件(event )实现复杂交互。

客户端

一、客户端消息处理核心流程(闭环图)

浏览器/JS janus.js Janus 服务端 WebSocket/HTTP 连接 new Janus({ server: "ws://..." }) 建立长连接,监听 `message` 事件 收到服务端消息时,触发 `message` 事件 调用 `handleEvent(json)` 处理消息 根据 `json.janus` 类型分支处理(ack/success/event 等) 通过 `pluginHandle.send()` 或 `janus.send()` 发消息 调用 `sendMessage(json)` 构造请求 通过 WebSocket 发送 JSON 信令 服务端返回响应消息 再次触发 `handleEvent` 处理响应 浏览器/JS janus.js Janus 服务端 WebSocket/HTTP 连接

二、客户端接收消息:从 WebSockethandleEvent

  1. WebSocket 监听与触发
    janus.js 在创建 Session 时,会为 WebSocket 连接绑定 message 事件监听:
// janus.js 核心代码(简化)
Janus.prototype.init = function() {this.websocket = new WebSocket(this.server);this.websocket.onmessage = (event) => {const json = JSON.parse(event.data);this.handleEvent(json); // 收到消息后,调用 handleEvent 处理};
};
  1. handleEvent 分支处理(核心逻辑)
    handleEvent 是消息处理的“分发中心”,根据 json.janus 字段区分消息类型,处理逻辑如下:
json.janus 类型处理逻辑关联流程
keepalive心跳消息,重置超时定时器(this.resetTimeout()保活机制
ack确认消息,标记客户端发送的请求已被服务端接收(this.resolveRequest()可靠性保证(请求-确认)
success操作成功响应,标记请求完成(this.resolveRequest()),返回业务数据create/attach 成功响应
trickleICE 候选消息,解析 candidate 并交给 RTCPeerConnection 处理媒体协商(WebRTC)
webrtcupPeer 连接已建立,触发 pluginHandlewebrtcup 事件媒体流开始传输
hangup用户挂断,通知 pluginHandle 执行挂断逻辑(如关闭 RTCPeerConnection通话终止
detached插件与 Janus Core 断开,清理 pluginHandle 资源插件解绑
media媒体流状态变更(开始/停止),通知业务层更新 UI(如显示“对方关闭摄像头”)媒体状态同步
slowlink网络质量检测,通知业务层调整码率/分辨率(需业务实现)弱网适配
error错误响应,标记请求失败(this.rejectRequest()),传递错误信息异常处理
event插件业务事件(如房间用户加入/退出),交给 pluginHandle 处理业务逻辑(如视频会议房间状态)
timeout超时响应,重试或清理资源容错机制
  1. pluginHandle 的交互
    对于插件相关消息(如 event/webrtcup ),handleEvent 会调用 pluginHandlehandleMessage 方法,让插件实例处理业务逻辑:
// janus.js 中 handleEvent 处理 `event` 消息的逻辑(简化)
if (json.janus === "event") {const pluginHandle = this.findPluginHandle(json.session_id, json.handle_id);if (pluginHandle) {pluginHandle.handleMessage(json, jsep); // 交给插件句柄处理}
}

pluginHandle 进一步解析 plugindata 字段,区分具体插件(如 videoroom/audiobridge )并触发业务事件:

// PluginHandle.prototype.handleMessage(简化)
handleMessage(json, jsep) {const plugindata = json.plugindata;if (plugindata.plugin === "janus.plugin.videoroom") {this.emit("roomEvent", plugindata.data, jsep); // 触发 videoroom 业务事件}this.emit("message", json, jsep); // 通用消息事件
}

三、客户端发送消息:从 send() 到服务端

  1. 发送消息的封装(sendMessage
    janus.js 为上层应用封装了 send() 方法(实际调用 sendMessage ),负责构造 JSON 信令并通过 WebSocket 发送:
// janus.js 核心发送逻辑(简化)
Janus.prototype.sendMessage = function(json) {const transaction = this.generateTransaction(); // 生成唯一事务 IDjson.transaction = transaction; // 标记请求this.pendingRequests[transaction] = { // 记录待响应的请求timeout: setTimeout(() => this.handleTimeout(transaction), this.timeout),resolve: null,reject: null};this.websocket.send(JSON.stringify(json)); // 通过 WebSocket 发送return new Promise((resolve, reject) => {this.pendingRequests[transaction].resolve = resolve;this.pendingRequests[transaction].reject = reject;});
};

2. 典型发送场景

  • 创建 Session

    janus.sendMessage({ janus: "create" }).then((response) => {console.log("Session 创建成功,session_id:", response.id);
    });
    
  • 绑定插件(attach

    janus.sendMessage({ janus: "attach", session_id: "xxx", plugin: "janus.plugin.videoroom" 
    }).then((response) => {const pluginHandle = new PluginHandle(janus, response.id);console.log("插件绑定成功,handle_id:", pluginHandle.id);
    });
    
  • 加入房间(join

    pluginHandle.sendMessage({ janus: "message", handle_id: "yyy", body: { request: "join", room: 123, ptype: "publisher" } 
    }).then((response) => {console.log("加入房间成功:", response);
    });
    

3. 请求-响应闭环

发送消息时,janus.js 会记录 transaction(事务 ID),服务端返回响应时,handleEvent 通过 transaction 找到对应的 Promise 并决议(resolve/reject ):

// handleEvent 处理 success 消息时(简化)
if (json.janus === "success") {const request = this.pendingRequests[json.transaction];if (request) {request.resolve(json); // 决议 Promise,返回响应clearTimeout(request.timeout);delete this.pendingRequests[json.transaction];}
}

四、关键设计:可靠性与插件解耦

  1. 可靠性保证(ack/success

    • ack 确保服务端已收到请求(但未处理完成)。
    • success 确保服务端已处理完成(返回业务结果)。
    • 客户端通过 Promise 等待 success,保证操作原子性。
  2. 插件解耦

    • 会话(Janus)与插件句柄(PluginHandle )分离,不同插件的消息互不干扰。
    • 业务层只需监听 pluginHandle 的事件(如 roomEvent ),无需关心底层信令。
  3. 自动重连与保活

    • keepalive 心跳消息自动发送,维持长连接。
    • 超时(timeout )时自动重试或清理资源,保证健壮性。

五、总结:客户端收发消息的本质
Janus 客户端收发消息的核心是 “基于 WebSocket 的请求-响应闭环 + 插件化事件驱动”

  • 接收:WebSocket 监听 message 事件 → handleEvent 分发消息 → 插件句柄处理业务事件。
  • 发送:上层调用 send()sendMessage 构造信令 → WebSocket 发送 → 等待服务端响应并决议 Promise。

4 Janus音视频会话建立全流程:

一、流程总览:信令与媒体的协同逻辑
Janus的会话建立遵循 “信令协商先行,媒体流传输在后” 的设计,核心分为五大阶段:

每个阶段通过JSON信令交互完成,最终实现音视频数据的实时传输。

在这里插入图片描述

二、分步拆解:从信令到媒体流的每一步

  1. 阶段1:会话创建——建立基础连接
  • 客户端动作:发送create信令,请求初始化会话。
    {"janus": "create","transaction": "Yddh20mU3s"
    }
    
  • 服务端响应:返回success信令,分配唯一session_id(如714784477113709)。
    {"janus": "success","transaction": "Yddh20mU3s","data": { "id": 714784477113709 }
    }
    
  • 关键作用:生成会话唯一标识,建立客户端与Janus服务端的基础通信上下文。

2. 阶段2:插件绑定——关联业务功能

  • 客户端动作:发送attach信令,绑定videoroom插件(视频房间业务)。
    {"janus": "attach","plugin": "janus.plugin.videoroom","session_id": 714784477113709,"transaction": "ZxL7Jf7W1U"
    }
    
  • 服务端响应:返回success信令,分配handle_id(如7668325947218508)。
    {"janus": "success","session_id": 714784477113709,"handle_id": 7668325947218508,"transaction": "ZxL7Jf7W1U"
    }
    
  • 关键作用:将“视频房间”业务逻辑与会话关联,后续操作通过handle_id执行。

3. 阶段3:房间加入——声明身份与状态同步

  • 客户端动作:发送message信令,以发布者身份加入房间(ptype: "publisher")。
    {"janus": "message","session_id": 714784477113709,"handle_id": 7668325947218508,"transaction": "AbDmSrm3T","body": {"request": "join","room": 1234,"ptype": "publisher","display": "Ben"}
    }
    
  • 服务端响应
    • 先返回ack信令(确认收到请求)。
      {"janus": "ack","session_id": 714784477113709,"transaction": "AbDmSrm3T"
      }
      
    • 再返回event信令,同步房间状态(如房间ID、当前发布者列表)。
      {"janus": "event","session_id": 714784477113709,"handle_id": 7668325947218508,"plugindata": {"plugin": "janus.plugin.videoroom","data": {"videoroom": "joined","room": 1234,"publishers": []}}
      }
      
  • 关键作用:完成房间加入,客户端获取实时状态(如当前无其他发布者)。

4. 阶段4:媒体协商——解决“怎么传流”的技术问题
(1)发送SDP Offer:提议媒体参数

  • 客户端动作:发送message信令,携带SDP Offer(包含编码、分辨率等媒体参数)。
    {"janus": "message","session_id": 714784477113709,"handle_id": 7668325947218508,"transaction": "QvOYPH9VOj","body": {"request": "configure","audio": true,"video": true},"jsep": {"type": "offer","sdp": "v=0\r\no=- 1234567890 1234567890 IN IP4 0.0.0.0\r\ns=...",}
    }
    
  • 作用:告诉服务端“我要推流,支持这些编码和网络参数”。

(2)ICE候选交换:协商网络路径

  • 客户端动作:多次发送trickle信令,传递ICE候选(设备网络地址)。
    {"janus": "trickle","candidate": {"candidate": "candidate:2302048529 ... typ host generation 0","sdpMid": "audio","sdpMLineIndex": 0},"session_id": 714784477113709,"handle_id": 7668325947218508,"transaction": "A3n5mWz8S"
    }
    
  • 服务端响应:返回ack信令确认接收。
  • 作用:找到最优网络路径(P2P或中继),为流传输铺路。

(3)接收SDP Answer:确认媒体参数

  • 服务端动作:通过event信令返回SDP Answer(确认双方兼容的媒体参数)。
    {"janus": "event","session_id": 714784477113709,"handle_id": 7668325947218508,"plugindata": {"plugin": "janus.plugin.videoroom","data": {"videoroom": "event","configured": {"audio": true,"video": true},"jsep": {"type": "answer","sdp": "v=0\r\no=- 9876543210 9876543210 IN IP4 0.0.0.0\r\ns=...",}}}
    }
    
  • 客户端处理:解析Answer,完成WebRTC媒体协商。
  • 作用:确保双方编码、传输协议兼容,建立媒体通道。

5. 阶段5:流传输——从信令到实际数据

  • 服务端事件:返回webrtcup信令,标识WebRTC连接已建立。
    {"janus": "webrtcup","session_id": 714784477113709,"handle_id": 7668325947218508
    }
    
  • 服务端事件:返回media信令,标识音视频流开始传输。
    {"janus": "media","session_id": 714784477113709,"handle_id": 7668325947218508,"sender": 7668325947218508,"receiving": { "video": true, "audio": true }
    }
    
  • 客户端动作:将音视频流渲染到页面(如绑定<video>标签)。
  • 关键作用:完成从“信令协商”到“实际媒体流传输”的闭环。
http://www.xdnf.cn/news/909343.html

相关文章:

  • 【计算机网络】非阻塞IO——poll实现多路转接
  • AIGC 基础篇 Python基础 01
  • 使用阿里云百炼embeddings+langchain+Milvus实现简单RAG
  • PCB设计教程【大师篇】——STM32开发板电源设计(LDO、DCDC)
  • 深入Kubernetes源码阅读指南:从环境搭建到核心原理剖析
  • 【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
  • 在 Caliper 中执行不同合约的方法
  • Varjo如何帮助Entrol最大化其XR模拟器的性能
  • 探索GIS局部放电监测:PRPD与PRPS图谱的奥秘
  • 好子集的数目之解决方案
  • EDA断供危机下的冷思考:中国芯片设计软件的破局之道优雅草卓伊凡
  • Executors for C++- A Long Story
  • C++.OpenGL (4/64)纹理(Texture)
  • Vue3 GSAP动画库绑定滚动条视差效果 绑定滚动条 滚动条动画 时间轴
  • 破壁焕新能:DeviceNET转EtherNet/IP网关赋能烟草智能制造跃迁
  • Redis 主从 + 哨兵集群部署
  • Python爬虫伪装
  • 校招 Java 面试基础题目解析学习指南含新技术实操要点
  • Android第十三次面试总结基础
  • 【工具变量】上市公司企业华证esg数据集(2009-2024年)
  • 在Window上安装和配置VTK9.x,并在QT项目中调试VTK是否可用
  • 2025远离Deno和Fresh
  • 5G 核心网中 NF 选择机制:基于优先级、权重与负载分担的策略解析
  • 靶场(十九)--靶场体会小白分享--Billyboss
  • Langgraph实战--在Agent中加入人工反馈
  • OLED(SSD306)移植全解-基于IIC
  • 零基础完成 Token 创建的全流程教学
  • 芋道源码 - 本地文件上传配置与实现
  • 【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
  • 配置sudo免密却不生效的问题