Janus Gateway API 文档

目录

  1. 概述
  2. 连接方式
  3. 消息格式
  4. 会话管理
  5. 插件管理
  6. VideoRoom 插件
  7. 完整示例

概述

Janus Gateway 是一个开源的 WebRTC 服务器,提供多种通信接口:

  • HTTP REST API: 基于 HTTP 的 RESTful 接口
  • WebSocket: 实时双向通信
  • RabbitMQ: 消息队列集成
  • MQTT: 物联网协议支持
  • Nanomsg: 高性能消息传递
  • Unix Sockets: 本地进程间通信

连接方式

HTTP REST API

端点: http://<host>:8088/janus

特点:

  • 端口: 8088
  • 路径: /janus
  • 请求方式: POST
  • Content-Type: application/json

示例:

curl -X POST http://localhost:8088/janus \
-H "Content-Type: application/json" \
-d '{"janus": "create", "transaction": "123456"}'

WebSocket

端点: ws://<host>:8188/

特点:

  • 端口: 8188
  • 路径: 无(注意:WebSocket 不需要 /janus 路径)
  • 双向实时通信
  • 自动重连支持

示例 (JavaScript):

const ws = new WebSocket('ws://localhost:8188/');
ws.onopen = () => {
  ws.send(JSON.stringify({
    janus: "create",
    transaction: "123456"
  }));
};

示例 (C++ Qt):

QWebSocket *ws = new QWebSocket();
ws->open(QUrl("ws://localhost:8188/"));

消息格式

请求消息结构

{
  "janus": "消息类型",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "唯一事务ID",
  "body": {},
  "jsep": {}
}

字段说明:

字段 类型 必需 说明
janus string 消息类型(create, attach, message等)
session_id number 会话 ID(创建会话后必需)
handle_id number 插件句柄 ID(附加插件后必需)
transaction string 唯一事务标识符,用于匹配响应
body object 插件特定的消息体
jsep object WebRTC SDP 信息(offer/answer)

响应消息结构

{
  "janus": "success|event|error|ack",
  "session_id": 1234567890,
  "transaction": "123456",
  "data": {},
  "plugindata": {},
  "jsep": {},
  "error": {}
}

响应类型:

类型 说明
success 操作成功
event 插件事件或通知
error 操作失败
ack 确认收到(异步操作)

会话管理

创建会话

创建与 Janus Gateway 的会话。

请求:

{
  "janus": "create",
  "transaction": "123456"
}

成功响应:

{
  "janus": "success",
  "transaction": "123456",
  "data": {
    "id": 1234567890
  }
}

销毁会话

终止会话并释放所有资源。

请求:

{
  "janus": "destroy",
  "session_id": 1234567890,
  "transaction": "123456"
}

成功响应:

{
  "janus": "success",
  "transaction": "123456"
}

保活

保持会话活跃,防止超时断开。

请求:

{
  "janus": "keepalive",
  "session_id": 1234567890,
  "transaction": "123456"
}

建议: 每 30 秒发送一次 keepalive 消息。


插件管理

附加插件

将插件附加到会话。

请求:

{
  "janus": "attach",
  "session_id": 1234567890,
  "plugin": "janus.plugin.videoroom",
  "transaction": "123456"
}

成功响应:

{
  "janus": "success",
  "transaction": "123456",
  "data": {
    "id": 9876543210
  }
}

常用插件:

插件名称 功能
janus.plugin.videoroom 视频会议室
janus.plugin.sip SIP 网关
janus.plugin.recordplay 录制回放
janus.plugin.voicemail 语音信箱
janus.plugin.textroom 文本聊天室
janus.plugin.echotest 回声测试

分离插件

从会话中移除插件。

请求:

{
  "janus": "detach",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456"
}

VideoRoom 插件

VideoRoom 是 Janus 最常用的插件,用于实现多人视频会议。

创建房间

创建一个新的视频会议室。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "create",
    "room": 1234,
    "description": "会议室描述",
    "publishers": 10,
    "bitrate": 128000,
    "fir_freq": 10,
    "record": false,
    "rec_dir": "/tmp/janus-recordings"
  }
}

参数说明:

参数 类型 必需 默认值 说明
room number - 房间 ID
description string - 房间描述
publishers number 3 最大发布者数量
bitrate number 128000 视频比特率(bps)
fir_freq number 10 关键帧请求频率(秒)
record boolean false 是否录制
rec_dir string - 录制目录

成功响应:

{
  "janus": "success",
  "transaction": "123456",
  "plugindata": {
    "data": {
      "videoroom": "created",
      "room": 1234
    }
  }
}

销毁房间

删除已存在的房间。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "destroy",
    "room": 1234
  }
}

加入房间(发布者)

作为发布者加入房间,可以发送音视频流。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "join",
    "room": 1234,
    "ptype": "publisher",
    "display": "用户名",
    "id": 5678
  }
}

