/** * 对象方式的培训助手JS,以对象的方式初始化trainingHelper * 示例代码如下: * @author Asy * @time 2020-12-25 14:36 */ (function ($) { var $this; // 当前对象 var ua; var session; var oNotifICall; var ringtone = null; var ringBackTone = null; var keyboard = null; var oSipSessionCall; // sipPhone处理 mySipPhone = { init: function (options) { // 可供外部调用的方法和属性 var o = $.extend({ uri: "", // sip服务器的IP和端口 wss: "", // webSocket 地址 user: "", // sip分机号 pwd: "", // 分机号密码 callNumber: "", // 培训默认外呼号码 options: { audioBW: '', // 设置音频带宽 videoBW: '', // 设置视频带宽 ice: '', // ICE穿透NAT/防火墙 stunUrl: '', // STUN穿透 turnUrl: '', // TURN穿透 turnUserName: '', // TURN用户名 turnCredential: '' // TURN证书 }, connecting: function (evt) {}, // webSocket连接中 connected: function (evt) {}, // webSocket已连接 disconnected: function (evt) {}, // webSocket连接断开 registered: function (evt) {}, // SIP注册成功 unregistered: function (evt) {}, // SIP未注册 registrationFailed: function (evt) {}, // SIP注册失败 busy: function (evt) {}, // 线路忙 ringing: function (num) {}, // 呼入振铃,参数为呼入号码 progress: function (localStream) {}, // 外呼处理中 dialing: function (evt) {}, // 外呼中 confirmed: function (evt) {}, // 外呼已确认 accepted: function (callType, evt) {}, // 呼叫接通,callType:inbound 呼入,outbound 呼出 stream: function (callType, localStream, remoteStream) {}, // 获取到数据流,callType:inbound 呼入,outbound 呼出 failed: function (callType, evt) {}, // 呼叫失败,callType:inbound 呼入,outbound 呼出 ended: function (callType, evt) {}, // 呼叫结束,callType:inbound 呼入,outbound 呼出 refer: function (evt) {} // 转接 }, options); $this = this; $this.data('mySipPhone', o); }, /** 获取注入的数据和事件 */ getData: function () { var o = $this.data('mySipPhone'); return o; }, /** 注册SIP */ register: function () { try { // 如果当前已经注册过,则直接跳过 if(ua){ return false; } var o = mySipPhone.getData(); // 启用浏览器消息通知 if (window.webkitNotifications && window.webkitNotifications.checkPermission() != 0) { window.webkitNotifications.requestPermission(); } var socket = new PhoneJs.WebSocketInterface(o.wss); socket.onconnect = function () { console.log('mySipPhone:webSocket连接中'); }; var configuration = { sockets: [socket], uri: o.user + '@' + o.uri, password: o.pwd, authorization_user: o.user, display_name: o.user, register: false, // dont't automatic register trace_sip: true //connection_recovery_max_interval: 30, //connection_recovery_min_interval: 2, }; ua = new PhoneJs.UA(configuration); if (o.options.videoBW != undefined && o.options.videoBW != '') { ua.setVideoBandwidth(o.options.videoBW); } if (o.options.audioBW != undefined && o.options.audioBW != '') { ua.setAudioBandwidth(o.options.audioBW); } ua.on('connecting', function (e) { o.connecting(e); }); ua.on('connected', function (e) { o.connected(e); ua.registrator().setExtraHeaders([ 'X-Foo: x-bar' ]); ua.register(); // connect to SIP server }); ua.on('disconnected', function (e) { o.disconnected(e); }); ua.on('registered', function (e) { o.registered(e); }); ua.on('unregistered', function (e) { o.unregistered(e); }); ua.on('registrationFailed', function (e) { o.registrationFailed(e); }); ua.on('newRTCSession', function (e) { session = e.session; var count = Object.keys(ua._sessions).length; if (count > 1) { var extraHeaders = ['X-Foo: foo', 'X-Bar: bar']; var status_code = "486"; var reason_phrase = "BUSY HERE"; var opts = { 'extraHeaders': extraHeaders, 'status_code': status_code, 'reason_phrase': reason_phrase }; session.terminate(opts); o.busy(e); } else { if (session.direction == 'incoming') { mySipPhone._startRingtone($('[name="ringtone"]').val()); var sRemoteNumber = (session.remote_identity._display_name || session.remote_identity._user || 'unknown'); o.ringing(sRemoteNumber); mySipPhone.showNotification(sRemoteNumber); session.on('peerconnection', function (e) { var peerconnection = session.connection; peerconnection.addEventListener('addstream', function (e) { var localStream = peerconnection.getLocalStreams()[0]; o.stream("inbound", localStream, e.stream); }); }); session.on('progress', function (e) { console.log('Call progress event 呼入振铃...'); var peerconnection = session.connection; peerconnection.addEventListener('addstream', function (e) { var localStream = peerconnection.getLocalStreams()[0]; o.stream("inbound", localStream, e.stream); }); }); session.on('failed', function (e) { session = null; mySipPhone._stopRingtone(); o.failed("inbound", e); }); session.on('ended', function (e) { session = null; o.ended("inbound", e); }); session.on('accepted', function (e) { mySipPhone._stopRingtone(); o.accepted("inbound", e); }); } } }); ua.start(); } catch (e) { console.error(e); } }, /** * 签出 **/ unregister: function () { if (ua) { ua.stop(); // shutdown all sessions ua = null; } }, /** * 来电应答 **/ answer: function (videoEnabled) { try { var o = mySipPhone.getData(); if (session) { var optAnswer = { 'mediaConstraints': { 'audio': true, 'video': videoEnabled } }; var stunUrl = o.options.stunUrl; var turnUrl = o.options.turnUrl; var turnUserName = o.options.turnUserName; var turnCredential = o.options.turnCredential; if (stunUrl && stunUrl.length > 0) { optAnswer.pcConfig = { iceServers: [{ urls: [stunUrl] }] }; } if (turnUrl && turnUrl.length > 0) { if ((turnUserName && turnUserName.length > 0) && (turnCredential && turnCredential.length > 0)) { if (optAnswer.pcConfig && optAnswer.pcConfig.iceServers) { optAnswer.pcConfig.iceServers.push({ 'urls': turnUrl, 'username': turnUserName, 'credential': turnCredential }); } else { optAnswer.pcConfig = { iceServers: [{ 'urls': turnUrl, 'username': turnUserName, 'credential': turnCredential }] }; } } else { if (optAnswer.pcConfig && optAnswer.pcConfig.iceServers) { optAnswer.pcConfig.iceServers.push({ 'urls': turnUrl }); } else { optAnswer.pcConfig = { iceServers: [{ 'urls': turnUrl }] }; } } } session.answer(optAnswer); } else { alert('错误 : No session'); } } catch (error) { console.error(error); } }, /** * 外呼 * @param videoEnabled true 表示视频外呼,false 表示语音外呼 * @param callNum 外呼的号码 * */ call: function (videoEnabled, callNum) { try { var o = mySipPhone.getData(); var eventHandlers = { 'progress': function (data) { // 制造振铃 mySipPhone._startRingbackTone(); var localStream = null; if(videoEnabled){ var peerconnection = session.connection; localStream = peerconnection.getLocalStreams()[0]; } o.progress(localStream); }, 'failed': function (data) { session = null; mySipPhone._stopRingbackTone(); o.failed("outbound", data); }, 'confirmed': function (data) { o.confirmed(data); }, 'ended': function (data) { session = null; o.ended("outbound", data); }, 'connecting': function (data) { o.dialing(data); }, 'accepted': function (data) { mySipPhone._stopRingbackTone(); o.accepted("outbound", data); }, 'refer': function (data) { o.refer(data); } }; var options = { 'eventHandlers': eventHandlers, 'extraHeaders': ['X-Foo: foo', 'X-Bar: bar', 'video : ' + videoEnabled], 'mediaConstraints': { 'audio': true, 'video': videoEnabled }, 'rtcOfferConstraints': { offerToReceiveAudio: 1, offerToReceiveVideo: 1 } }; var stunUrl = o.options.stunUrl; var turnUrl = o.options.turnUrl; var turnUserName = o.options.turnUserName; var turnCredential = o.options.turnCredential; //var pcConfig = { iceServers: [ { urls : [ iceServer ] } ] }; if (stunUrl && stunUrl.length > 0) { options.pcConfig = { iceServers: [{ urls: [stunUrl] }] }; } if (turnUrl && turnUrl.length > 0) { if ((turnUserName && turnUserName.length > 0) && (turnCredential && turnCredential.length > 0)) { if (options.pcConfig && options.pcConfig.iceServers) { options.pcConfig.iceServers.push({ 'urls': turnUrl, 'username': turnUserName, 'credential': turnCredential }); } else { options.pcConfig = { iceServers: [{ 'urls': turnUrl, 'username': turnUserName, 'credential': turnCredential }] }; } } else { if (options.pcConfig && options.pcConfig.iceServers) { options.pcConfig.iceServers.push({ 'urls': turnUrl }); } else { options.pcConfig = { iceServers: [{ 'urls': turnUrl }] }; } } } session = ua.call(callNum, options); var peerconnection = session.connection; peerconnection.addEventListener('addstream', function (e) { console.log("===========外呼"); var localStream = peerconnection.getLocalStreams()[0]; o.stream("outbound", localStream, e.stream); }); } catch (error) { console.error(error); } }, // holds or resumes the call hold: function () { if (session) { var bHold = session.isOnHold(); if (bHold.local) { console.log('unhold'); session.unhold(); } else { console.log('hold'); session.hold(); } } }, // Mute or Unmute the call mute: function () { if (session) { var bMute = session.isMuted(); if (bMute && bMute.audio) { console.log('unmute'); session.unmute({ 'audio': true, // Local audio is muted 'video': true // Local audio is not muted }); } else { console.log('mute'); session.mute({ 'audio': true, // Local audio is muted 'video': true // Local audio is not muted }); } } }, // terminates the call (SIP BYE or CANCEL) hangUp: function () { if (session) { var extraHeaders = ['X-Foo: foo', 'X-Bar: bar']; var options = { 'extraHeaders': extraHeaders }; session.terminate(options); } }, // 发送按键给服务器 sendDTMF: function (c) { if (oSipSessionCall && c) { if (oSipSessionCall.dtmf(c) == 0) { try { if(keyboard == null){ keyboard = new Audio(); keyboard.src = "./sounds/dtmf.wav"; } keyboard.play(); } catch (e) { console.error(e); } } } }, /** * 当有来电时展示消息提醒 * @param s_number 来电号码 * */ showNotification: function (s_number) { // permission already asked when we registered if (window.webkitNotifications && window.webkitNotifications.checkPermission() == 0) { if (oNotifICall) { oNotifICall.cancel(); } oNotifICall = window.webkitNotifications.createNotification('images/sipml-34x39.png', 'Incaming call', 'Incoming call from ' + s_number); oNotifICall.onclose = function () { oNotifICall = null; }; oNotifICall.show(); } }, // transfers the call transfer: function (transferNum) { if (session) { if (transferNum == undefined || transferNum == null || transferNum == '') { transferNum = prompt('请输入转接号码', ''); } if (transferNum != undefined && transferNum != "") { session.refer(transferNum); } } }, consult: function (number) { if (session) { if (number == undefined || number == null || number == '') { number = prompt('请输入咨询号码', ''); } if (number != undefined && number != "") { // TODO 发起三方通话 session.refer(number); } } }, meeting: function (number) { if (session) { if (number == undefined || number == null || number == '') { number = prompt('请输入咨询号码', ''); } if (number != undefined && number != "") { // TODO 发起三方通话 session.refer(number); } } }, monitor: function (number) { if (session) { if (number == undefined || number == null || number == '') { number = prompt('请输入咨询号码', ''); } if (number != undefined && number != "") { // TODO 发起三方通话 session.refer(number); } } }, _startRingtone: function (num) { try { /* if(ringtone == null){ ringtone = new Audio(); ringtone.loop = true; if(num == 2 ) ringtone.src = "../../sounds/3.wav"; else if(num == 3 ) ringtone.src = "../../sounds/4.wav"; else ringtone.src = "sounds/ringtone.mp3"; } */ //ringtone.play(); } catch (e) { console.error(e); } }, _stopRingtone: function () { try { if(ringtone != null) { ringtone.pause(); } } catch (e) { console.error(e); } }, _startRingbackTone: function () { try { if(ringBackTone == null){ ringBackTone = new Audio(); ringBackTone.loop = true; ringBackTone.src = "../../sounds/ringbacktone.wav"; } //ringBackTone.play(); } catch (e) { console.error(e); } }, _stopRingbackTone: function () { try { if(ringBackTone != null){ ringBackTone.pause(); } } catch (e) { console.error(e); } } } $.fn.mySipPhone = function (method) { if (mySipPhone[method]) { return mySipPhone[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === 'object' || !method) { return mySipPhone.init.apply(this, arguments); } else { $.error('Method ' + method + ' does not exist on mySipPhone'); } }; })(jQuery)