Package Exports
- @toolmain/jssip
Readme
Overview
jssip 库的封装,加入中文错误提示和ice candidate策略
支持一个 UA 多个 RTCSession 同时拨打和接听
使用 vue库使用Typescript编写并提供
.d.ts文件,可用在react或者vue环境中
Change log
v1.0.1 锁定jssip版本号为3.10.0
v1.0.3 修改useMedia::playUrl内部播放逻辑
v1.0.4 删除vue依赖
v1.0.5 解决IOS无法通过动态创建 audio 标签播放音频轨道
Usage 用法
包目录结构。使用时应包含所有文件
++jssip
--package.json
++dist
--index.js
--index.d.ts由于浏览器的限制(WebRTC以及麦克风权限限制),使用时必须在https站点下
拨号流程
1.实例化SipWorker类
2.通过实例方法postMessage进行 初始化 登录 拨号 挂断 静音 取消静音 暂停 取消暂停操作
3.通过实例方法on监听拨号后的事件,详细的事件可在该文档中的Type 拨号命令以及消息事件枚举部分查看
4.每当发起一次通话或者收到电话,jssip库会产生一个session会话,用户可以通过SipWorker类的on方法监听这个session
示例代码
import { SipWorker, cmd, on, CauseData } from "path/to/你放置该库的根路径"
import { ulid } from "ulid" // 或者 uuid
/**
* SipWorker 是拨号类,必须先实例化
*/
const sip = new SipWorker()
/**
* 初始化,拨号之前要初始化
*/
sip.postMessage(cmd.INIT, {
/**
*
* 传id或者HTMLAudioElement
* 用于播放音频流
*
* ios中动态创建的audio标签无法播放音频
* 安卓和PC可以动态创建标签,可以不传该字段
*/
audio: document.getElementById("audioId"),
viaTransport: "wss", // 固定wss
uri: "sip:your_account@192.168.1.1", // 格式:sip:账号@拨号服务器IP
sipWorkerid: ulid(), // 随机字符串
sipServer: ["wss://192.168.1.1:8089/ws"],// 格式:wss://拨号服务器IP:端口/ws
// 如果没有,留空数组
sipIce: ["stun:192.168.1.1:3478"], // 格式:stun:拨号服务器IP:STUN端口
/**
* 自动注册,如果false,需要手动调用
* sip.postMessage(cmd.LOGIN, null);
*/
register: true,
password: "your_account's password", // 密码
realm: "192.168.1.1", // 格式:拨号服务器IP
})
/**
* 登录
*/
sip.postMessage(cmd.LOGIN, null)
/**
* 登出
*/
sip.postMessage(cmd.LOGOUT, null)
/**
* 拨号
*/
sip.postMessage(cmd.CALL, {
target: "10086",// 要拨打的号码
})
/**
* 挂电话
*/
sip.postMessage(cmd.HANGUP, {
isTimeout: false,
sessionId: `sessionId`,
})
/**
* 通话session状态发生变化
* 收到电话或者拨号会触发此事件
*/
sip.on(on.ON_RTCSESSION_STATE, (res: CauseData) => {
switch (res.msg) {
case on.ON_SESSTION_ACCEPTED: // 已接受
case on.ON_SESSTION_CONFIRMED: // 已接听
break;
case on.ON_SESSTION_FAILED: // 通话失败
case on.ON_SESSTION_ENDED: // 通话结束
break;
case on.ON_NEWRTCSESSION: // 新的通话session到来
//! 呼出
if (res.sessionOrigin == "outgoing") {
console.log("会话ID:",res.sessionId;)
} else {
console.log("会话ID:",res.sessionId;)
const sessionId = res.sessionId;
// console.log(">>>呼入", uSip.sip.sessionById(sessionId));
const fromUser = uSip.sip.sessionById(sessionId).session.remote_identity.uri.user;
console.log("呼入者:",fromUser)
// 接听
uSip.sip.postMessage(cmd.ANSWER, {
sessionId,
});
// 挂断/拒接
uSip.sip.postMessage(cmd.HANGUP, {
sessionId,
});
}
}
})
// 注册
sip.on(on.ON_REGISTERED, () => {
console.log("已注册")
});
sip.on(on.ON_UNREGISTERED, () => {
console.log("未注册")
});
// 静音
sip.on(on.ON_MUTE, () => {
console.log("静音")
});
sip.on(on.ON_UNMUTE, () => {
console.log("取消静音")
});
sip.on(on.ON_RESUME, () => {
console.log("继续")
});
sip.on(on.ON_HOLD, () => {
console.log("保持")
});
// 失败
sip.on(on.ON_DISCONNECTED, res => {
/**
* 触发原因:
* 1. 由于服务器使用自签证书,需要手动访问拨号服务器的 https 站点手动验证,端口和地址为cmd.INIT时的sipServer字段值,并且wss改为https
* 2. 由于网络环境不稳定等其他原因
*
*/
console.log("socket 连接失败",res)
});
sip.on(on.ON_REGISTRATIONFAILED, res => {
console.log("socket 连接成功,但是sip注册失败",res)
});
// etc...额外可供使用的工具
import { useMedia, useRetry } from "path/to/你放置该库的根路径"
// 播放音频流或者音频url
/**
export interface AutoMedia {
playRemote: (media: readonly MediaStream[]) => void
playUrl: (url: string) => void
stopPlayUrl: () => void
}
*/
const media = useMedia()
// eg:
media.playUrl("xxx.mp3")
/**
export interface Retry {
// 更新最大重试次数
updateMaxRetry: (id: string, maxRetry: number) => void
// 获取重试次数
getRetryTime: (id: string) => number
// 是否到达最大重试次数
isMax: (id: string) => boolean
// 重试,如果可以重试,返回true,否则返回false
retry: (id: string) => boolean
// 重置重试次数
reset: (id: string) => void
// 延迟重试
// id 重试唯一id
// cb 回调函数
// increase 每次重试递增时间
// 返回: 0:超过重试次数,大于0:下次重试时间
delayRetry: (id: string, cb: () => void, increase?: number) => number
}
*/
const retry = useRetry()Type 拨号命令以及消息事件枚举
// sip 操作命令
export declare enum cmd {
INIT = "INIT", // 数据初始化操作
LOGIN = "LOGIN", // 连接socket并登录sip服务器
CALL = "CALL", // 拨打电话
ANSWER = "ANSWER", // 接听电话
LOGOUT = "LOGOUT", // 断开socket并且登出sip服务器
HANGUP = "HANGUP", // 挂断电话
HOLD = "HOLD", // 保持通话
UNHOLD = "ONHOLD", // 取消保持通话,继续
MUTE = "MUTE", // 静音
UNMUTE = "UNMUTE", // 取消静音
REGISTER = "REGISTER", // 注册sip服务
SESSTION_DEL = "SESSTION_DEL", // 删除sessionId,并且挂电话
UNREGISTER = "UNREGISTER", // 保持socket连接,但登出sip服务
NOTIFY = "NOTIFY", // unused
EMPTY = "EMPTY", // unused
}
// sip 消息事件
export declare enum on {
ON_TIME_OUT = "Request Timeout", // 请求超时
ON_REGISTERED = "registered", // 已注册
ON_UNREGISTERED = "unregistered", // 未注册(或已登出)
ON_REGISTRATIONFAILED = "registrationFailed", // 注册失败
ON_REGISTRATIONEXPIRING = "registrationExpiring", // 注册即将过期,库会自动重新注册
ON_CONNECTING = "connecting", // socket连接中
ON_CONNECTED = "connected", // socket 已连接
ON_DISCONNECTED = "disconnected", // socket 已断开
ON_NEWRTCSESSION = "newRTCSession", // 新的通话,呼入或者呼出
ON_RTCSESSION_STATE = "new_RTCSession_state", // 通话session的状态
ON_SESSION_PROGRESS = "session_progress", // 183 session建立中,早期媒体(early media) 时期
ON_SESSTION_CONNECTING = "session_connecting", // 通话线路正在连接
ON_SESSTION_ACCEPTED = "session_accepted", // 通话已被接受
ON_SESSTION_CONFIRMED = "session_confirmed", // 通话已确认(已接通)
ON_SESSTION_ENDED = "session_ended", // 通话结束
ON_SESSTION_FAILED = "session_failed", // 通话失败
ON_SESSION_ANSWER_FAILED = "session_answer_failed", // 通话应答失败
ON_RESUME = "onResume", // 已继续
ON_HOLD = "onHold", // 已保持
ON_MUTE = "onMute", // 已静音
ON_UNMUTE = "onUnmute", // 已取消静音
ON_EVENT = "event", // unused
ON_NEWOPTIONS = "newOptions", // unused
ON_SIPEVENT = "sipEvent", // unused
ON_NEWMESSAGE = "newMessage", // unused
ON_SESSION_RECALL = "session_recall", // unused
ON_RE_REGISTERED = "re_register", // unused
ON_RE_LOGIN = "re_login", // unused
ON_SESSION_TIMEOUT = "session_req_timeout", // unused
ON_LISTEN_EVENT = "call_event", // unused
}
// sip事件消息格式
export declare type CauseData = {
/**
* JsSIP实例id
*/
id: string
/**
* sip账号
*/
uri: string
/**
* 呼入时:呼叫方的uri
*/
fromUri?: string
/**
* 事件ON_* on.ts 中声明
*/
msg: string
/**
* 200 500 etc.
*/
code: number
/**
*
* 当CauseData由session发出时,该字段标识JsSIP每个UA下不同的RTCSession id
*/
sessionId?: string
/**
* 当CauseData由session发出时,该字段标识`主叫`或者`被叫`
*/
sessionOrigin?: "incoming" | "outgoing"
/**
* 事件的中文翻译
*/
msgCN?: string
/**
* 标识sip,socket,session,ua消息的发出方
*/
originator?: Originator.LOCAL | Originator.REMOTE | Originator.SYSTEM
/**
* 如果出现错误,错误的原因
*/
cause?: string
/**
* 错误原因中文
*/
causeCN?: string
/**
* sip 或者 websocket状态码
*/
statusCode?: number
/**
* sip 或者 websocket 状态码对应的具体原因
*/
reason?: string
/**
* 消息生成时间
*/
time?: string | number | Date | undefined
/**
* 额外cause数据
*/
extra?: {
suffix?: string
prefix?: string
}
/**
* 额外data,每个msg可能有额外的数据
*/
data?: any
}
export declare class SipWorker extends JSSipWraper {
constructor(cfg?: JsSipConfig)
postMessage<T extends cmd>(type: T, data: CMDData<T>): void
}postMessage 命令
export declare interface CMDMap {
[cmd.INIT]: CMDInit;
[cmd.CALL]: CMDCall;
[cmd.ANSWER]: CMDAnswer;
[cmd.HANGUP]: CMDHangup;
[cmd.LOGIN]: CMDLogin;
[cmd.LOGOUT]: CMDLogout;
[cmd.EMPTY]: CMDEmpty;
[cmd.HOLD]: CMDHold;
[cmd.SESSTION_DEL]: CMDSESSTION_DEL;
[cmd.UNHOLD]: CMDUnhold;
[cmd.MUTE]: CMDMute;
[cmd.UNMUTE]: CMDUnmute;
[cmd.REGISTER]: CMDRegister;
[cmd.UNREGISTER]: CMDUnregister;
[cmd.NOTIFY]: CMDNotify;
}
export declare type CMDCall = {
target: string;
options?: CallOptions; =>> import type { CallOptions } from 'jssip/lib/UA';
};
export declare type CMDHangup = {
/**
* 是否以客户端假定是拨打超时的方式挂断电话
*/
sessionId: string;
isTimeout: boolean;
};
export declare type CMDAnswer = {
sessionId: string;
};
测试
可访问 https://vivcode.cn/jssip 测试