参数说明:

参数 类型 必需 说明
room number 房间 ID
ptype string 参与者类型(publisher/subscriber)
display string 显示名称
id number 自定义用户 ID

成功响应:

{
  "janus": "event",
  "session_id": 1234567890,
  "transaction": "123456",
  "plugindata": {
    "plugin": "janus.plugin.videoroom",
    "data": {
      "videoroom": "joined",
      "room": 1234,
      "description": "会议室描述",
      "id": 5678,
      "private_id": 12345678,
      "publishers": [
        {
          "id": 123456789,
          "display": "其他用户",
          "talking": false
        }
      ]
    }
  }
}

加入房间(订阅者)

作为订阅者加入房间,只能接收音视频流。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "join",
    "room": 1234,
    "ptype": "subscriber",
    "feed": 123456789
  }
}

参数说明:

参数 类型 必需 说明
room number 房间 ID
ptype string 必须为 "subscriber"
feed number 要订阅的发布者 ID

发布流

开始发送音视频流到房间。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "publish",
    "audio": true,
    "video": true,
    "data": true
  }
}

参数说明:

参数 类型 必需 默认值 说明
audio boolean true 是否发送音频
video boolean true 是否发送视频
data boolean false 是否发送数据通道

成功响应:

{
  "janus": "event",
  "session_id": 1234567890,
  "transaction": "123456",
  "plugindata": {
    "plugin": "janus.plugin.videoroom",
    "data": {
      "videoroom": "event",
      "configured": "ok"
    }
  },
  "jsep": {
    "type": "answer",
    "sdp": "v=0\r\no=- 1234567890 2 IN IP4 127.0.0.1\r\n..."
  }
}

取消发布

停止发送音视频流。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "unpublish"
  }
}

离开房间

退出当前房间。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "leave"
  }
}

配置订阅者

设置订阅者的媒体参数。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "configure",
    "audio": true,
    "video": true,
    "data": false
  }
}

订阅发布者

订阅房间中的特定发布者。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "subscribe",
    "room": 1234,
    "feed": 123456789,
    "audio": true,
    "video": true,
    "data": false
  }
}

取消订阅

停止订阅某个发布者。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "unsubscribe",
    "feed": 123456789
  }
}

列出房间

获取所有可用房间列表。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "list"
  }
}

成功响应:

{
  "janus": "success",
  "transaction": "123456",
  "plugindata": {
    "plugin": "janus.plugin.videoroom",
    "data": {
      "videoroom": "success",
      "list": [
        {
          "room": 1234,
          "description": "会议室1",
          "num_participants": 3
        },
        {
          "room": 5678,
          "description": "会议室2",
          "num_participants": 5
        }
      ]
    }
  }
}

列出参与者

获取房间中的参与者列表。

请求:

{
  "janus": "message",
  "session_id": 1234567890,
  "handle_id": 9876543210,
  "transaction": "123456",
  "body": {
    "request": "listparticipants",
    "room": 1234
  }
}

完整示例

JavaScript 完整流程

const ws = new WebSocket('ws://localhost:8188/');
let sessionId = null;
let handleId = null;

ws.onopen = () => {
  console.log('Connected to Janus');
  createSession();
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  handleMessage(msg);
};

function createSession() {
  ws.send(JSON.stringify({
    janus: 'create',
    transaction: 'create-session'
  }));
}

function attachPlugin() {
  ws.send(JSON.stringify({
    janus: 'attach',
    session_id: sessionId,
    plugin: 'janus.plugin.videoroom',
    transaction: 'attach-plugin'
  }));
}

function createRoom() {
  ws.send(JSON.stringify({
    janus: 'message',
    session_id: sessionId,
    handle_id: handleId,
    transaction: 'create-room',
    body: {
      request: 'create',
      room: 1234,
      description: 'Demo Room',
      publishers: 10
    }
  }));
}

function joinRoom() {
  ws.send(JSON.stringify({
    janus: 'message',
    session_id: sessionId,
    handle_id: handleId,
    transaction: 'join-room',
    body: {
      request: 'join',
      room: 1234,
      ptype: 'publisher',
      display: 'DemoUser'
    }
  }));
}

function publishStream() {
  ws.send(JSON.stringify({
    janus: 'message',
    session_id: sessionId,
    handle_id: handleId,
    transaction: 'publish',
    body: {
      request: 'publish',
      audio: true,
      video: true
    }
  }));
}

function handleMessage(msg) {
  if (msg.janus === 'success') {
    if (msg.data && msg.data.id) {
      sessionId = msg.data.id;
      console.log('Session created:', sessionId);
      attachPlugin();
    }
    if (msg.data && msg.data.id && handleId === null) {
      handleId = msg.data.id;
      console.log('Plugin attached:', handleId);
      createRoom();
    }
  } else if (msg.janus === 'event') {
    if (msg.plugindata && msg.plugindata.data) {
      const data = msg.plugindata.data;
      if (data.videoroom === 'created') {
        console.log('Room created');
        joinRoom();
      } else if (data.videoroom === 'joined') {
        console.log('Joined room');
        publishStream();
      }
    }
  }
}

