import {UA as Agent, WebSocketInterface as Socket, debug} from 'jssip';
import EventEmitter from "./eventEmitter";
debug('JsSIP:RTCSession:DTMF');
export default class SipClient extends EventEmitter {
#debug = false;
#state = 'unknown';
#agent = null;
#session = null;
#player = null;
#server = '';
constructor(opts = {}) {
super();
this.#debug = opts.debug || false;
const player = document.createElement('audio');
player.autoplay = true;
this.#player = player;
}
register(opts = {server: '', aor: '', displayName: ''}) {
this.#state = 'unknown';
this.#agent = this.#session = null;
this.#server = opts.server;
this.#initAgent({
sockets: [new Socket(this.#server)],
uri: 'sip:' + opts.aor,
password: opts.password,
display_name: opts.displayName,
no_answer_timeout: 50, // 电话呼入无人应答超时
register: true, // 自动注册
session_timers: false//启用会话计时器(根据RFC 4028)
});
this.#agent.start();
}
get debug() {
return this.#debug;
}
set debug(val) {
this.#debug = val;
}
get state() {
return this.#state;
}
get session() {
return this.#session;
}
/**
* 呼叫
* @param {string} aor 对方的SIP号码
*/
call(aor) {
this.#log('-> 呼叫', aor);
const opts = {
mediaConstraints: {audio: true, video: false},
pcConfig: {iceServers: []},
eventHandlers: {
peerconnection: evt => this.#peerConnectionEventHandler(evt),
}
}
this.localSession = this.#agent.call(`sip:${aor}`, opts);
}
/**
* 取消呼叫
*/
cancel() {
this.#log('-> 取消')
if (this.#session) {
this.#session.terminate();
} else {
this.#log('呼出的会话不存在');
}
}
/**
* 拒绝对方
*/
decline() {
this.#log('-> 拒绝')
if (this.#session) {
this.#session.terminate();
} else {
this.#log('呼入的会话不存在');
}
}
/**
* 接受对方
*/
accept() {
this.#log('-> 接受')
if (this.#session) {
this.#session.answer({
mediaConstraints: {audio: true, video: false},
pcConfig: {iceServers: []},
});
} else {
this.#log('呼入的会话不存在');
}
}
/**
* 挂断
*/
hangup() {
this.#log('-> 挂断')
if (this.#session) {
this.#session.terminate();
} else {
this.#log('会话不存在')
}
}
sendDtmf() {
}
/**
* 设置状态
* @param {string} state
*/
#setState(state) {
this.#log('setState', state);
if(state === 'idle'){
this.#session = null;
this.#player.srcObject = null;
}
if (this.#state !== state) {
this.emit('state', state);
}
this.#state = state;
}
#sessionEventHandler(evt) {
const {originator, session} = evt;
if (originator === 'remote') {
this.remoteSession = session;
// 如果正在通话中, 回复忙
if(this.#session){
session.terminate({
status_code: 486
});
return;
}
const {uri, display_name} = session.remote_identity;
const remote = {user: uri.user, name: display_name || uri.user, host: uri.host};
this.emit('callIn', remote);
}
session.on('peerconnection', evt => {
this.#peerConnectionEventHandler(evt);
}).on('connecting', evt => {
}).on('progress', evt => {
this.#log(evt.originator === 'remote' ? '等待对方接听' : '等待自己接听', evt);
this.#setState('waiting');
}).on('accepted', evt => {
}).on('confirmed', evt => {// 确认呼叫后触发
this.#log(evt.originator === 'remote' ? '自己已接受' : '对方已接受', evt);
this.#setState('calling');
}).on('sdp', evt => {
this.#log(evt.originator === 'remote' ? '对方SDP' : '自己SDP', evt);
}).on('newDTMF', evt => {
this.#log('收到DTMF', evt);
}).on('ended', evt => {
this.#log(evt.originator === 'remote' ? '对方挂断' : '自己挂断', evt);
this.#setState('idle');
}).on('failed', evt => this.#failedEventHandler(evt));
this.#session = session;
}
#peerConnectionEventHandler(evt) {
this.#log('======peerconnection', evt);
evt.peerconnection.onaddstream = evt => {
this.#log('onAddStream', evt.stream.getTracks());
this.#player.srcObject = evt.stream;
}
}
#failedEventHandler(evt) {
this.#log('failed', evt.cause, evt);
const {originator, cause} = evt;
const isRemote = originator === 'remote';
switch (cause) {
case 'Canceled':
this.#log(isRemote ? '对方已取消' : '自己已取消');
this.emit('canceled', originator);
break;
case 'Unavailable':
this.#log(isRemote ? '对方不可用' : '自己不可用');
break;
case 'No Answer':
this.#log(isRemote ? '对方无应答' : '自己无应答');
this.emit('noAnswer');
break;
case 'Rejected':
this.#log(isRemote ? '对方拒绝' : '自己拒绝');
break;
case 'SIP Failure Code':
this.#log(isRemote ? '对方呼叫失败' : '自己呼叫失败');
break;
default:
this.#log('failedEventHandler', cause, originator);
break;
}
this.#setState('idle');
}
/**
* 初始化
*/
#initAgent(opts = {}) {
this.#log('createAgent', opts);
const agent = new Agent(opts);
agent.on('connected', _ => this.#setState('connected'));
agent.on('disconnected', _ => this.#setState('disconnected'));
// 注册成功,data:Response JsSIP.IncomingResponse收到的SIP 2XX响应的实例
agent.on('registered', _ => this.#setState('idle'));
agent.on('unregistered', _ => this.#setState('unregistered'));
//注册失败而被解雇,data:Response JsSIP.IncomingResponse接收到的SIP否定响应的实例,如果失败是由这样的响应的接收产生的,否则为空
agent.on('registrationFailed', evt => {
this.#setState('registrationFailed');
});
//1.在注册到期之前发射几秒钟。如果应用程序没有为这个事件设置任何监听器,JsSIP将像往常一样重新注册。
//2.如果应用程序订阅了这个事件,它负责ua.register()在registrationExpiring事件中调用(否则注册将过期)。
//3.此事件使应用程序有机会在重新注册之前执行异步操作。对于那些在REGISTER请求中的自定义SIP头中使用外部获得的“令牌”的环境很有用。
agent.on('registrationExpiring', evt => {
});
agent.on('newRTCSession', evt => {
this.#log('newRTCSession', evt);
this.#sessionEventHandler(evt);
});
this.#agent = agent;
}
#log(...args) {
this.#debug && console.log('SipClient', new Date().toLocaleTimeString(), ...args);
}
}