需要将语音系统的拨打功能,集成到办公CRM系统。实现办公OA,在浏览器中拨打,接听电话功能。
需求改造如下:落地移动IMS,增加了媒体服务器双机负载。网页拨打电话使用开源的Opensips+Rtpengine(当然现在很多厂商的新版SBC都带webrtc功能,价格参考 50并发≈3万rmb)。
Opensips+Rtpengine为此花了一些时间补充了一版, oepnsips搭建指南填坑版。(opensips教程基本是不可用的)。jssip demo, 99%发布的都是不可用的。jssip可用demo
html+jssip-3.10.0.js,后面基于demo开发了web拨号盘,通过网页悬浮框拨打电话。webrtc 谷歌插件。
<!DOCTYPE html>
<html>
<head>
<title>JsSIP + WebRTC </title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="Author" content="foruok" />
<meta name="description" content="JsSIP based example web application." />
<script src="jssip-3.10.0.js" type="text/javascript"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="login-page" style="width: 424px; height: 260px; background-color: #f2f4f4; border: 1px solid grey; padding-top: 4px">
<table border="0" frame="void" width="418px">
<tr>
<td class="td_label" width="160px" align="right"><label for="sip_uri">SIP URI:</label></td>
<td width="258px"><input style="width:250px" id="sip_uri" type="text" placeholder="SIP URI (i.e: sip:alice@example.com)" value="sip:8003@example.com"/></td>
</tr>
<tr>
<td class="td_label" align="right"><label for="sip_password">分机密码:</label></td>
<td><input style="width:250px" id="sip_password" type="password" placeholder="SIP password" value="a123456789"/></td>
</tr>
<tr>
<td class="td_label" align="right"><label for="ws_uri">WSS URI:</label></td>
<td><input style="width:250px" id="ws_uri" class="last unset" type="text" placeholder="WSS URI (i.e: wss://example.com)" value="wss://example.com:1443/wss"/></td>
</tr>
<tr>
<td class="td_label" align="right"><label class="input_label" for="sip_phone_number">被叫号码:</label></td>
<td><input style="width:250px" id="sip_phone_number" type="text" placeholder="sip:3000@example.com" value="9001"></td>
</tr>
<tr>
<td colspan="2" align="center"><button onclick="testStart()"> 登录 </button></td>
</tr>
<tr>
<td colspan="2" align="center"><button onclick="testCall()"> 呼叫 </button></td>
</tr>
<tr hidden="true">
<td colspan="2" align="center"><button onclick="captureLocalMedia()"> Capture Local Media</button></td>
</tr>
</table>
<audio id="audioElement"></audio>
</div>
<div hidden="true" style="width: 424px; height: 324px;background-color: #333333; border: 2px solid blue; padding:0px; margin-top: 4px;">
<video id="videoView" width="420px" height="320px" autoplay ></video>
</div>
</body>
<script type="text/javascript">
var outgoingSession = null;
var incomingSession = null;
var currentSession = null;
var videoView = document.getElementById('videoView');
var constraints = {
audio: true,
video: false,
mandatory: {
maxWidth: 640,
maxHeight: 360
}
};
URL = window.URL || window.webkitURL;
var localStream = null;
var userAgent = null;
function gotLocalMedia(stream) {
console.info('Received local media stream');
localStream = stream;
videoView.src = URL.createObjectURL(stream);
}
function captureLocalMedia() {
console.info('Requesting local video & audio');
navigator.webkitGetUserMedia(constraints, gotLocalMedia, function(e){
alert('getUserMedia() error: ' + e.name);
});
}
function testStart(){
var sip_uri_ = document.getElementById("sip_uri").value.toString();
var sip_password_ = document.getElementById("sip_password").value.toString();
var ws_uri_ = document.getElementById("ws_uri").value.toString();
console.info("get input info: sip_uri = ", sip_uri_, " sip_password = ", sip_password_, " ws_uri = ", ws_uri_);
var socket = new JsSIP.WebSocketInterface(ws_uri_);
var configuration = {
sockets: [ socket ],
//outbound_proxy_set: ws_uri_,
uri: sip_uri_,
password: sip_password_,
//register: true,
//session_timers: false
session_timers: false, // 启用会话计时器(根据RFC 4028)
user_agent: "Aegis WebRTC v1.0",
contact_uri: sip_uri_,
// outbound_proxy_set: wsUri,
// display_name: String(phone),
autostart: true, // 自动连接
register: true, // 自动就绪
};
userAgent = new JsSIP.UA(configuration);
userAgent.on("connecting", (args) => {
// this.$notify({
// title: "提示",
// message: "开始尝试连接",
// duration: 500,
// });
console.info("开始尝试连接", args);
});
userAgent.on('registered', function(data){
console.info("registered: ", data.response.status_code, ",", data.response.reason_phrase);
});
userAgent.on('registrationFailed', function(data){
console.log("registrationFailed, ", data);
//console.warn("registrationFailed, ", data.response.status_code, ",", data.response.reason_phrase, " cause - ", data.cause);
});
userAgent.on('registrationExpiring', function(){
console.warn("registrationExpiring");
});
userAgent.on('newRTCSession', function(e){
console.log(`新的${e.originator === "local" ? "外呼" : "来电"}`, e);
const session = e.session;
this.session = session;
const peerconnection = session.connection;
if (e.originator === "local") {
// 打电话
peerconnection.addEventListener("addstream", (event) => {
const audio = document.querySelector(".audio");
audio.srcObject = event.stream;
});
} else {
//document.getElementById("localVideo").play();
// 接电话
this.dialogVisible = true;
this.isg = true;
this.callers = session.remote_identity.uri.user;
}
// 接通,在这一步可以处理音频播放
// 接通并不代表对方已经接受,接通代表 滴 滴 滴
session.on("confirmed", () => {
console.info("接通中");
// peerconnection.addEventListener("track", (event) => {
// const audioView = document.querySelector("#conversation");
// audioView.srcObject = event.streams[0];
// audioView.play();
// audioView.volume = 1;
// });
const audioElement = document.getElementById("audioElement");
audioElement.autoplay = true;
const stream = new MediaStream();
const receivers = session.connection.getReceivers();
if (receivers)
receivers.forEach((receiver) => stream.addTrack(receiver.track));
audioElement.srcObject = stream;
// 最后都要播放
audioElement.play();
});
// 接听失败
session.on("failed", (mdata) => {
const myAuto = document.getElementById("localVideo");
myAuto.pause();
myAuto.load();
console.info("来电的时候 拒接或者 还没接听对方自己就挂断了");
});
// 接听成功
session.on("accepted", (response, cause) => {
// 嘟嘟嘟
//const conversation = document.getElementById("conversation");
//conversation.pause();
//conversation.load();
//this.Answering = true;
console.info("接听成功");
});
// 接听成功后 挂断
session.on("ended", () => {
this.dialogVisible = false;
this.InCall = false;
});
// 通话被挂起
session.on("hold", (data) => {
const org = data.originator;
if (org === "local") {
log("通话被本地挂起:", org);
} else {
// log("通话被远程挂起:", org);
}
});
// 通话被继续
session.on("unhold", (data) => {
const org = data.originator;
if (org === "local") {
// );
} else {
// log("通话被远程继续:", org);
}
});
});
userAgent.on('newMessage', function(data){
if(data.originator == 'local'){
console.info('onNewMessage , OutgoingRequest - ', data.request);
}else{
console.info('onNewMessage , IncomingRequest - ', data.request);
}
});
console.info("call register");
userAgent.start();
};
// Register callbacks to desired call events
var eventHandlers = {
'progress': function(e) {
console.log('call is in progress');
},
'failed': function(e) {
console.log('call failed: ', e);
},
'ended': function(e) {
console.log('call ended : ', e);
},
'confirmed': function(e) {
console.log('call confirmed');
}
};
function testCall(){
var sip_phone_number_ = document.getElementById("sip_phone_number").value.toString();
var options = {
'pcConfig': {
'rtcpMuxPolicy': "negotiate",
},
'rtcOfferConstraints': {
'offerToReceiveAudio': '1',
'offerToReceiveVideo': '0',
},
'eventHandlers': eventHandlers,
'mediaConstraints': {
'audio': true,
'video': false,
},
};
//outgoingSession = userAgent.call('sip:3000@192.168.40.96:5060', options);
outgoingSession = userAgent.call(sip_phone_number_, options);
}
</script>
</html>