SoftPhoneForWebrtc.ts :
import JsSIP from 'jssip'
// 自定义数据进行通信的socket事件
function selfSocketEvent(data) {
if (window.$ak.socketMyself) {
window.$ak.socketMyself.emit('selfEvent', {
externalNo: `webrtc-${localStorage.getItem('clientPhone')}`, //客户号
internalNo: localStorage.getItem('seatCode'), //坐席号
msgContent: data,
})
}
}
let vm = null
// JsSIP.debug.enable('JsSIP:Transport JsSIP:RTCSession*')
import eventBus from '@/util/Bus'
const sendThis = (_this) => {
vm = _this
}
function SoftPhone() {
this.version = '0.1'
this.session = null
this.ua = null
this.logEnable = true
this.URI = null
this.SIP_SCHEME = 'sip'
this.localStream = null
this.remoteMedia = null
this.localMedia = null
this._contactUri = ''
this.socket = null
this.messager = null
this.flowList = []
this._init()
}
export { sendThis, SoftPhone }
SoftPhone.prototype = {
_init() {
this._initSoftPhoneObject()
},
_initSoftPhoneObject() {
//JSSIP参数修改,FreeSWITCH要求session_expires会话间隔大于120s
// Session-Expires这个字段的值必须要大于Min-SE,否则就认为会话结束。
// 所以你最初应该将Session-Expires设置一个比较大的值,或者就直接不用Session-Expires字段
JsSIP.C.SESSION_EXPIRES = 3600
JsSIP.C.MIN_SESSION_EXPIRES = 3600
},
_bindUAEvent(ua) {
ua.on('registered', (data) => this._UAEventHandler('registered', data))
ua.on('unregistered', (data) => this._UAEventHandler('unregistered', data))
ua.on('registrationFailed', (data) =>
this._UAEventHandler('registrationFailed', data)
)
ua.on('registrationExpiring', () => this._registrationExpiring())
ua.on('connecting', (data) => this._UAEventHandler('connecting', data))
ua.on('connected', (data) => this._UAEventHandler('connected', data))
ua.on('disconnected', (data) => this._UAEventHandler('disconnected', data))
ua.on('newRTCSession', (data) => this._newRTCSessionHandler(data))
ua.on('newMessage', (data) => this._UAEventHandler('newMessage', data))
ua.on('sipEvent', (data) => this._UAEventHandler('sipEvent', data))
ua.on('newInfo', (data) => this._UAEventHandler('newInfo', data))
},
_UAEventHandler(status, data) {
if (this.logEnable && console && typeof console.log == 'function') {
console.log('%c=======' + status + '=======', 'color:blue')
}
if (status == 'connected') {
console.log('软电话已经连接')
this.session = data.session
}
if (status == 'newInfo') {
console.log('来新消息了')
}
if (status == 'sipEvent') {
console.log('触发了sipEvent事件')
}
},
_newRTCSessionHandler(data) {
this.session = data.session
this._bindRTCSessionEvent(this.session)
},
_registrationExpiring() {
console.log('注册超时,重新再注册一次')
this.ua.register()
},
_bindRTCSessionEvent(session) {
;[
'newInfo',
'peerconnection',
'connecting',
'sending',
'progress',
'accepted',
'confirmed',
'ended', //已建立的通话结束时触发。
'failed',
'newDTMF',
'hold',
'unhold',
'muted',
'unmuted',
'reinvite',
'update',
'refer',
'replaces',
'sdp',
'getusermediafailed',
'peerconnection:createofferfailed',
'peerconnection:createanswerfailed',
'peerconnection:setlocaldescriptionfailed',
'peerconnection:setremotedescriptionfailed',
].forEach((key) =>
session.on(key, (data) => {
this._RTCSessionEventHandler(key, data)
})
)
},
_RTCSessionEventHandler(status, data) {
if (this.logEnable && console && typeof console.log == 'function') {
console.log('%c*******' + status + '*******', 'color:green')
this.flowList.push(status)
}
switch (status) {
case 'peerconnection:createanswerfailed':
console.log('peerconnection:createanswerfailed')
break
case 'peerconnection':
this._peerconnectionEventHandler(data)
break
case 'accepted':
this._acceptedEventHandler(data)
break
case 'progress':
// 客户端的呼叫只有走到了progress阶段,坐席侧的振铃才应该被弹出来
selfSocketEvent('progress')
console.log('========progress=======')
break
case 'failed':
vm.onCallFail()
selfSocketEvent('')
this.remoteMedia.pause()
this.localMedia.pause()
break
case 'confirmed':
// 设置坐席状态为通话中
console.log('%c*******' + '通话中' + '*******', 'color:green')
vm.isTaking = true
eventBus.$emit('_isTaking', true)
break
case 'ended':
// 设置坐席状态为未在通话中
vm.isTaking = false
eventBus.$emit('_isTaking', false)
this._endedEventHandler(data)
break
case 'muted':
eventBus.$emit('_muteSucess')
break
case 'unmuted':
eventBus.$emit('_unmutedSucess')
break
}
},
// 已建立的通话结束时触发。
_endedEventHandler(data) {
console.log('已挂机')
vm.callEnded()
},
_peerconnectionEventHandler(data) {},
_acceptedEventHandler() {
var self = this
this.remoteMedia.srcObject = this.session.connection.getRemoteStreams()[0]
this.localMedia.srcObject = this.session.connection.getRemoteStreams()[0]
let constraints = {
audio: true,
video: false,
}
navigator.mediaDevices
.getUserMedia(constraints)
.then(function(stream) {
self.getLocalMedia(stream)
})
.catch(function(err) {
console.log(err)
})
},
_createMediaDOM(id, dom) {
var div = document.createElement('div')
div.style.width = '0'
div.style.height = '0'
var audio = document.createElement('AUDIO')
audio.id = id
audio.setAttribute('autoplay', 'autoplay')
div.appendChild(audio)
document.body.appendChild(div)
this[dom] = audio
},
setMediaObj(remote, local) {
remote && (this.remoteMedia = remote)
local && (this.localMedia = local)
!this.remoteMedia && this._createMediaDOM('_remoteMedia', 'remoteMedia')
!this.localMedia && this._createMediaDOM('_localMedia', 'localMedia')
},
getLocalMedia(stream) {
this.session.connection.addStream(stream)
},
toSipURI(number) {
return new JsSIP.URI(this.SIP_SCHEME, number, this.URI.host).toString()
},
toContactURI(number) {
return new JsSIP.URI(this.SIP_SCHEME, number, this.URI.host, null, {
transport: 'ws',
}).toString()
},
captureLocalMedia() {
let constraints = {
audio: true,
video: false,
}
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) => console.log(stream))
.catch((err) => console.log(err))
},
/**
* 注册
* @param sipUser 用户名
* @param sipPwd 密码
* @param sipServer 服务地址
*/
register(sipUser, sipPwd, sipServer) {
return new Promise((resolve, reject) => {
this.setMediaObj()
if (sipUser == '' || sipPwd == '' || sipServer == '') {
return
}
this.URI = JsSIP.Grammar.parse(sipServer, 'Request_URI')
this._contactUri = this.toContactURI(sipUser)
var _sipUser = this.toSipURI(sipUser)
var socket = new JsSIP.WebSocketInterface(sipServer)
this.socket = socket
var configuration = {
sockets: [socket],
uri: _sipUser,
password: sipPwd,
register: false,
contact_uri: this._contactUri,
register_expires: 3600, //注册有效时间
connection_recovery_max_interval: 3600 * 24 * 1000,
connection_recovery_min_interval: 3600 * 24 * 1000,
no_answer_timeout: 120,
}
this.ua = new JsSIP.UA(configuration)
// console.log(this.ua._configuration, 'ua配置')
this._bindUAEvent(this.ua)
this.ua.start()
this.ua.register()
this.ua.on('registered', (data) => resolve(data))
this.ua.on('registrationFailed', (data) => reject(data))
})
},
/**
* 注销
*/
unregister() {
if (this.ua) {
this.ua.unregister()
this.ua.stop()
}
return true
},
/**
* 外呼
* @param dtmfNumber 被叫号码
*/
placeCall(seatNumber) {
this.flowList = []
var sipNumber = this.toSipURI(seatNumber)
var eventHandlers = {
confirmed: function(data) {
console.log('电话接通confirmed')
vm.onCallReceive()
},
ended: function(data) {
console.log('通话结束ended')
},
}
var options = {
eventHandlers: eventHandlers,
mediaConstraints: { audio: true, video: false },
}
this.session = this.ua.call(sipNumber, options)
},
/**
* 接听
*/
answer() {
this.remoteMedia.loop = true
this.localMedia.loop = true
this.remoteMedia.volume = 1
this.localMedia.volume = 1
this.remoteMedia.play()
this.localMedia.play()
var options = {
mediaConstraints: { audio: true, video: false },
}
this.session.answer(options)
},
/**
* 挂机
*/
reject() {
if (this.session) {
this.session.terminate()
}
},
/**
* 保持
*/
hold() {
this.session.hold()
},
/**
* 拾回
*/
retrieveHold() {
this.session.unhold()
},
/**
* 静音
*/
mute(e) {
this.session.mute()
if (e === '坐席') {
this.remoteMedia.pause()
}
if (e === '客户') {
this.localMedia.pause()
}
},
/**
* 取消静音
*/
unmute(e) {
this.session.unmute()
if (e === '坐席') {
this.remoteMedia.play()
}
if (e === '客户') {
this.localMedia.play()
}
},
// 清空flowList
clearFlow() {
this.flowList = []
},
/**
* 二次拨号
* @param dtmfNumber 二次拨号号码
*/
dtmf(dtmfNumber) {
console.log(dtmfNumber)
this.session.sendDTMF(dtmfNumber)
},
/**
* 设置在线
*/
setReady() {
if (this.ua && !this.ua.isRegistered()) {
this.ua.register()
}
},
/**
* 设置离线
*/
setNoDisturb() {
if (this.ua && this.ua.isRegistered()) {
this.ua.unregister()
}
},
/**
* 电话转接
*/
transfer(number) {
var sipNumber = this.toSipURI(number)
this.session.refer(sipNumber)
},
}