C++ Qt 完整流程

#include <QWebSocket>
#include <QJsonObject>
#include <QJsonDocument>

class JanusClient : public QObject {
    Q_OBJECT
public:
    void connectToServer(const QString &url) {
        m_webSocket->open(QUrl(url));
    }

    void createSession() {
        QJsonObject msg;
        msg["janus"] = "create";
        msg["transaction"] = "create-session";
        sendMessage(msg);
    }

    void attachPlugin() {
        QJsonObject msg;
        msg["janus"] = "attach";
        msg["session_id"] = static_cast<qint64>(m_sessionId);
        msg["plugin"] = "janus.plugin.videoroom";
        msg["transaction"] = "attach-plugin";
        sendMessage(msg);
    }

    void createRoom(int roomId) {
        QJsonObject msg;
        msg["janus"] = "message";
        msg["session_id"] = static_cast<qint64>(m_sessionId);
        msg["handle_id"] = static_cast<qint64>(m_handleId);
        msg["transaction"] = "create-room";

        QJsonObject body;
        body["request"] = "create";
        body["room"] = roomId;
        body["description"] = "Demo Room";
        body["publishers"] = 10;
        msg["body"] = body;

        sendMessage(msg);
    }

    void joinRoom(int roomId, const QString &username) {
        QJsonObject msg;
        msg["janus"] = "message";
        msg["session_id"] = static_cast<qint64>(m_sessionId);
        msg["handle_id"] = static_cast<qint64>(m_handleId);
        msg["transaction"] = "join-room";

        QJsonObject body;
        body["request"] = "join";
        body["room"] = roomId;
        body["ptype"] = "publisher";
        body["display"] = username;
        msg["body"] = body;

        sendMessage(msg);
    }

    void publishStream() {
        QJsonObject msg;
        msg["janus"] = "message";
        msg["session_id"] = static_cast<qint64>(m_sessionId);
        msg["handle_id"] = static_cast<qint64>(m_handleId);
        msg["transaction"] = "publish";

        QJsonObject body;
        body["request"] = "publish";
        body["audio"] = true;
        body["video"] = true;
        msg["body"] = body;

        sendMessage(msg);
    }

private:
    void sendMessage(const QJsonObject &msg) {
        QJsonDocument doc(msg);
        m_webSocket->sendTextMessage(doc.toJson(QJsonDocument::Compact));
    }

    QWebSocket *m_webSocket;
    quint64 m_sessionId;
    quint64 m_handleId;
};

curl 示例

# 创建会话
curl -X POST http://localhost:8088/janus \
-H "Content-Type: application/json" \
-d '{"janus": "create", "transaction": "123456"}'

# 附加 VideoRoom 插件
curl -X POST http://localhost:8088/janus/SESSION_ID \
-H "Content-Type: application/json" \
-d '{"janus": "attach", "plugin": "janus.plugin.videoroom", "transaction": "123456"}'

# 创建房间
curl -X POST http://localhost:8088/janus/SESSION_ID/HANDLE_ID \
-H "Content-Type: application/json" \
-d '{
  "janus": "message",
  "body": {
    "request": "create",
    "room": 1234,
    "description": "Demo Room"
  },
  "transaction": "123456"
}'

# 加入房间
curl -X POST http://localhost:8088/janus/SESSION_ID/HANDLE_ID \
-H "Content-Type: application/json" \
-d '{
  "janus": "message",
  "body": {
    "request": "join",
    "room": 1234,
    "ptype": "publisher",
    "display": "DemoUser"
  },
  "transaction": "123456"
}'

错误处理

常见错误代码

错误代码 说明
401 未授权(需要认证令牌)
403 禁止访问
404 资源不存在
426 协议错误
478 未知会话
477 未知插件
478 未知句柄
477 插件错误

错误响应示例

{
  "janus": "error",
  "transaction": "123456",
  "error": {
    "code": 478,
    "reason": "No such session"
  }
}

最佳实践

  1. 事务 ID: 使用唯一的事务 ID(如时间戳)来匹配请求和响应
  2. 保活: 定期发送 keepalive 消息(建议 30 秒间隔)
  3. 错误处理: 始终检查响应中的 janus 字段
  4. 资源清理: 应用退出时销毁会话和分离插件
  5. 重连机制: WebSocket 断开时实现自动重连
  6. 心跳检测: 定期检查连接状态

参考资料

posted on 2026-02-07 14:06  Maxsun  阅读(2)  评论(0)    收藏  举报