usc-rtc-client
v2.3.0
Published
usc rtc client
Readme
音视频通话 webrtc sdk 开发手册
1.安装
//使用yarn安装
yarn add usc-rtc-client
//使用npm安装
npm install usc-rtc-client2.简单使用 demo
//用户A,演示主叫端的逻辑
import { RtcClient, createRtcClient, RtcSessionEvent, RtcClientEvent } from "usc-rtc-client"
const startClientA = async () => {
//初始化一个webrtc的sdk客户端,这里的本地号码是7001,server对应的是信令服务器地址由线下对接进行提供
let client = await createRtcClient({ server: "xxxxxxx", localNumber: "7001" })
//创建一个针对于7002的视频会话
let session = client.createSession("7002")
//这里演示给会话注册一个接受到远端视频流的事件监听
session.on(RtcSessionEvent.stream, (data) => {
//data 的stream 属性就是视频流信息,你可以像下面这样把他放到一个视频容器下进行播放
let playerContainer = document.getElementById("player")
playerContainer.srcObject = data.stream
playerContainer.play()
})
//给会话注册断开事件,
session.on(RtcSessionEvent.bye, () => alert("BYE!!!!!!"))
//给会话选择本端所提供的流,这里使用的是摄像头的流信息
await session.useDefaultDevice(document.getElementById("localPalyer") as HTMLVideoElement)
//开始呼叫
session.start()
}
startClientA()
//====================================
//用户B,演示被叫端的逻辑
const startClientB = async (client: RtcClient) => {
//初始化一个webrtc的sdk客户端,这里的本地号码是7021,server对应的是信令服务器地址由线下对接进行提供
let client = await createRtcClient({ server: "xxxxxxx", localNumber: "7002" })
//给客户端注册一个接收到视频通话邀请的事件监听
client.on(RtcClientEvent.invite, async (session) => {
//注册接受到远端流信息的事件监听
session.on(RtcSessionEvent.stream, (data: any) => {
//播放流信息的代码,如上面的主叫端一样
})
////给会话注册断开事件,
session.on(RtcSessionEvent.bye, () => alert("BYE!!!!!!"))
//给会话选择本端所提供的流,这里使用的是摄像头的流信息
await session.useDefaultDevice(document.getElementById("localPalyer") as HTMLVideoElement);
//接受会话
await session.acceptCall();
})
}
startClientB()
3.sdk 说明
此项目为 typescript 项目,按照 npm 的约定生成了 type 描述文件,具体 api 说明请在 type 描述文件中进行查看,以下是一些重要的 Api 说明:
1.createRtcClient
此方法用来创建一个 webrtc 的终端,内部已经封装了心跳,信令交互,以及视频处理的逻辑,api 描述如下:
declare const createRtcClient: (config: {
server: string; //信令服务器的地址
localNumber: string; //自己的号码
heartTime?: number; //向信令服务器的发送心跳的间隔时间,默认是30s
expireTime?: number; //注册信令的过期时间,默认是60s
}) => Promise<RtcClient>; //注意,这里返回的是Promise的对象,resolve前置是在创建websocket链接之后发送一次心跳信息,等待服务器返回确认信息使用事例:
import { createRtcClient } from 'usc-rtc-client'
let client = await createRtcClient({ server: 'wss://xxxxxx.com/', localNumber: '7001' })2.RtcClient
RtcClient 是用来实例化 webrtc 终端的对象,内部主要方法如下
- createSession(remoteNumber?: string): RtcSession 用来来创建一个会话
- destroy() 销毁客户端,会销毁所有创建的会话以及断开与信令服务器的链接,与信令服务器断开链接之后也会自动触发此方法
- reConnect() 重新连接信令服务器,如果之前处于连接状态会自动调用 destory 的方法
- on(event:RtcClientEvent,cb:Function) 注册相关监听事件
- sendPttMessage() 此方法是后续添加,是用来发送 ptt 消息,消息具体格式请参考 ptt 对讲以及 ims 开发文档
事例代码:
import { createRtcClient, RtcClientEvent, RtcSession, RtcCalledSession } from 'usc-rtc-client'
let client = await createRtcClient({ server: 'wss://xxxxxx.com/', localNumber: '7001' })
let session: RtcSession = client.createSession('7002')
client.on(RtcClientEvent.invite, (calledSession: RtcCalledSession) => {})3.RtcClientEvent
RtcClientEvent 表示底层封装向上抛出的一些业务与非业务的生命周期事件,具体如下
- connected 信令服务器连接成功事件
- disconnected 信令服务器断开事件,连接失败这里也可以触发
- invite 收到音视频通话邀请的事件
- FPREInfo 收到 ptt 消息通知事件(目前主要为 ptt 组在线用户查询消息,具体格式参考 ptt 对讲以及 ims 开发文档)
事例代码
import { createRtcClient , RtcClientEvent,RtcCalledSession } from "usc-rtc-client"
let client = await createRtcClient({server:"wss://xxxxxx.com/",localNumber:"7001"})
client.on(RtcClientEvent.disconnected,()=>{
console.log("socket is closed")
})
client.on(RtcClientEvent.invite,(session:RtcCalledSession)=>{
console.log("revice invite,do something plz...")
})4.RtcSession
RtcSession 是用来实例化 RtcClient 所创建的音视频会话的对象,内部主要方法如下
- addRemote(remoteNumber: string) 修改需要呼叫的用户
- rejectCall() 拒绝邀请,使用于创建会话过程中的会议类型变更
- acceptCall() 接受邀请,使用于创建会话过程中的会议类型变更
- start() 开始呼叫
- destroy() 销毁会话,如果呼叫已经开始,会先向被叫端发送 bye 请求
- getAllInputResource() 获取所有的音视频输入源
- useDefaultDevice(container: HTMLVideoElement) 使用默认的本地音视频输入设备(麦克风与摄像头),并将本地的流媒体渲染到对应的 video 上(可以不传),实际上是下面几个方法的组合使用
- useAudio(deviceId) 使用对应 deviceId 的麦克风设备作为音频流,如果 deviceId 为 undefined,那么使用默认的设备作为音频源
- useDeviceVideo(deviceId) 使用对应 deviceid 摄像头设备的视频流,如果 deviceId 为 undefined,那么使用默认的设备作为视频源
- useDeskTopVideo() 使用桌面流作为视频流输入
- disableAudio() 禁用本地流的音频输入,本地音频静音
- disableVideo() 禁用本地流的视频输入,关闭本地视频画面
- enableAudio() 启用本地音频输入
- enableVideo() 启用本地视频输入
- renderLocalStreamToContainer(container:HTMLVideoElement|HTMLAudioElement) 将本地的流媒体使用对应的 video 或者 audio 元素进行播放
- on(event:RtcSessionEvent,cb:Function) 注册相关监听事件
- addDeviceChangeListener(listener) 注册物理输入设备变化监听事件,如摄像头拔线之类的操作
- removeDeviceChangeListener(listener) 删除物理输入设备变化监听事件(组件化的框架如 react 之类的,在组件销毁的时候使用这个方法进行回收)
事例代码
...
session.addRemote("7003")
await session.useDefaultDevice(document.getElementById("localplayer"))
session.start()5.RtcCalledSession
RtcCalledSession 是用来实例化被叫会话的对象,主要方法如下
- getRemoteNumber() 获取呼叫自己的远端号码
- rejectCall() 拒绝邀请,使用于创建会话与会话过程中的会议类型变更
- acceptCall() 接受邀请,使用于创建会话与会话过程中的会议类型变更
- destroy() 销毁会话,如果呼叫已经开始,会先向被叫端发送 bye 请求
- getAllInputResource() 获取所有的音视频输入源
- useDefaultDevice(container: HTMLVideoElement) 使用默认的本地音视频输入设备(麦克风与摄像头),并将本地的流媒体渲染到对应的 video 上(可以不传),实际上是下面几个方法的组合使用
- useAudio(deviceId) 使用对应 deviceId 的麦克风设备作为音频流,如果 deviceId 为 undefined,那么使用默认的设备作为音频源
- useDeviceVideo(deviceId) 使用对应 deviceid 摄像头设备的视频流,如果 deviceId 为 undefined,那么使用默认的设备作为视频源
- useDeskTopVideo() 使用桌面流作为视频流输入
- disableAudio() 禁用本地流的音频输入,本地音频静音
- disableVideo() 禁用本地流的视频输入,关闭本地视频画面
- enableAudio() 启用本地音频输入
- enableVideo() 启用本地视频输入
- renderLocalStreamToContainer(container:HTMLVideoElement|HTMLAudioElement) 将本地的流媒体使用对应的 video 或者 audio 元素进行播放
- on(event:RtcSessionEvent,cb:Function) 注册相关监听事件
- addDeviceChangeListener(listener) 注册物理输入设备变化监听事件,如摄像头拔线之类的操作
- removeDeviceChangeListener(listener) 删除物理输入设备变化监听事件(组件化的框架如 react 之类的,在组件销毁的时候使用这个方法进行回收
事例代码
...
client.on(RtcClientEvent.invite,async (session:RtcCalledSession)=>{
await session.useDefaultDevice(document.getElementById("localplayer"))
session.acceptCall()
})6.RtcSessionEvent
会话流程事件
- bye 会话结束事件
- stream 会话收到流的事件
- localAudioChange 本地音频流设备发生了变化
- localVideoChange 本地视频流设备发生了变化
- reInvite 会话过程中修改会话类型会重新发 invite 比如原先只是语音通话,然后变成视频会话
- localStreamChange 本地流发生了变化
事例代码
...
session.on(RtcSessionEvent.stream,({audioOnly,stream})=>{
if(audioOnly){
//use audio to play
}else{
let playerContainer = document.getElementById("player")
playerContainer.srcObject = stream
playerContainer.play()
}
})
...
session.on(RtcSessionEvent.bye,()=>{
//close something
alert("bye")
})4.监控模式
监控模式是本 SDK 提供的一种特殊功能,允许在不建立完整通话会话的情况下,单向播放远程流。
1.使用 playRemote 函数
import { playRemote } from 'usc-rtc-client'
// 监控模式播放远程流
async function startMonitoring(remoteNumber) {
try {
// 创建监控播放器
const monitorPlayer = document.getElementById('monitor-player')
// 开始监控远程号码
const player = await playRemote(
'ws://your-signaling-server-url', // 信令服务器地址
remoteNumber, // 要监控的远程号码
monitorPlayer, // 视频容器
{
audioOnly: false, // 是否仅播放音频
heartTime: 30, // 心跳间隔时间(秒)
expireTime: 60, // 信令过期时间(秒)
onerror: (error) => {
console.error('监控错误:', error)
}
}
)
console.log('监控会话创建成功')
// 5秒后结束监控
setTimeout(() => {
player.destroy()
console.log('监控会话已结束')
}, 5000)
} catch (error) {
console.error('监控会话创建失败:', error)
}
}2.playRemote 函数参数说明
| 参数名 | 类型 | 描述 | 是否必填 | | ------------ | ---------------- | ---------------- | -------- | | server | string | 信令服务器地址 | 是 | | remoteNumber | string | 要监控的远程号码 | 是 | | container | HTMLVideoElement | 视频播放容器 | 是 | | options | object | 可选配置项 | 否 |
options 配置项:
| 配置项 | 类型 | 默认值 | 描述 | |--------|------|--------|------| | audioOnly | boolean | false | 是否仅播放音频 | | expireTime | number | 60 | 注册信令的过期时间(秒) | | heartTime | number | 30 | 向信令服务器发送心跳的间隔时间(秒) | | onerror | function | undefined | 错误回调函数,在发生错误时调用 |
3.监控模式的特点
- 监控模式使用'anonymous'作为本地号码,无需登录即可使用
- 监控模式是单向的,被监控方不会收到任何通知
- 监控模式支持配置心跳间隔和信令过期时间
- 监控模式适合用于监控、预览等场景
5.ptt 消息接口
1.启用 ptt 客户端功能
本客户端(webrtc)集成了 ptt 对讲客户端的全部接口,功能等同于 usc-ptt-sdk 提供的功能,若想单独使用 ptt 功能,可直接安装 usc-ptt-sdk 即可;以下是集成的功能及用法说明:
await createRtcClient({
server,
localNumber,
enablePtt: true, // 初始化客户端时,可传入enablePtt,true表示启用ptt客户端,不传或flase表示不使用ptt客户端,
pttServer: 'ws://localhost:40000', //pttClient服务器地址,当enablePtt为true时,需要传入该字段,否则初始化失败
})2.RtcClient 集成的 ptt 接口
当启用 ptt 客户端时,ptt 客户端提供的接口将可以在 RtcClient 上使用,接口说明如下: joinGroup('100114','10000008003','10.168.1.17',20023) //加入对讲组 quitGroup() //退出组 talk() //说话 stop() //停止说话
事例代码
const client = await createRtcClient({ server, localNumber, enablePtt: true, pttServer: 'ws://localhost:40000' })
// 加入组
client.joinGroup('100114', '10000008003', '10.168.1.17', 20023)
//退出组
client.quitGroup()
//本客户端用户需要说话时调用(须先加入一个对讲组)
client.talk()
//停止说话
client.stop()
以上接口只作为向服务器发送请求消息, 结果将以客户端事件的形式响应给用户, 开发人员只需要在初始化完客户端后监听相应的事件即可3.PTTEvent
PTTEvent 表示底层封装向上抛出的一些业务与非业务的生命周期事件,具体如下
const clientEvent = {
ERROR:'error', //错误事件
SPEAKER:'speaker', //有用户说话或停止说话时触发该事件
TALKING:'talking', //自己说话或停止说话时触发该事件
ACTIVE_GROUP:'activeGroup', //加入组或退出组时触发该事件
JOIN_INFO:'joinInfo', //有用户加入或退出组时触发该事件
}事例代码
const client = await createRtcClient({ server, localNumber, enablePtt: true, pttServer: 'ws://localhost:40000' })
client.on(PTTEvent.ACTIVE_GROUP, (e) => {
// e为空字符串时表示当前没有加入任何组,否则e为加入的组的组id
if (e) console.log(`进入组:${e}`)
else console.log('退出组')
})
client.on(PTTEvent.SPEAKER, (e) => {
// e为空字符串时表示当前没有人在说话或当前说话人停止说话,否则e为说话人的ptt号
if (e) console.log(`${e}正在说话`)
else console.log(`停止说话`)
})
client.on(PTTEvent.TALKING, (e) => {
// e为true时表示当前自己在说话,否则表示自己停止说话
if (e) console.log(`您正在说话`)
else console.log(`您已停止说话`)
})
client.on(PTTEvent.JOIN_INFO, (e) => {
// 有用户加入组时通知,
// e的结构如下
// {
// user:8003, 加入的用户号
// group:100114, 组id
// code:1, 0表示退出组,1表示加入组
// }
console.log(`用户加入通知:${JSON.stringify(e)}`)
})6.最佳实践
1.错误处理
在使用 SDK 时,建议添加适当的错误处理,以提高应用的稳定性:
try {
const client = await createRtcClient({ server, localNumber })
// 使用client
} catch (error) {
console.error('创建RTC客户端失败:', error)
// 处理错误,如提示用户重试
}
// 会话事件处理
session.on(RtcSessionEvent.stream, ({ stream }) => {
if (stream) {
const container = document.getElementById('player')
container.srcObject = stream
container.play().catch((error) => {
console.error('播放流失败:', error)
// 处理自动播放失败的情况
})
}
})2.资源释放
在不需要使用 SDK 或会话结束时,建议释放相关资源:
// 销毁会话
session.destroy()
// 销毁客户端
client.destroy()
// 释放流资源
releaseStreamFromContainer(container)3.设备管理
在使用音视频设备前,建议先检查设备是否可用:
const resources = await session.getAllInputResource()
if (resources.audio.length === 0) {
console.error('没有检测到麦克风')
// 处理没有麦克风的情况
}
if (resources.video.length === 0) {
console.error('没有检测到摄像头')
// 处理没有摄像头的情况
}7.限制
- 目前仅做了 chrome 浏览器的兼容支持,要求 chrome 内核版本在 90 以上
- webrtc 接口调用的页面需要依赖 https 环境(本地地址如 localhost,127.0.0.1 除外),页面运行环境如果无法提供支持,请在 chrome 的 unsafely-treat-insecure 中添加白名单来让页面支持 webrtc(chrome 浏览器打开 chrome://flags/#unsafely-treat-insecure-origin-as-secure,在 Insecure origins treated as secure 下面的输入框中添加您的网页地址,选择右边的 enable,重启浏览器生效)
8.API 参考
核心函数
createRtcClient(config): 创建 RTC 客户端playRemote(server, remoteNumber, container, options): 监控模式播放远程流renderStreamToContainer(stream, container): 渲染流到容器releaseStreamFromContainer(container): 从容器释放流enableLog(): 启用日志
事件类型
RtcClientEvent: 客户端事件RtcSessionEvent: 会话事件PTTEvent: PTT 事件
类
RtcClient: RTC 客户端类RtcSession: 会话类RtcCalledSession: 被叫会话类
9. 声明导出
SDK 提供了完整的 TypeScript 类型声明,以下是所有可导出的内容列表:
核心函数声明
export function createRtcClient(config: { server: string; localNumber: string; heartTime?: number; expireTime?: number; enablePtt?: boolean; pttServer?: string }): Promise<RtcClient>
export function playRemote(
server: string,
remoteNumber: string,
container: HTMLVideoElement,
options?: {
expireTime?: number;
heartTime?: number;
onerror?: (e: any) => void;
audioOnly?: boolean;
}
): Promise<{
destroy: () => void;
}>
export function renderStreamToContainer(stream: MediaStream, container: HTMLVideoElement | HTMLAudioElement): void
export function releaseStreamFromContainer(container: HTMLVideoElement | HTMLAudioElement): void
export function enableLog(): void
export function disableLog(): void
export function setLogLevel(level: 'debug' | 'info' | 'warn' | 'error'): void类和接口
export class RtcClient extends RtcClientBase {
createSession(remoteNumber: string): RtcSession
on(event: RtcClientEvent, callback: Function): void
off(event: RtcClientEvent, callback: Function): void
destroy(): void
reConnect(): Promise<void>
sendPttMessage(data: any): void
// PTT 相关方法
joinGroup(groupId: string, userId: string, ip: string, port: number): void
quitGroup(): void
talk(): void
stop(): void
}
export class RtcSession extends RtcAbstractSession {
addRemote(remoteNumber: string): void
rejectCall(): void
acceptCall(): void
start(): void
destroy(): void
getAllInputResource(): Promise<{ audio: MediaDeviceInfo[]; video: MediaDeviceInfo[] }>
useDefaultDevice(container?: HTMLVideoElement): Promise<void>
useAudio(deviceId?: string): Promise<void>
useDeviceVideo(deviceId?: string): Promise<void>
useDeskTopVideo(): Promise<void>
disableAudio(): void
disableVideo(): void
enableAudio(): void
enableVideo(): void
renderLocalStreamToContainer(container: HTMLVideoElement | HTMLAudioElement): void
on(event: RtcSessionEvent, callback: Function): void
off(event: RtcSessionEvent, callback: Function): void
addDeviceChangeListener(listener: () => void): void
removeDeviceChangeListener(listener: () => void): void
}
export class RtcCalledSession extends RtcAbstractSession {
getRemoteNumber(): string | undefined
rejectCall(): void
acceptCall(): void
destroy(): void
getAllInputResource(): Promise<{ audio: MediaDeviceInfo[]; video: MediaDeviceInfo[] }>
useDefaultDevice(container?: HTMLVideoElement): Promise<void>
useAudio(deviceId?: string): Promise<void>
useDeviceVideo(deviceId?: string): Promise<void>
useDeskTopVideo(): Promise<void>
disableAudio(): void
disableVideo(): void
enableAudio(): void
enableVideo(): void
renderLocalStreamToContainer(container: HTMLVideoElement | HTMLAudioElement): void
on(event: RtcSessionEvent, callback: Function): void
off(event: RtcSessionEvent, callback: Function): void
addDeviceChangeListener(listener: () => void): void
removeDeviceChangeListener(listener: () => void): void
}
export abstract class RtcAbstractSession {
sessionId: string
remoteNumber: string | undefined
getRemoteNumber(): string | undefined
destroy(): void
}事件类型声明
export enum RtcClientEvent {
connected = 'connected',
disconnected = 'disconnected',
invite = 'invite',
}
export enum RtcSessionEvent {
bye = 'bye',
stream = 'stream',
localAudioChange = 'localAudioChange',
localVideoChange = 'localVideoChange',
reInvite = 'reInvite',
localStreamChange = 'localStreamChange',
}
export enum PTTEvent {
PTTInfo = 'PTTInfo',
FPREInfo = 'FPREInfo',
IMSInfo = 'IMSInfo',
ERROR = 'error',
SPEAKER = 'speaker',
TALKING = 'talking',
ACTIVE_GROUP = 'activeGroup',
JOIN_INFO = 'joinInfo',
}常量和工具类型
export enum RtcInviteStatus {
timeout = 408,
reject = 488,
busy = 486,
notfound = 404,
notAllowedMedia = 415,
ok = 200,
}
export enum RtcCalledType {
audio = 'audio',
video = 'video',
audioVideo = 'audioVideo',
unknown = 'unknown',
}完整导入示例
// 导入所有内容
import * as UscRtcClient from 'usc-rtc-client'
// 按需导入
import {
createRtcClient,
playRemote,
authDevice,
RtcClient,
RtcSession,
RtcInviteStatus,
RtcClientEvent,
RtcSessionEvent,
PTTEvent,
RtcCalledType,
renderStreamToContainer,
releaseStreamFromContainer,
closeStreamAndTrack,
enableLog,
disableLog,
} from 'usc-rtc-client'