omnilink-client
v2.1.1
Published
Omnilink Video Conferencing SDK
Maintainers
Readme
OmniLink Client SDK
基于 WebRTC + mediasoup 的高清视频会议客户端 SDK,提供完整的音视频通话、屏幕共享、实时聊天、会议管理等功能。
特性
- 高清音视频通话 — 基于 WebRTC 的低延迟实时通信
- 屏幕共享 — 支持分享整个屏幕或单个窗口
- 实时聊天 — 会议内文字消息实时收发
- 会议管理 — 创建、加入、离开、关闭会议
- 等候室 — 主持人审批机制,控制入会人员
- 权限控制 — 静音、禁用视频、强制离开等主持人功能
- 多端支持 — 自动识别 PC / 平板 / 手机设备类型
- 自动重连 — WebSocket 断线后自动恢复连接
版本信息
当前版本:v2.1.1
| 版本 | 日期 | 说明 |
|------|------|------|
| v2.1.1 | 2026-06-11 | 修复音频约束:getAudioTrack() 显式启用 echoCancellation、noiseSuppression、autoGainControl,解决近距离设备开会时的啸叫问题 |
| v2.1.0 | 2026-06-10 | 新增 socketUrl 和 basePath 配置,支持 HTTP API 与 WebSocket 使用不同基础路径,适配 /omnilink 子路径部署 |
| v2.0.10 | 2026-04-27 | 新增 getUserByOriginalId() 和 getUsersByOriginalIds() 接口,支持通过外部系统用户登录名查询用户基础信息(无需 Token) |
| v2.0.9 | 2026-04-24 | 邀请功能支持附带文本信息:session.inviteUser()、session.inviteUsers()、session.generateInviteLink() 新增可选 message 参数;meeting:invited 事件新增 message 字段 |
| v2.0.8 | 2026-04-23 | 创建会议支持 startAt(预约开始时间)和 duration(会议时长),会议到期自动结束 |
| v2.0.7 | 2026-04-23 | API Key 登录参数调整为 originalId(用户登录名),更符合企业 SSO 实际使用场景 |
| v2.0.6 | 2026-04-23 | 新增 API Key 登录支持:client.loginWithApiKey()、client.user.exchangeApiKey(),支持第三方应用通过预登录令牌接入 |
| v2.0.5 | 2026-04-23 | 修复 client.meeting.getMeeting() 接口,现在可在未加入会议时获取会议详情 |
| v2.0.4 | 2026-04-23 | 新增 session.inviteUsers() 批量邀请接口,支持一次邀请多个用户 |
| v2.0.3 | 2026-04-23 | 修复 meeting:invited 事件监听:被邀请方现在可通过 client.onMeetingInvited() 接收会议邀请推送 |
| v2.0.2 | — | 初始稳定版本 |
查看完整 更新日志
安装
npm install omnilink-client快速开始
1. 初始化客户端
import { OmniLinkClient } from 'omnilink-client'
const client = new OmniLinkClient({
serviceUrl: 'https://your-server.com'
})2. 登录
try {
const result = await client.login({
originalId: 'user001', // 用户登录名
password: '123456', // 密码
deviceType: 'pc', // 设备类型:pc / mobile / tablet
deviceId: 'device-001' // 设备唯一标识
})
console.log('登录成功', result.userId)
} catch (err) {
console.error('登录失败', err.message)
}通过 API Key 登录(第三方接入 / 企业 SSO)
适用于企业已有统一身份认证系统、无需用户输入密码的场景。
架构说明:apiKey 是敏感密钥,必须由第三方企业服务端保管和调用 exchange 接口;前端只接收 preLoginToken 并调用 login。严禁将 apiKey 暴露到前端代码或浏览器中。
Step 1 — 企业服务端调用 exchange(HTTP 原始接口):
第三方服务端可用任意语言(Java、Python、Go、PHP 等)直接调用 OmniLink HTTP 接口:
# cURL 示例
curl -X POST https://api.example.com/api/v1/apikey/exchange \
-H "Content-Type: application/json" \
-d '{
"apiId": "ak_c18d803d05e20348",
"apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
"originalId": "zhangsan"
}'# Python 示例
import requests
res = requests.post("https://api.example.com/api/v1/apikey/exchange", json={
"apiId": "ak_c18d803d05e20348",
"apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
"originalId": "zhangsan"
})
data = res.json()["data"]
pre_login_token = data["preLoginToken"] # 5分钟有效// Java (OkHttp) 示例
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(
MediaType.parse("application/json"),
"{\"apiId\":\"ak_c18d803d05e20348\",\"apiKey\":\"sk_...\",\"originalId\":\"zhangsan\"}"
);
Request request = new Request.Builder()
.url("https://api.example.com/api/v1/apikey/exchange")
.post(body)
.build();
Response response = client.newCall(request).execute();
// 解析 response 获取 preLoginTokenStep 2 — 企业服务端将 preLoginToken 返回给前端:
{
"preLoginToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 300,
"userId": "69cf72ac6359168b78c504d3",
"userName": "张三"
}Step 3 — 前端用 preLoginToken 完成登录:
const loginResult = await client.loginWithApiKey({
preLoginToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', // 来自服务端
deviceType: 'web',
deviceId: 'enterprise-portal-browser-001'
})
console.log('API Key 登录成功', loginResult.userId)3. 创建并加入会议
// 创建会议(自动加入)
const session = await client.createAndJoinMeeting({
meetingName: '产品周会',
waitingRoom: false, // 是否开启等候室
startAt: Date.now(), // 会议开始时间(时间戳,默认当前时间)
duration: 60, // 会议时长(分钟,默认无限制)
})
console.log('会议ID:', session.meetingId)
console.log('会议密码:', session.password)4. 初始化媒体并推流
// 初始化 mediasoup 媒体客户端
await session.initMediaClient()
// 获取本地音视频
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
})
// 发布视频
const videoTrack = stream.getVideoTracks()[0]
const { producerId } = await session.produceTrack(videoTrack, { kind: 'video' })
// 发布音频
const audioTrack = stream.getAudioTracks()[0]
await session.produceTrack(audioTrack, { kind: 'audio' })5. 拉取远端流
// 订阅其他成员的音视频
client.onProducerCreated(async ({ producerId, kind, userId }) => {
// 拉流
const { stream, consumerId } = await session.consume(producerId, kind)
// 将流绑定到 video/audio 元素
const videoEl = document.getElementById(`video-${userId}`) as HTMLVideoElement
videoEl.srcObject = stream
videoEl.play()
})6. 实时聊天
// 发送消息
await session.sendChatMessage('大家好,会议开始!')
// 接收消息
client.onChatMessage((msg) => {
console.log(`${msg.senderName}: ${msg.content}`)
})7. 监听会议事件
// 成员加入
client.onMemberJoined((data) => {
console.log(`${data.userName} 加入了会议`)
})
// 成员离开
client.onMemberLeft((data) => {
console.log(`${data.userId} 离开了会议`)
})
// 会议被关闭
client.onMeetingClosed((data) => {
console.log('会议已关闭:', data.reason)
})
// 被踢出会议
client.onKicked((data) => {
console.log('您已被踢出会议:', data.reason)
})
// 连接状态变化
client.onConnectionStateChange((state) => {
console.log('连接状态:', state) // disconnected / connecting / connected / reconnecting
})8. 离开会议
await client.leaveMeeting()API 参考
OmniLinkClient
核心客户端类,继承自 EventEmitter。提供统一的认证、连接、会议入口。
import { OmniLinkClient } from 'omnilink-client'
// 或默认导出
import OmnilinkClient from 'omnilink-client'构造函数
new OmniLinkClient(config?: OmniLinkClientConfig)参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| config | OmniLinkClientConfig | 否 | {} | 客户端配置对象 |
| config.serviceUrl | string | 否 | 'http://localhost:3000' | 统一服务地址,推荐直接使用此字段配置 |
| config.userServiceUrl | string | 否 | 继承 serviceUrl | 用户服务独立地址(旧版兼容) |
| config.meetingServiceUrl | string | 否 | 继承 serviceUrl | 会议服务独立地址(旧版兼容) |
| config.socketUrl | string | 否 | 继承 serviceUrl | WebSocket 连接地址。当 HTTP API 和 WebSocket 使用不同基础路径时设置,例如 API 通过 /omnilink/api 代理,但 socket.io 通过 /socket.io 代理 |
| config.basePath | string | 否 | '' | 前端部署的子路径(如 /web),用于生成正确的邀请链接等完整 URL |
属性
| 属性 | 类型 | 说明 |
|------|------|------|
| user | UserService | 用户/认证服务实例,暴露登录、注册、资料管理等 HTTP API |
| meeting | MeetingService | 会议服务实例,暴露会议创建、查询、加入等 HTTP API |
| socket | SocketManager | WebSocket 管理实例,用于 Socket.IO 连接和事件收发 |
登录相关
login(credentials)
用户登录,成功后自动保存 token 到 localStorage,并自动建立 WebSocket 连接。
async login(credentials: LoginCredentials): Promise<LoginResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| credentials | LoginCredentials | 是 | 登录凭证 |
| credentials.originalId | string | 是 | 用户登录名/账号 |
| credentials.password | string | 是 | 用户密码 |
| credentials.deviceType | string | 是 | 设备类型:'pc' / 'mobile' / 'tablet' |
| credentials.deviceId | string | 是 | 设备唯一标识,用于多设备登录管理 |
| credentials.userAgent | string | 否 | 浏览器 User-Agent(可选) |
| credentials.force | boolean | 否 | 是否强制登录,用于挤掉其他设备的已有会话 |
返回值
Promise<LoginResult> — 登录成功结果:
token: string— JWT 认证令牌userId: string— 用户唯一 IDendpointId: string— 当前设备端点 IDsession: string— 会话标识endpoints: Array<{ id, deviceId, deviceType, loginTime, session }>— 该账号所有在线设备列表
异常
- 账号密码错误、账号不存在时抛出
HttpError - 账号已在其他设备登录时会返回
needForce: true,再次调用并传入force: true可强制登录
loginWithApiKey(credentials)
使用 API Key 预登录令牌登录,适用于第三方应用接入场景。
先通过 client.user.exchangeApiKey() 获取 preLoginToken,再调用此方法完成登录。
登录成功后自动保存 token 到 localStorage,并自动建立 WebSocket 连接。
async loginWithApiKey(credentials: ApiKeyLoginCredentials): Promise<LoginResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| credentials | ApiKeyLoginCredentials | 是 | API Key 登录凭证 |
| credentials.preLoginToken | string | 是 | 预登录令牌(由 exchangeApiKey() 获取) |
| credentials.deviceType | string | 是 | 设备类型:'pc' / 'mobile' / 'tablet' / 'web' |
| credentials.deviceId | string | 是 | 设备唯一标识 |
| credentials.userAgent | string | 否 | 用户代理字符串 |
| credentials.force | boolean | 否 | 是否强制登录,挤掉同类型设备的已有会话 |
返回值
Promise<LoginResult> — 同 login() 的返回值
异常
- 预登录令牌无效或过期时抛出
HttpError(401) - 账号已在其他设备登录时返回
needForce: true(409)
logout()
退出登录,断开 WebSocket 连接,清除本地 token。
async logout(): Promise<void>返回值 Promise<void>
isLoggedIn()
检查当前是否已登录。
isLoggedIn(): boolean返回值 boolean — true 表示已登录且 token 有效
restoreSession()
从 localStorage 恢复 token 并重新连接 WebSocket。适用于页面刷新后的自动重连场景。
async restoreSession(): Promise<boolean>返回值 Promise<boolean> — true 表示恢复成功并连接 WebSocket
exchangeApiKey(credentials)
使用 API Key 凭证交换预登录令牌。这是 API Key 登录的第一步。
async exchangeApiKey(credentials: ApiKeyExchangeCredentials): Promise<ApiKeyExchangeResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| credentials | ApiKeyExchangeCredentials | 是 | API Key 交换凭证 |
| credentials.apiId | string | 是 | API 公开标识符,如 ak_xxxxxxxx |
| credentials.apiKey | string | 是 | API 私密密钥,如 sk_xxxxxxxx |
| credentials.userId | string | 是 | 目标用户 ID |
返回值
Promise<ApiKeyExchangeResult> — 交换结果:
preLoginToken: string— 预登录令牌(JWT,5 分钟有效)expiresIn: number— 过期时间(秒,默认 300)userId: string— 目标用户 IDuserName: string— 目标用户名称
异常
- API ID 不存在、API Key 不正确(401)
- API Key 已停用或过期(401)
- 目标用户不存在(404)或被封禁/禁用(403)
loginWithApiKey(credentials)
使用预登录令牌完成登录(API Key 登录方式)。这是 API Key 登录的第二步。 内部会保存 token 到 localStorage。
async loginWithApiKey(credentials: ApiKeyLoginCredentials): Promise<LoginResponse>参数说明 同 OmniLinkClient.loginWithApiKey() 的 credentials
返回值 Promise<LoginResponse> — 登录响应:
- 成功:
{ success: true, data: LoginResult } - 失败:
{ success: false, code: number, message: string, data?: any, needForce?: boolean }
getProfile()
获取当前登录用户的详细信息。
async getProfile(): Promise<User>返回值 Promise<User> — 用户信息:
id: string— 用户唯一 IDoriginalId: string— 用户登录名name: string— 用户昵称avatar?: string— 头像 URLemail?: string— 邮箱createdAt: Date— 注册时间
getCurrentUserId()
从当前 token 中解析并返回用户 ID。
getCurrentUserId(): string | null返回值 string | null — 用户 ID,未登录时返回 null
连接相关
connectSocket()
手动连接 WebSocket。通常由 login() 自动调用,无需手动使用。
async connectSocket(): Promise<void>异常 未登录(无 token)时抛出错误
返回值 Promise<void>
disconnectSocket(intentional?)
断开 WebSocket 连接。
disconnectSocket(intentional?: boolean): void参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| intentional | boolean | 否 | true | 是否为主动断开。主动断开会取消自动重连;非主动断开会触发自动重连 |
isConnected()
检查 WebSocket 是否已连接。
isConnected(): boolean返回值 boolean
getConnectionState()
获取当前 WebSocket 连接状态。
getConnectionState(): ConnectionState返回值 ConnectionState 枚举值:
'disconnected'— 已断开'connecting'— 连接中'connected'— 已连接'reconnecting'— 自动重连中
会议相关
createAndJoinMeeting(data)
创建一个新会议并自动加入。等效于先调用 meeting.createMeeting() 再调用 meeting.joinMeeting(),最后通过 WebSocket 加入会议房间。
async createAndJoinMeeting(data: CreateMeetingData): Promise<MeetingSession>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| data | CreateMeetingData | 是 | — | 会议创建参数 |
| data.meetingName | string | 否 | — | 会议名称 |
| data.visitorAllowed | boolean | 否 | — | 是否允许访客入会 |
| data.memberInviteAllowed | boolean | 否 | — | 是否允许成员邀请他人 |
| data.multiEndpoint | boolean | 否 | — | 是否允许同一用户多设备入会 |
| data.needPassword | boolean | 否 | — | 是否需要密码 |
| data.allowScreenShare | boolean | 否 | — | 是否允许屏幕共享 |
| data.allowRecording | boolean | 否 | — | 是否允许录制 |
| data.waitingRoom | boolean | 否 | false | 是否开启等候室 |
| data.muteOnEntry | boolean | 否 | — | 成员入会时是否自动静音 |
| data.startAt | number | 否 | Date.now() | 会议开始时间(Unix 时间戳,毫秒) |
| data.duration | number | 否 | — | 会议计划时长(分钟),设置后会议将在到期后自动结束 |
| data.notice | string | 否 | — | 会议公告 |
| data.inviteMembers | string[] | 否 | — | 初始邀请用户 ID 列表 |
返回值 Promise<MeetingSession> — 会议会话实例
joinMeeting(data)
通过会议 ID 和密码加入会议。
async joinMeeting(data: JoinMeetingData): Promise<MeetingSession>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| data | JoinMeetingData | 是 | 加入会议参数 |
| data.meetingId | string | 是 | 会议 ID(9位数字) |
| data.password | string | 是 | 会议密码(6位数字) |
| data.deviceType | string | 否 | 设备类型:'pc' / 'mobile' / 'tablet' |
| data.force | boolean | 否 | 是否强制加入(挤掉同设备已有会话) |
返回值 Promise<MeetingSession> — 会议会话实例
restoreMeeting(meetingId, password)
恢复已加入的会议。适用于页面刷新后重建会话但无需重新输入会议密码的场景。
async restoreMeeting(meetingId: string, password: string): Promise<MeetingSession | null>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| meetingId | string | 是 | 会议 ID |
| password | string | 是 | 会议密码 |
返回值 Promise<MeetingSession | null> — 恢复成功返回会话实例,失败返回 null
leaveMeeting()
离开当前会议。调用后会议会话将不可用。
async leaveMeeting(): Promise<void>返回值 Promise<void>
isInMeeting()
检查当前是否在会议中。
isInMeeting(): boolean返回值 boolean
getCurrentSession()
获取当前会议会话实例。
getCurrentSession(): MeetingSession | null返回值 MeetingSession | null — 当前会话,未在会议中时返回 null
事件监听
所有事件监听方法都返回 void,通过传入回调函数来接收事件。使用 offEvent(event, callback) 可取消监听。
onConnectionStateChange(callback)
WebSocket 连接状态变化时触发。
onConnectionStateChange(callback: (state: ConnectionState) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| state | ConnectionState | 新状态:'disconnected' / 'connecting' / 'connected' / 'reconnecting' |
onMemberJoined(callback)
有成员加入会议时触发。
onMemberJoined(callback: (data: MemberJoinedEvent) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| data.userId | string | 加入成员的用户 ID |
| data.userName | string | 加入成员的昵称 |
| data.role | 'host' \| 'member' | 角色 |
| data.avatar? | string | 头像 URL |
| data.online? | boolean | 是否在线 |
onMemberLeft(callback)
有成员离开会议时触发。
onMemberLeft(callback: (data: MemberLeftEvent) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| data.userId | string | 离开成员的用户 ID |
onMembersUpdated(callback)
会议成员列表发生变化时触发(如有人加入或离开)。
onMembersUpdated(callback: (data: MembersUpdatedEvent) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| data.members | MemberInfo[] | 当前所有在线成员列表,每个成员包含 userId、userName、role、avatar、online |
onProducerCreated(callback)
有成员开始推流(发布音视频)时触发。应在此回调中调用 session.consume() 拉取远端流。
onProducerCreated(callback: (data: ProducerEvent) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| data.producerId | string | 生产者 ID |
| data.kind | 'audio' \| 'video' | 媒体类型。注意:屏幕共享的 kind 也是 'video',可通过 appData 区分 |
| data.userId? | string | 推流用户的 ID |
| data.appData? | any | 应用自定义数据,如 { kind: 'screen' } |
onExistingProducers(callback)
加入会议后,获取会议中已有的推流列表时触发。
onExistingProducers(callback: (data: { producers: ProducerInfo[] }) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| data.producers | ProducerInfo[] | 已有生产者列表,每项包含 id、kind、appData |
onProducerClosed(callback)
有成员停止推流时触发。
onProducerClosed(callback: (data: ProducerEvent) => void): void回调参数 同 onProducerCreated
onChatMessage(callback)
收到聊天消息时触发。
onChatMessage(callback: (data: { senderId: string; senderName: string; content: string; time: Date }) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| senderId | string | 发送者用户 ID |
| senderName | string | 发送者昵称 |
| content | string | 消息内容 |
| time | Date | 发送时间 |
onMeetingClosed(callback)
会议被主持人关闭时触发。
onMeetingClosed(callback: (data: { meetingId: string; reason?: string }) => void): voidonKicked(callback)
被踢出会议时触发。
onKicked(callback: (data: { meetingId: string; reason?: string }) => void): voidonNoticePublished(callback)
收到主持人发布的公告时触发。
onNoticePublished(callback: (data: { content: string; senderId: string }) => void): voidonPermissionsChanged(callback)
自己的会议权限发生变化时触发(如被主持人静音/取消静音)。
onPermissionsChanged(callback: (data: PermissionsChangedEvent) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| data.meetingId | string | 会议 ID |
| data.permissions | object | 权限对象,包含 video?、audio?、screen?、text? 布尔值 |
onSocketConnected(callback)
WebSocket 连接成功时触发。
onSocketConnected(callback: () => void): voidonSocketDisconnected(callback)
WebSocket 断开时触发。
onSocketDisconnected(callback: () => void): voidonReconnecting(callback)
正在自动重连时触发。
onReconnecting(callback: (data: { attempt: number; maxAttempts: number; delay: number }) => void): void回调参数
| 参数 | 类型 | 说明 |
|------|------|------|
| attempt | number | 当前重连次数 |
| maxAttempts | number | 最大重连次数 |
| delay | number | 距离下次重连的延迟(毫秒) |
onReconnected(callback)
WebSocket 重连成功时触发。
onReconnected(callback: () => void): voidonReconnectFailed(callback)
WebSocket 自动重连失败(达到最大重试次数)时触发。
onReconnectFailed(callback: () => void): voidonKickedFromServer(callback)
被服务端踢下线时触发(如账号在其他设备登录)。
onKickedFromServer(callback: (data: { deviceId: string; session: string; reason: string }) => void): voidonMeetingInvited(callback)
收到会议邀请时触发。当主持人调用 session.inviteUser(userId) 或 session.inviteUsers(userIds) 邀请当前用户时,被邀请方会收到此事件。如果邀请时附带了 message,会一同传递。
onMeetingInvited(callback: (data: MeetingInvitedEvent) => void): void回调参数
| 字段 | 类型 | 说明 |
|------|------|------|
| meetingId | string | 会议 ID |
| invitedBy | string | 邀请人用户 ID |
| message | string | 邀请附带的文本信息(如邀请语),邀请时未传则为 undefined |
| timestamp | number | 邀请时间戳 |
使用示例
client.onMeetingInvited((data) => {
console.log(`收到会议邀请,会议ID: ${data.meetingId},邀请人: ${data.invitedBy}`)
if (data.message) {
console.log(`邀请语: ${data.message}`)
}
// 弹出邀请提示框,或自动跳转加入会议
})offEvent(event, callback)
取消指定事件的监听。
offEvent<K extends keyof SocketEventMap>(event: K, callback: (data: SocketEventMap[K]) => void): void参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| event | EventName | 是 | 事件名称 |
| callback | function | 是 | 注册时使用的回调函数 |
ConnectionState
WebSocket 连接状态枚举。
enum ConnectionState {
DISCONNECTED = 'disconnected',
CONNECTING = 'connecting',
CONNECTED = 'connected',
RECONNECTING = 'reconnecting',
}UserService
用户认证 HTTP 服务,通过 client.user 访问。所有 token 自动持久化到 localStorage。
setToken(token)
手动设置用户认证 token。
setToken(token: string): void参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| token | string | 是 | JWT 认证令牌 |
getToken()
获取当前用户 token。
getToken(): string | null返回值 string | null
clearToken()
清除本地保存的用户 token。
clearToken(): voidregister(data)
用户注册。
async register(data: RegisterData): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| data | RegisterData | 是 | 注册信息 |
| data.originalId | string | 是 | 用户登录名/账号 |
| data.username | string | 是 | 用户昵称 |
| data.password | string | 是 | 密码 |
| data.email | string | 否 | 邮箱 |
login(credentials)
用户登录。内部会保存 token 到 localStorage。
async login(credentials: LoginCredentials): Promise<LoginResponse>参数说明 同 OmniLinkClient.login() 的 credentials
返回值 Promise<LoginResponse> — 登录响应:
- 成功:
{ success: true, data: LoginResult } - 失败:
{ success: false, code: number, message: string, data?: any, needForce?: boolean }
logout()
退出登录,调用后端注销接口并清除本地 token。
async logout(): Promise<void>getProfile()
获取当前用户资料。
async getProfile(): Promise<User>返回值 Promise<User> — 用户信息,字段见 OmniLinkClient.getProfile()
getUsers()
获取系统中的用户列表。
async getUsers(): Promise<User[]>返回值 Promise<User[]> — 用户数组
updateName(name)
修改当前用户昵称。
async updateName(name: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| name | string | 是 | 新昵称 |
updateAvatar(avatar)
修改当前用户头像。
async updateAvatar(avatar: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| avatar | string | 是 | 头像图片 URL |
updatePassword(oldPassword, newPassword)
修改当前用户密码。
async updatePassword(oldPassword: string, newPassword: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| oldPassword | string | 是 | 旧密码 |
| newPassword | string | 是 | 新密码 |
isLoggedIn()
检查是否已登录。
isLoggedIn(): boolean返回值 boolean
getSessions()
获取当前账号的所有登录会话列表。
async getSessions(): Promise<any[]>返回值 Promise<any[]> — 会话列表,每项包含设备信息、登录时间等
getUserByOriginalId(originalId)
通过外部系统用户登录名(originalId)获取用户基础信息。无需登录即可调用,属于公开接口。
async getUserByOriginalId(originalId: string): Promise<UserBasicInfo>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| originalId | string | 是 | 用户登录名/账号(外部系统标识) |
返回值 Promise<UserBasicInfo> — 用户基础信息:
userId: string— OmniLink 内部用户 IDoriginalId: string— 用户登录名name: string— 用户昵称avatar?: string— 头像 URL
异常
- 用户不存在时抛出
HttpError(404)
使用示例
try {
const user = await client.user.getUserByOriginalId('zhangsan')
console.log('用户ID:', user.userId)
console.log('昵称:', user.name)
} catch (err) {
console.error('用户不存在')
}getUsersByOriginalIds(originalIds)
批量通过外部系统用户登录名(originalId)获取多个用户的基础信息。无需登录即可调用,属于公开接口。
async getUsersByOriginalIds(originalIds: string[]): Promise<UserBasicInfo[]>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| originalIds | string[] | 是 | 用户登录名数组(非空) |
返回值 Promise<UserBasicInfo[]> — 匹配到的用户基础信息数组。未匹配到的 originalId 将被自动忽略,不会报错。
使用示例
const users = await client.user.getUsersByOriginalIds(['zhangsan', 'lisi', 'wangwu'])
console.log(`查询 ${users.length} 个用户`)
users.forEach(u => {
console.log(u.originalId, '->', u.userId, u.name)
})MeetingService
会议 HTTP 服务,通过 client.meeting 访问。负责会议的发现、创建、加入和邀请验证。
createMeeting(data)
创建一个新会议。
async createMeeting(data: CreateMeetingData): Promise<{ meetingId: string; password: string }>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| data | CreateMeetingData | 是 | — | 同 OmniLinkClient.createAndJoinMeeting() 的参数 |
返回值 Promise<{ meetingId: string; password: string }> — 新创建会议的 ID 和密码
listMeetings(params?)
分页获取会议列表。
async listMeetings(params?: PaginationParams): Promise<MeetingListResult>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| params | PaginationParams | 否 | — | 分页参数 |
| params.page | number | 否 | — | 页码,从 1 开始 |
| params.pageSize | number | 否 | — | 每页条数 |
返回值 Promise<MeetingListResult> — 分页结果:
meetings: Meeting[]— 会议列表total: number— 总条数page: number— 当前页码pageSize: number— 每页条数totalPages: number— 总页数
getMeeting(meetingId)
获取会议详情。
async getMeeting(meetingId: string): Promise<Meeting>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| meetingId | string | 是 | 会议 ID |
返回值 Promise<Meeting> — 会议详情:
id: string— 会议 IDmeetingName: string— 会议名称creator: string— 创建者用户 IDpassword?: string— 密码visitorAllowed: boolean— 是否允许访客memberInviteAllowed?: boolean— 是否允许成员邀请multiEndpoint?: boolean— 是否允许多端needPassword?: boolean— 是否需要密码allowScreenShare?: boolean— 是否允许屏幕共享allowRecording?: boolean— 是否允许录制waitingRoom?: boolean— 是否开启等候室muteOnEntry?: boolean— 入会是否自动静音notice?: string— 公告status: 'active' | 'closed'— 会议状态createdAt: Date— 创建时间updatedAt: Date— 更新时间forbidden?: object— 全局禁言状态{ video, audio, screen, text, consumer }
getMeetingMembers(meetingId)
获取会议的成员列表。
async getMeetingMembers(meetingId: string): Promise<MeetingMember[]>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| meetingId | string | 是 | 会议 ID |
返回值 Promise<MeetingMember[]> — 成员数组,每项包含:
userId: string— 用户 IDuserName: string— 昵称avatar?: string— 头像role: 'host' | 'member'— 角色joinedAt: Date— 入会时间online?: boolean— 是否在线endpoints?: Array<...>— 端点信息hasAudio?: boolean— 是否有音频forbidden?: object— 被禁止的权限
joinMeeting(data)
加入会议,返回 MeetingSession 会话实例。
async joinMeeting(data: JoinMeetingData): Promise<MeetingSession>参数说明 同 OmniLinkClient.joinMeeting(data)
返回值 Promise<MeetingSession> — 会议会话实例
restoreSession(meetingId?, password?)
从 localStorage 中存储的 meeting token 恢复会议会话。
async restoreSession(meetingId?: string, password?: string): Promise<MeetingSession | null>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| meetingId | string | 否 | 会议 ID,不传则从 localStorage 读取 |
| password | string | 否 | 会议密码,不传则从 localStorage 读取 |
返回值 Promise<MeetingSession | null> — 恢复成功返回会话,失败返回 null
hasMeetingToken()
检查本地是否有会议 token。
hasMeetingToken(): boolean返回值 boolean
getMeetingToken()
获取本地保存的会议 token。
getMeetingToken(): string | null返回值 string | null
clearMeetingToken()
清除本地保存的会议 token 和会话信息。
clearMeetingToken(): voidgetCurrentSession()
获取当前的会议会话实例。
getCurrentSession(): MeetingSession | null返回值 MeetingSession | null
verifyInviteLink(token)
验证邀请链接是否有效。
async verifyInviteLink(token: string): Promise<{ valid: boolean; meetingId?: string; message?: string }>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| token | string | 是 | 邀请链接中的 token |
返回值 Promise<{ valid: boolean; meetingId?: string; message?: string }>
joinByInviteLink(token, deviceType?, force?)
通过邀请链接加入会议。
async joinByInviteLink(token: string, deviceType?: string, force?: boolean): Promise<MeetingSession>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| token | string | 是 | — | 邀请 token |
| deviceType | string | 否 | — | 设备类型 |
| force | boolean | 否 | false | 是否强制加入 |
返回值 Promise<MeetingSession> — 会议会话实例
MeetingSession
会议会话实例,通过 client.createAndJoinMeeting()、client.joinMeeting() 或 client.meeting.joinMeeting() 获取。管理会议内的所有操作,包括媒体推流/拉流、聊天、成员管理、权限控制等。
注意:会话实例通常由 SDK 内部创建,不应直接实例化。
属性
| 属性 | 类型 | 说明 |
|------|------|------|
| meetingId | string | 会议 ID(9位数字) |
| password | string | 会议密码(6位数字) |
| meetingToken | string | 会议令牌(内部使用,每次请求自动附加到 header) |
| closed | boolean | 会话是否已关闭。关闭后不可再使用 |
| isMediaClientInitialized | boolean | 媒体客户端是否已初始化,只有为 true 时才能调用推流/拉流方法 |
媒体生命周期
initMediaClient()
初始化媒体客户端。必须在开始推流或拉流前调用。
内部流程:
- 创建
MediaClientNode实例 - 向后端请求服务器 RTP 能力
- 使用 RTP 能力初始化本地 mediasoup Device
async initMediaClient(): Promise<void>异常
- 会话已关闭时抛出
Error - 后端返回失败或设备初始化失败时抛出
返回值 Promise<void>
produceTrack(track, appData, encodings?)
发布本地媒体轨道到会议。
async produceTrack(
track: MediaStreamTrack,
appData: { kind: 'audio' | 'video' | 'screen' },
encodings?: types.RtpEncodingParameters[]
): Promise<ProduceResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| track | MediaStreamTrack | 是 | 媒体轨道,通过 getUserMedia() 或 getDisplayMedia() 获取 |
| appData | object | 是 | 应用数据 |
| appData.kind | 'audio' \| 'video' \| 'screen' | 是 | 媒体类型标识。screen 在 mediasoup 内部也会被映射为 video |
| encodings | RtpEncodingParameters[] | 否 | RTP 编码参数,用于设置码率、分辨率层级等高级参数 |
返回值 Promise<ProduceResult> — 推流结果:
producerId: string— 生产者 ID,用于后续管理该推流(暂停/恢复/关闭)
异常
- 媒体客户端未初始化时抛出
- 轨道状态非
'live'时抛出 - 创建生产者失败时抛出
produceWithDevice(deviceId, kind)
通过设备 ID 便捷推流。自动获取指定设备的媒体轨道并开始推流。
async produceWithDevice(deviceId: string, kind: 'audio' | 'video'): Promise<ProduceResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| deviceId | string | 是 | 设备 ID,通过 listVideoInputDevices() 或 listAudioInputDevices() 获取 |
| kind | 'audio' \| 'video' | 是 | 媒体类型 |
返回值 Promise<ProduceResult> — 推流结果
closeProducer(producerId)
关闭指定生产者,停止向会议发送媒体流。
async closeProducer(producerId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| producerId | string | 是 | 要关闭的生产者 ID |
pauseProducer(producerId)
暂停指定生产者。暂停后媒体流不再发送到会议,但轨道保持打开,可通过 resumeProducer() 恢复。
async pauseProducer(producerId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| producerId | string | 是 | 生产者 ID |
resumeProducer(producerId)
恢复被暂停的生产者,重新开始发送媒体流。
async resumeProducer(producerId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| producerId | string | 是 | 生产者 ID |
getProducerState(producerId)
获取指定生产者的当前状态。
getProducerState(producerId: string): { paused: boolean; closed: boolean } | undefined参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| producerId | string | 是 | 生产者 ID |
返回值 { paused: boolean; closed: boolean } | undefined — 生产者状态,未找到时返回 undefined
consume(producerId, kind)
订阅远端媒体流。
async consume(producerId: string, kind: 'audio' | 'video'): Promise<ConsumeResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| producerId | string | 是 | 远端生产者 ID(从 onProducerCreated 事件中获得) |
| kind | 'audio' \| 'video' | 是 | 媒体类型 |
返回值 Promise<ConsumeResult> — 消费结果:
consumerId: string— 消费者 IDstream: MediaStream— 媒体流对象,可直接绑定到<video>或<audio>元素的srcObjectkind: 'audio' | 'video'— 媒体类型producerId: string— 对应的生产者 ID
closeConsumer(consumerId)
关闭指定消费者,停止接收远端媒体流。
async closeConsumer(consumerId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| consumerId | string | 是 | 消费者 ID |
pauseConsumer(consumerId)
暂停接收媒体流,但保持 Consumer 连接。
async pauseConsumer(consumerId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| consumerId | string | 是 | 消费者 ID |
resumeConsumer(consumerId)
恢复接收媒体流。
async resumeConsumer(consumerId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| consumerId | string | 是 | 消费者 ID |
getStats()
获取底层 WebRTC 统计信息。
async getStats(): Promise<{ transportId: string; direction: string; stats: RTCStatsReport }[]>返回值 Promise<Array<{ transportId, direction, stats }>> — 所有传输的统计报告数组
会议信息
meetingInfo()
获取当前会议的详细信息。
async meetingInfo(): Promise<Meeting>返回值 Promise<Meeting> — 会议详情,字段见 MeetingService.getMeeting()
getMembers()
获取当前会议内的在线成员列表。
async getMembers(): Promise<Member[]>返回值 Promise<Member[]> — 成员数组,包含 userId、userName、role、avatar、online 等字段
聊天
sendChatMessage(content, type?, fileMeta?)
发送聊天消息到会议。
async sendChatMessage(
content: string,
type?: string,
fileMeta?: { fileUrl?: string; fileName?: string; fileSize?: number }
): Promise<void>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| content | string | 是 | — | 消息内容 |
| type | string | 否 | 'text' | 消息类型:text、image、video、file、system、notice |
| fileMeta | object | 否 | — | 文件元数据(发送文件/图片消息时使用) |
| fileMeta.fileUrl | string | 否 | — | 文件 URL |
| fileMeta.fileName | string | 否 | — | 文件名 |
| fileMeta.fileSize | number | 否 | — | 文件大小(字节) |
uploadMeetingFile(file)
上传会议文件(图片、文档等)。
async uploadMeetingFile(file: File): Promise<{ fileUrl: string; fileName: string; fileSize: number }>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| file | File | 是 | 浏览器 File 对象 |
返回值 Promise<{ fileUrl, fileName, fileSize }> — 上传结果
getChatHistory(limit?)
获取会议内的历史聊天消息。
async getChatHistory(limit?: number): Promise<ChatMessage[]>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| limit | number | 否 | 50 | 返回消息数量限制 |
返回值 Promise<ChatMessage[]> — 消息数组,每项包含:
id?: string— 消息 IDsenderId: string— 发送者 IDsenderName: string— 发送者昵称avatar?: string— 发送者头像content: string— 内容time: Date | string— 时间type?: 'text' | 'image' | 'video' | 'file' | 'system' | 'notice'— 类型fileUrl?、fileName?、fileSize?— 文件信息
主持人控制
以下方法需要主持人权限,后端会进行权限校验。
forbidMemberSpeak(memberId, kind?)
禁止指定成员发言或发送视频。
async forbidMemberSpeak(
memberId: string,
kind?: 'audio' | 'video' | 'screen' | 'text'
): Promise<void>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| memberId | string | 是 | — | 目标成员用户 ID |
| kind | string | 否 | 'audio' | 禁止类型:audio(静音)、video(关闭视频)、screen(禁止共享)、text(禁止聊天) |
permitMemberSpeak(memberId, kind?)
允许指定成员恢复发言或视频。
async permitMemberSpeak(
memberId: string,
kind?: 'audio' | 'video' | 'screen' | 'text'
): Promise<void>参数说明 同 forbidMemberSpeak
forbidAllSpeak(kind?)
全员静音(禁止所有非主持人成员发言)。
async forbidAllSpeak(kind?: 'audio' | 'video' | 'screen' | 'text'): Promise<void>参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| kind | string | 否 | 'audio' | 禁止类型 |
permitAllSpeak(kind?)
允许全员恢复发言。
async permitAllSpeak(kind?: 'audio' | 'video' | 'screen' | 'text'): Promise<void>参数说明 同 forbidAllSpeak
kickMember(memberId)
踢出指定成员。
async kickMember(memberId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| memberId | string | 是 | 要踢出的成员用户 ID |
updateMeetingName(name)
修改会议名称。
async updateMeetingName(name: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| name | string | 是 | 新会议名称 |
updateMeetingPassword(password)
修改会议密码。
async updateMeetingPassword(password: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| password | string | 是 | 新密码 |
publishNotice(content)
发布公告到会议,所有成员都会收到。
async publishNotice(content: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| content | string | 是 | 公告内容 |
inviteUser(userId, message?)
邀请单个用户加入当前会议,可附带邀请文本信息。
async inviteUser(userId: string, message?: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| userId | string | 是 | 被邀请用户的 ID |
| message | string | 否 | 邀请附带的文本信息,被邀请方可在 meeting:invited 事件中收到 |
inviteUsers(userIds, message?)
批量邀请多个用户加入当前会议,可附带统一的邀请文本信息。
async inviteUsers(userIds: string[], message?: string): Promise<InviteUsersResult>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| userIds | string[] | 是 | 被邀请用户的 ID 数组 |
| message | string | 否 | 邀请附带的文本信息,所有被邀请方均可在 meeting:invited 事件中收到 |
返回值 Promise<InviteUsersResult> — 邀请结果,包含:
success: boolean— 是否成功invitedCount: number— 成功邀请的用户数量invitedUserIds: string[]— 实际被邀请的用户 ID 列表
使用示例
const result = await session.inviteUsers(
['user001', 'user002', 'user003'],
'请参加下午3点的产品评审会'
)
console.log(`成功邀请 ${result.invitedCount} 人`)
console.log('被邀请用户:', result.invitedUserIds)generateInviteLink(message?)
生成会议邀请链接,可附带邀请文本信息。链接默认 24 小时内有效。
async generateInviteLink(message?: string): Promise<{ inviteLink: string; expiresIn: number }>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| message | string | 否 | 邀请附带的文本信息,将嵌入到邀请令牌中,被邀请方验证链接时可见 |
返回值 Promise<{ inviteLink: string; expiresIn: number }> — 邀请链接和过期时间(秒)
等候室(主持人)
getWaitingMembers()
获取当前在等候室中的成员列表。
async getWaitingMembers(): Promise<WaitingMember[]>返回值 Promise<WaitingMember[]> — 等候成员数组,每项包含:
userId: string— 用户 IDendpointId: string— 端点 IDdeviceType: string— 设备类型requestedAt: Date— 请求入会时间userName: string— 昵称avatar?: string— 头像
approveWaitingMember(userId, endpointId)
批准等候室中的成员入会。
async approveWaitingMember(userId: string, endpointId: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| userId | string | 是 | 用户 ID |
| endpointId | string | 是 | 端点 ID |
rejectWaitingMember(userId, endpointId, reason?)
拒绝等候室中的成员入会。
async rejectWaitingMember(
userId: string,
endpointId: string,
reason?: string
): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| userId | string | 是 | 用户 ID |
| endpointId | string | 是 | 端点 ID |
| reason | string | 否 | 拒绝原因 |
approveAllWaitingMembers()
一键批准所有等候室成员。
async approveAllWaitingMembers(): Promise<{ userId: string; endpointId: string }[]>返回值 Promise<Array<{ userId, endpointId }>> — 被批准的成员列表
rejectAllWaitingMembers(reason?)
一键拒绝所有等候室成员。
async rejectAllWaitingMembers(reason?: string): Promise<{ userId: string; endpointId: string }[]>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| reason | string | 否 | 拒绝原因 |
返回值 Promise<Array<{ userId, endpointId }>> — 被拒绝的成员列表
setWaitingRoom(enabled)
开启或关闭等候室功能。
async setWaitingRoom(enabled: boolean): Promise<{ waitingRoom: boolean }>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| enabled | boolean | 是 | true 开启,false 关闭 |
返回值 Promise<{ waitingRoom: boolean }> — 当前等候室状态
会话生命周期
leave()
离开会议。调用后端接口并清理本地资源。离开后此会话实例不可再使用。
async leave(): Promise<void>close()
关闭会议(仅主持人可用)。关闭后所有成员将被强制离开,此会话实例不可再使用。
async close(): Promise<void>异常 调用者不是主持人时抛出权限错误
MediaClientNode
底层 mediasoup-client 封装类。通常不需要直接使用,由 MeetingSession 内部管理。在需要深度自定义 WebRTC 行为时使用。
import { MediaClientNode } from 'omnilink-client'属性
| 属性 | 类型 | 说明 |
|------|------|------|
| isDeviceLoaded | boolean | 设备是否已加载,加载完成后才能创建传输 |
| rtpCapabilities | types.RtpCapabilities | 本地设备的 RTP 能力参数,需在 initDevice() 后获取 |
initDevice(routerRtpCapabilities)
初始化 mediasoup Device。
async initDevice(routerRtpCapabilities: types.RtpCapabilities): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| routerRtpCapabilities | types.RtpCapabilities | 是 | 服务器端 RTP 能力,从后端 /api/v1/meeting/rtp-capabilities 获取 |
createSendTransport(options, onConnect, onProduce)
创建发送传输(用于推流)。
async createSendTransport(
options: types.TransportOptions,
onConnect: (transportId: string, dtlsParameters: types.DtlsParameters) => Promise<void>,
onProduce: (transportId: string, kind: types.MediaKind, rtpParameters: types.RtpParameters, appData?: Record<string, unknown>) => Promise<string>
): Promise<types.Transport>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| options | types.TransportOptions | 是 | 传输配置(id、iceParameters、iceCandidates、dtlsParameters 等) |
| onConnect | function | 是 | DTLS 连接回调。收到 dtlsParameters 时需要发送到服务端完成连接 |
| onProduce | function | 是 | 生产回调。需要向服务端请求创建 Producer,返回服务端分配的 producerId |
返回值 Promise<types.Transport> — 发送传输对象
createRecvTransport(options, onConnect)
创建接收传输(用于拉流)。
async createRecvTransport(
options: RecvTransportOptions,
onConnect: (transportId: string, dtlsParameters: types.DtlsParameters) => Promise<void>
): Promise<types.Transport>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| options | RecvTransportOptions | 是 | 接收传输配置 |
| onConnect | function | 是 | DTLS 连接回调 |
返回值 Promise<types.Transport> — 接收传输对象
produce(transport, options)
在发送传输上生产媒体。
async produce(transport: types.Transport, options: ProduceOptions): Promise<types.Producer>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| transport | types.Transport | 是 | 发送传输对象 |
| options | ProduceOptions | 是 | 生产选项 |
| options.track | MediaStreamTrack | 是 | 媒体轨道 |
| options.appData? | Record<string, unknown> | 否 | 应用自定义数据 |
| options.encodings? | types.RtpEncodingParameters[] | 否 | 编码参数 |
返回值 Promise<types.Producer> — mediasoup Producer 对象
produceVideo(sendTransport, videoTrack, appData?)
便捷方法:创建视频生产者。内部调用 produce()。
async produceVideo(
sendTransport: types.Transport,
videoTrack: MediaStreamTrack,
appData?: Record<string, unknown>
): Promise<types.Producer>consume(data, appData?)
消费远端媒体流。
async consume(
data: {
transportId: string
consumerId: string
producerId: string
kind: 'audio' | 'video'
rtpParameters: types.RtpParameters
},
appData?: Record<string, unknown>
): Promise<{ consumerId: string; stream: MediaStream }>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| data.transportId | string | 是 | 接收传输 ID |
| data.consumerId | string | 是 | 服务端分配的消费者 ID |
| data.producerId | string | 是 | 远端生产者 ID |
| data.kind | 'audio' \| 'video' | 是 | 媒体类型 |
| data.rtpParameters | types.RtpParameters | 是 | RTP 参数 |
| appData | object | 否 | 附加数据 |
返回值 Promise<{ consumerId: string; stream: MediaStream }>
closeProducer(producerId)
关闭指定生产者。
async closeProducer(producerId: string): Promise<void>getProducer(id)
获取生产者实例。
getProducer(id: string): types.Producer | undefinedgetConsumer(id)
获取消费者实例。
getConsumer(id: string): types.Consumer | undefinedgetSendTransport(id)
获取发送传输实例。
getSendTransport(id: string): types.Transport | undefinedgetRecvTransport(id)
获取接收传输实例。
getRecvTransport(id: string): types.Transport | undefinedgetStats()
获取 WebRTC 统计信息。
async getStats(): Promise<{ transportId: string; direction: string; stats: RTCStatsReport }[]>返回值 统计报告数组
close()
关闭所有传输并清理资源。调用后此实例不可复用,需重新创建 MediaClientNode。
close(): voidHttpService
HTTP 请求封装层,基于 Axios。通常由内部服务使用,也可独立使用。
import { HttpService } from 'omnilink-client'静态常量
| 常量 | 值 | 说明 |
|------|-----|------|
| SUCCESS_CODE | 200 | 成功状态码 |
| UNAUTHORIZED_CODE | 401 | 未授权 |
| FORBIDDEN_CODE | 403 | 禁止访问 |
| NOT_FOUND_CODE | 404 | 未找到 |
| SERVER_ERROR_CODE | 500 | 服务器错误 |
setTokenGetter(getter)
设置令牌获取函数。每次请求前会调用此函数获取 token。
setTokenGetter(getter: () => string | null): void参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| getter | () => string \| null | 是 | 返回 token 或 null 的函数 |
setTokenHeaderKey(key)
设置认证请求头字段名。默认 'Authorization'。
setTokenHeaderKey(key: string): voidsetPublicPaths(paths)
设置免认证路径列表。匹配这些路径的请求不会自动附加 token。
setPublicPaths(paths: string[]): voidaddPublicPath(path)
添加单个免认证路径。
addPublicPath(path: string): voidsetTimeout(timeout)
设置请求超时时间(毫秒)。默认 30000ms。
setTimeout(timeout: number): voidget(url, config?)
发送 GET 请求。
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| url | string | 是 | 请求地址 |
| config | AxiosRequestConfig | 否 | Axios 请求配置 |
返回值 Promise<T> — 响应数据(已提取 response.data.data)
post(url, data?, config?)
发送 POST 请求。
async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>put(url, data?, config?)
发送 PUT 请求。
async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>patch(url, data?, config?)
发送 PATCH 请求。
async patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>delete(url, config?)
发送 DELETE 请求。
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>getAxiosInstance()
获取底层 Axios 实例,用于添加自定义拦截器等高级操作。
getAxiosInstance(): AxiosInstancedispose()
释放资源。
dispose(): voidHttpError
HTTP 请求错误类,继承自 Error。所有通过 HttpService 发起的请求失败时都会抛出此错误。
import { HttpError } from 'omnilink-client'属性
| 属性 | 类型 | 说明 |
|------|------|------|
| status | number | HTTP 状态码(如 401、403、500) |
| code | number | 业务错误码(后端返回的业务 code) |
| data | any | 错误响应的原始数据 |
| url | string | 触发错误的请求地址 |
| message | string | 错误信息(继承自 Error) |
| name | string | 固定为 'HttpError' |
SocketManager
WebSocket 连接管理器,基于 Socket.IO。通常由 OmniLinkClient 内部管理,也可通过 client.socket 访问。
connect(url, token?)
建立 Socket.io WebSocket 连接。
async connect(url: string, token?: string): Promise<void>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| url | string | 是 | 服务器地址,如 'ws://localhost:3000' |
| token | string | 否 | 用户认证 token |
disconnect(intentional?)
断开连接。
disconnect(intentional?: boolean): void参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|:--:|--------|------|
| intentional | boolean | 否 | true | 是否主动断开 |
isConnected()
是否已连接。
isConnected(): booleanisConnecting()
是否正在连接中。
isConnecting(): booleangetReconnectAttempts()
获取当前重连尝试次数。
getReconnectAttempts(): numberrequest(command, data?)
发送请求到服务器并等待响应。
async request(command: string, data?: any): Promise<any>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| command | string | 是 | 命令名称 |
| data | any | 否 | 请求数据 |
返回值 Promise<any> — 服务器响应数据
notify(command, data?)
发送通知到服务器(无需响应)。
notify(command: string, data?: any): voidjoinMeeting(token)
通过会议 token 加入会议房间。
async joinMeeting(token: string): Promise<any>参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|:--:|------|
| token | string | 是 | 会议 token |
leaveMeeting()
离开会议房间。
async leaveMeeting(): Promise<void>getCurrentMeetingToken()
获取当前会议的 token。
getCurrentMeetingToken(): string | nullgetMeetingMembers(meetingId)
通过 WebSocket 获取会议成员。
async getMeetingMembers(meetingId: string): Promise<any>sendChat(content)
发送聊天消息。
sendChat(content: string): voidLogger
静态日志工具类,支持日志级别控制和源代码位置显示。
import { Logger, LogLevel } from 'omnilink-client'在浏览器环境中可通过 window.OmniLinkLogger 直接访问。
enum LogLevel {
DEBUG = 0, // 最详细,输出所有日志
INFO = 1, // 关键流程节点(默认)
WARN = 2, // 警告信息
ERROR = 3, // 仅错误
}setLevel(level)
设置日志级别。低于此级别的日志不会输出。
static setLevel(level: LogLevel): voiddebug(...args)
输出 DEBUG 级别日志(蓝色)。
static debug(...args: any[]): voidinfo(...args)
输出 INFO 级别日志(绿色)。
static info(...args: any[]): voidwarn(...args)
输出 WARN 级别日志(橙色)。
static warn(...args: any[]): voiderror(...args)
输出 ERROR 级别日志(红色)。
static error(...args: any[]): void媒体设备辅助函数
import { listVideoInputDevices, listAudioInputDevices } from 'omnilink-client'listVideoInputDevices()
列出所有视频输入设备(摄像头)。
注意:需要先获取摄像头权限,否则设备标签可能为空字符串。
async listVideoInputDevices(): Promise<MediaDeviceInfo[]>返回值 Promise<MediaDeviceInfo[]> — 视频设备列表。获取失败时返回空数组。
listAudioInputDevices()
列出所有音频输入设备(麦克风)。
注意:需要先获取麦克风权限,否则设备标签可能为空字符串。
async listAudioInputDevices(): Promise<MediaDeviceInfo[]>返回值 Promise<MediaDeviceInfo[]> — 音频设备列表。获取失败时返回空数组。
类型定义
SDK 导出了以下 TypeScript 类型,可通过 import type { ... } from 'omnilink-client' 使用。
配置
| 类型 | 说明 |
|------|------|
| OmniLinkClientConfig | 客户端配置,{ serviceUrl?, userServiceUrl?, meetingServiceUrl?, socketUrl?, basePath? } |
| OmniLinkClientConfigResolved | 解析后的完整配置 |
| ReconnectConfig | 重连配置,{ enabled, maxAttempts, initialDelay, maxDelay, backoffMultiplier } |
| ReconnectState | 重连状态 |
| ReconnectingEvent | 重连事件数据,{ attempt, maxAttempts, delay } |
用户
| 类型 | 说明 |
|------|------|
| User | 用户信息,{ id, originalId, name, avatar?, email?, createdAt } |
| LoginCredentials | 登录凭证,{ originalId, password, deviceType, deviceId, userAgent?, force? } |
| LoginResult | 登录结果,{ token, userId, endpointId, session, endpoints } |
| RegisterData | 注册数据,{ originalId, username, password, email? } |
| EndpointInfo | 端点信息 |
| LoginResponse | 登录响应联合类型(成功/失败) |
| ApiResponse | 通用 API 响应 |
| ApiKeyExchangeCredentials | API Key 交换凭证,{ apiId, apiKey, originalId } |
| ApiKeyExchangeResult | API Key 交换结果,{ preLoginToken, expiresIn, userId, userName } |
| ApiKeyLoginCredentials | API Key 登录凭证,{ preLoginToken, deviceType, deviceId, userAgent?, force? } |
| UserBasicInfo | 用户基础信息(脱敏),{ userId, originalId, name, avatar? } |
会议
| 类型 | 说明 |
|------|------|
| Meeting | 会议详情,包含名称、创建者、密码、等候室开关、状态等 |
| MeetingMember | 会议成员,{ userId, userName, avatar?, role, joinedAt, online?, endpoints?, hasAudio?, forbidden? } |
| WaitingMember | 等候室成员,{ userId, endpointId, deviceType, requestedAt, userName, avatar? } |
| CreateMeetingData | 创建会议参数,{ meetingName?, visitorAllowed?, memberInviteAllowed?, multiEndpoint?, needPassword?, allowScreenShare?, allowRecording?, waitingRoom?, muteOnEntry?, startAt?, duration?, notice?, inviteMembers? } |
| JoinMeetingData | 加入会议参数,{ meetingId, password, deviceType?, force? } |
| MeetingJoinResult | 加入会议结果,{ meeting, token } |
| MeetingListResult | 会议列表分页结果,{ meetings, total, page, pageSize, totalPages } |
| PaginationParams | 分页参数,{ page?, pageSize? } |
| MeetingClosedEvent | 会议关闭事件 |
| KickedFromMeetingEvent | 被踢出会议事件 |
WebRTC / 媒体
| 类型 | 说明 |
|------|------|
| RtpCapabilities | RTP 能力参数 |
| IceCandidate | ICE 候选地址 |
| DtlsParameters | DTLS 参数 |
| IceParameters | ICE 参数 |
| TransportOptions | 传输配置选项 |
| ProducerInfo | 生产者信息,{ id, kind, appData? } |
| ConsumerInfo | 消费者信息 |
| MediaTrackInfo | 媒体轨道信息 |
| DeviceConstraints | 设备约束 |
| MediaDeviceInfo | 媒体设备信息 |
事件
| 类型 | 说明 |
|------|------|
| MemberInfo | 成员基础信息,{ userId, userName, role, avatar?, online? } |
| MemberJoinedEvent | 成员加入事件(继承 MemberInfo) |
| MemberLeftEvent | 成员离开事件,{ userId } |
| MembersUpdatedEvent | 成员列表更新事件,{ members: MemberInfo[] } |
| ProducerEvent | 生产者事件,{ producerId, kind, userId?, appData? } |
| ConsumerEvent | 消费者事件 |
| UserKickedEvent | 用户被踢事件 |
| UserConnectedEvent | 用户连接事件 |
| PermissionsChangedEvent | 权限变更事件,{ meetingId, permissions: { video?, audio?, screen?, text? } } |
| SocketEventMap | Socket 事件映射 |
| EventName | 事件名称类型 |
聊天 / 消息
| 类型 | 说明 |
|------|------|
| ChatMessage | 聊天消息,{ id?, senderId, senderName, avatar?, content, time, type?, fileUrl?, fileName?, fileSize? } |
| ChatMessageEvent | 聊天消息事件 |
| NoticePublishedEvent | 公告发布事件 |
| SystemNotification | 系统通知 |
录制
| 类型 | 说明 |
|------|------|
| RecordingOptions | 录制选项 |
| RecorderControl | 录制控制器 |
| RecordingResult | 录制结果 |
完整示例
通过 API Key 登录(企业 SSO / 第三方接入)
适用于已有统一身份认证系统的企业,或第三方应用无需密码直接接入 OmniLink 的场景。
⚠️ 安全警告:
apiKey(sk_开头)等同于密码,严禁暴露到前端代码或浏览器中。它必须由第三方企业服务端安全保管,仅用于服务端调用exchange接口。
┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ 第三方企业前端 │ │ 第三方企业服务端 │ │ OmniLink 后端 │
│ (浏览器/App) │ │ (Java/Python/Go) │ │ │
│ │ │ │ │ │
│ ① 用户在企业门户 │ │ ② 服务端调用 exchange │ │ │
│ 点击单点登录 │ ────> │ {apiId,apiKey} │ ────> │ POST /apikey/exchange
│ │ │ │ │ │
│ ④ 收到preLoginToken│ <──── │ ③ 返回preLoginToken │ <──── │ 返回预登录令牌 │
│ │ │ │ │ │
│ ⑤ 调用loginWithApiKey│ ────> │ (透传或直接代理) │ ────> │ POST /apikey/login
│ │ │ │ │ │
│ ⑥ 登录成功,自动 │ │ │ │ 返回标准 JWT │
│ 连接WebSocket │ │ │ │ │
└──────────────────┘ └─────────────────────┘ └──────────────────┘Step 1 — 第三方企业服务端调用 exchange(任意语言):
# cURL
curl -X POST https://omnilink.example.com/api/v1/apikey/exchange \
-H "Content-Type: application/json" \
-d '{
"apiId": "ak_c18d803d05e20348",
"apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
"originalId": "zhangsan"
}'# Python 示例
import requests
res = requests.post("https://omnilink.example.com/api/v1/apikey/exchange", json={
"apiId": "ak_c18d803d05e20348",
"apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
"originalId": "zhangsan"
})
data = res.json()["data"]
pre_login_token = data["preLoginToken"]
# 将 pre_login_token 返回给前端页面// Java (OkHttp) 示例
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(
MediaType.parse("application/json"),
"{\"apiId\":\"ak_c18d803d05e20348\",\"apiKey\":\"sk_...\",\"originalId\":\"zhangsan\"}"
);
Request request = new Request.Builder()
.url("https://omnilink.example.com/api/v1/apikey/exchange")
.post(body)
.build();
Response response = client.newCall(request).execute();
// 解析 response 获取 preLoginToken,返回给前端Step 2 — 前端用 preLoginToken 完成登录(omnilink-client SDK):
import { OmniLinkClient } from 'omnilink-client'
async function ssoLogin(preLoginToken: string) {
const client = new OmniLinkClient({ serviceUrl: 'https://omnilink.example.com' })
try {
const loginResult = await client.loginWithApiKey({
preLoginToken, // 来自企业服务端
deviceType: 'web',
deviceId: 'enterprise-portal-browser-001',
userAgent: 'EnterprisePortal/2.0'
})
console.log('登录成功', loginResult.userId)
console.log('当前会话:', loginResult.session)
console.log('在线设备列表:', loginResult.endpoints)
} catch (err: any) {
if (err.needForce) {
// 同一设备类型已有会话,强制踢掉旧会话
console.warn('账号已在其他设备登录,准备强制登录...')
const loginResult = await client.loginWithApiKey({
preLoginToken,
deviceType: 'web',
deviceId: 'enterprise-portal-browser-001',
force: true
})
console.log('强制登录成功', loginResult.userId)
} else {
throw err
}
}
// 获取当前用户信息
const user = await client.getProfile()
console.log('用户信息:', user.name, user.email)
// 监听被踢事件
client.onKickedFromServer((data) => {
console.warn('已被踢下线:', data.reason, '设备:', data.deviceId)
})
// 创建并加入会议
const session = await client.createAndJoinMeeting({
meetingName: '企业周例会',
waitingRoom: false,
duration: 60, // 1小时后自动结束
})
console.log('会议已创建:', session.meetingId)
return { client, session }
}流程说明:
- 企业服务端调用
POST /api/v1/apikey/exchange— 用apiId+apiKey+userId换取短期preLoginToken(默认 5 分钟)。支持任意编程语言,不依赖 Node.js。 - 企业服务端将
preLoginToken返回给前端 — 可通过 SSR 注入页面、JSON API 响应、或 cookie 等方式传递。 - 前端调用
client.loginWithApiKey()— 将preLoginToken+ 设备信息发送给 OmniLink 后端,完成登录并获取标准 JWT。 - 登录成功后 SDK 自动保存 token 到
localStorage,并自动建立 WebSocket 连接。 - 如遇
409冲突(同一deviceType已有活跃会话),可传force: true强制踢掉旧会话。
创建会议并分享屏幕
import { OmniLinkClient } from 'omnilink-client'
async function startScreenShareMeeting() {
const client = new OmniLinkClient({ serviceUrl: 'https://api.example.com' })
// 登录
await client.login({
originalId: '[email protected]',
password: 'password',
deviceType: 'pc',
deviceId: 'desktop-01'
})
// 创建会议
const session = await client.createAndJoinMeeting({
meetingName: '项目评审会',
waitingRoom: true,
duration: 90, // 1.5小时后自动结束
})
console.log(`会议ID: ${session.meetingId}`)
console.log(`密码: ${session.password}`)
// 初始化媒体
await session.initMediaClient()
// 获取屏幕共享流
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true
})
// 发布屏幕共享
const screenTrack = screenStream.getVideoTracks()[0]
await session.produceTrack(screenTrack, { kind: 'screen' })
// 监听远端流
client.onProducerCreated(async ({ producerId, kind, userId }) => {
if (kind === 'video' || kind === 'screen') {
const { stream } = await session.consume(producerId, kind)
createVideoElement(userId, stream)
}
})
// 监听聊天
client.onChatMessage((msg) => {
appendMessage(msg.senderName, msg.content)
})
}
function createVideoElement(userId: string, stream: MediaStream) {
let el = document.getElementById(`video-${userId}`) as HTMLVideoElement
if (!el) {
el = document.createElement('video')
el.id = `video-${userId}`
el.autoplay = true
document.getElementById('video-grid')!.appendChild(el)
}
el.srcObject = stream
}
function appendMessage(name: string, content: string) {
const div = document.createElement('div')
div.textContent = `${name}: ${content}`
document.getElementById('chat-list')!.appendChild(div)
}通过邀请链接加入会议
import { OmniLinkClient } from 'omnilink-client'
async function joinByInvite(inviteToken: string) {
const client = new OmniLinkClient({ serviceUrl: 'https://api.example.com' })
// 先登录
await client.login({
originalId: '[email protected]',
password: 'password',
deviceType: 'pc',
deviceId: 'desktop-02'
})
// 验证邀请链接
const verify = await client.meeting.verifyInviteLink(inviteToken)
if (!verify.valid) {
throw new Error('邀请链接已失效')
}
// 通过邀请链接加入
const session = await client.meeting.joinByInviteLink(inviteToken)
// 初始化媒体
await session.initMediaClient()
// 发布摄像头
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
await session.produceTrack(stream.getVideoTracks()[0], { kind: 'video' })
await session.produceTrack(stream.getAudioTracks()[0], { kind: 'audio' })
return session
}恢复已加入的会议
// 页面刷新后恢复会议
async function restoreMeeting(client: OmniLinkClient) {
const restored = await client.restoreSession()
if (!restored) {
console.log('会话已过期,需要重新登录')
return
}
if (client.isInMeeting()) {
const session = client.getCurrentSession()
if (session) {
// 重新初始化媒体
await session.initMediaClient()
// 重新发布音视频
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
await session.produceTrack(stream.getVideoTracks()[0], { kind: 'video' })
await session.produceTrack(stream.getAudioTracks()[0], { kind: 'audio' })
}
}
}等候室场景
// 主持人端:管理等候室
client.onEvent('meeting:waiting-member', async (member) => {
console.log(`等候室新成员: ${member.userName}`)
// 批准入会
await session.approveWaitingMember(member.userId)
// 或拒绝入会
// await session.rejectWaitingMember(member.userId)
})
// 开启/关闭等候室
await session.setWaitingRoom(true) // 开启
await session.setWaitingRoom(false) // 关闭配置选项
OmniLinkClientConfig
interface OmniLinkClientConfig {
serviceUrl?: string // 统一服务地址(推荐)
userServiceUrl?: string // 用户服务地址(兼容旧版)
meetingServiceUrl?: string // 会议服务地址(兼容旧版)
socketUrl?: string // WebSocket 连接地址(当与 HTTP 使用不同路径时设置)
basePath?: string // 前端部署子路径,如 '/omnilink/web'
}错误处理
SDK 中的异步方法失败时会抛出错误,建议统一包裹 try/catch:
try {
await client.login(credentials)
} catch (err: any) {
if (err.code === 409) {
// 账号已在其他设备登录
const force = confirm('账号已在其他设备登录,是否强制登录?')
if (force) {
await client.login({ ...credentials, force: true })
}
} else {
alert(err.message)
}
}浏览器兼容性
- Chrome 74+
- Edge 79+
- Firefox 67+
- Safari 14+
需要浏览器支持 getUserMedia 和 RTCPeerConnection。
更新日志
v2.1.1(2026-06-11)
重点修复:音频约束显式启用回声消除与噪声抑制
getAudioTrack()方法现在显式启用echoCancellation: true,解决两台设备物理距离较近时打开音频产生的啸叫问题- 同时启用
noiseSuppression: true和autoGainControl: true,降低环境噪声并自动限制麦克风增益 - 旧版本仅传
audio: true,依赖浏览器默认策略,部分环境下回声消除未生效
v2.1.0(2026-06-10)
重点更新:新增 socketUrl 和 basePath 配置
- 新增
socketUrl配置项,支持 HTTP API 和 WebSocket 使用不同基础地址 - 新增
basePath配置项,支持前端子路径部署(如/omnilink/web) - 修复子路径部署下的 socket.io Invalid namespace 问题
v2.0.10(2026-04-27)
重点更新:新增通过 originalId 查询用户接口
- 新增
client.user.getUserByOriginalId(originalId)方法,支持通过外部系统用户登录名查询单个用户的基础信息 - 新增
client.user.getUsersByOriginalIds(originalIds)方法,支持批量查询多个用户的基础信息 - 新增类型
UserBasicInfo,包含userId、originalId、name、avatar?字段 - 两个接口均为公开接口,无需 Token 即可调用,适用于第三方系统查询 OmniLink 用户映射关系的场景
- 后端对应接口:
GET /api/v1/user/by-original-id/:originalId、POST /api/v1/user/batch-by-original-ids
v2.0.9(2026-04-24)
重点更新:邀请功能支持附带文本信息
session.inviteUser(userId, message?)新增可选message参数,支持邀请单个用户时附带邀请语session.inviteUsers(userIds, message?)新增可选message参数,支持批量邀请时统一附带邀请语session.generateInviteLink(message?)新增可选message参数,支持生成邀请链接时嵌入邀请语MeetingInvitedEvent新增message?: string字段,被邀请方可通过client.onMeetingInvited()接收邀请语- 邀请信息会持久化到会议成员记录的
inviteMessage字段中
v2.0.8(2026-04-23)
重点更新:创建会议支持预约时间和时长
CreateMeetingData新增startAt(会议开始时间,时间戳)和duration(会议时长,分钟)参数- 支持预约未来时间开始的会议
- 设置
duration后,会议将在到期后自动结束,期间所有用户退出也不会提前关闭 - 适用于需要严格控制会议时长的场景(如限时面试、定时培训等)
v2.0.7(2026-04-23)
重点更新:API Key 登录参数调整为 originalId
exchange接口请求参数从userId(MongoDB ObjectId)改为originalId(用户登录名)- 第三方企业系统无需知道 OmniLink 内部数据库 ID,直接使用用户的登录名即可调用
- 所有文档示例已同步更新
v2.0.6(2026-04-23)
重点更新:新增 API Key 登录支持
- 新增
client.loginWithApiKey(credentials)方法,支持通过预登录令牌完成登录 - 新增
client.user.exchangeApiKey(credentials)方法,支持用 API 凭证交换预登录令牌 - 新增类型:
ApiKeyExchangeCredentials、ApiKeyExchangeResult、ApiKeyLoginCredentials - 适用于第三方应用服务端、企业 SSO、自动化脚本等无密码登录场景
- 完整的两步登录流程:exchange → preLoginToken → login → JWT
