写在前面:FreeSWITCH作为服务器,通过SIP协议,Web端使用jssip+webrtc和其他软电话进行通信
一、先配置FreeSWITCH(用的版本1.6.20)的配置:
1 、修改vars.xml文件,找到下面字段,并设置
<X-PRE-PROCESS cmd="set" data="internal_ssl_enable=true"/>
<X-PRE-PROCESS cmd="set" data="external_auth_calls=true"/>
<X-PRE-PROCESS cmd="set" data="external_ssl_enable=true"/>
2、修改 autoload_configs/acl.conf.xml文件,增加acl选项
<list name="wan" default="allow">
<node type="allow" cidr="10.10.21.0/22"/>
<node type="allow" cidr="10.250.250.0/24"/>
</list>
在配置文件 sip_profiles/internal.xml 增加如下配置
<param name="apply-candidate-acl" value="wan"/>
默认情况下建立连接失败,提示下面错误,并呼叫失败
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [WARNING] switch_core_media.c:3451 NO candidate ACL defined, Defaulting to wan.auto
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [DEBUG] switch_core_media.c:3481 Save audio Candidate cid: 1 proto: udp type: host addr: 10.10.21.32:52786
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [DEBUG] switch_core_media.c:3523 Searching for rtp candidate.
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [DEBUG] switch_core_media.c:3523 Searching for rtcp candidate.
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [DEBUG] switch_core_media.c:3567 sofia/internal/82s6ps5e@80ug9oo63ltj.invalid no suitable candidates found.
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [DEBUG] switch_core_media.c:4767
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [NOTICE] sofia.c:8240 Hangup sofia/internal/82s6ps5e@80ug9oo63ltj.invalid [CS_CONSUME_MEDIA] [INCOMPATIBLE_DESTINATION]
5fe89e1d-bb0c-473b-8877-d3648acd4076 2018-08-09 20:28:15.217384 [DEBUG] switch_core_codec.c:248 sofia/internal/1009@192.168.20.78 Restore previous codec PCMA:8.
a21d347d-5622-451b-a1db-d241ca823e4d 2018-08-09 20:28:15.217384 [DEBUG] sofia.c:1453 Channel is already hungup.
在配置文件 sip_profiles/internal.xml 增加如下配置,解决这个问题
<param name="apply-candidate-acl" value="localnet.auto"/>
<param name="apply-candidate-acl" value="rfc1918.auto"/>
二、编写Web客户端
样式如下: 不是专业前端,没有做布局
启动之后,就和正常的SIP软终端一致了,通过FreSWITCH拨打其他软终端,测试正常。
源代码:基于JsSIP的客户端,参考网上,并自己修改并调试通过
<!DOCTYPE html>
<html>
<head>
<title>JsSIP 对接 FreeSWITCH</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="Author" content="wangweichao"/>
<meta name="description" content="JsSIP based example web application."/>
<script src="js/jssip-3.0.13.min.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" value="sip:1009@192.168.20.78"/></td>
</tr>
<tr>
<td class="td_label" align="right"><label for="sip_password">SIP Password:</label></td>
<td><input style="width:250px" id="sip_password" type="password" value="654321"/></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" value="ws://192.168.20.78:5066"/>
</td>
</tr>
<tr>
<td class="td_label" align="right"><label class="input_label" for="sip_phone_number">SIP Phone Info:</label>
</td>
<td><input style="width:250px" id="sip_phone_number" type="text" value="1007"></td>
</tr>
<tr>
<td colspan="2" align="center">
<button onclick="testStart()"> 初始化</button>
</td>
</tr>
<tr>
<td colspan="5" align="center">
<button onclick="reg()"> 注册</button>
</td>
</tr>
<tr>
<td colspan="5" align="center">
<button onclick="unReg()"> 注销</button>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<button onclick="testCall()"> 呼叫</button>
</td>
</tr>
<tr>
<td colspan="5" align="center">
<button onclick="hangup()"> 挂断</button>
</td>
</tr>
</table>
</div>
<video id="videoView" width="420px" height="320px" autoplay></video>
</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 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
};
userAgent = new JsSIP.UA(configuration);
//注册成功
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 (data) {
console.info('onNewRTCSession: ', data);
//通话呼入
if (data.originator == 'remote') {
console.info("incomingSession, answer the call----------------------");
incomingSession = data.session;
data.session.answer({
'mediaConstraints': {
'audio': true,
'video': false,
mandatory: {maxWidth: 640, maxHeight: 360}
}, 'mediaStream': localStream
});
} else {
console.info("outgoingSession");
outgoingSession = data.session;
outgoingSession.on('connecting', function (data) {
console.info('onConnecting - ', data.request);
currentSession = outgoingSession;
outgoingSession = null;
});
}
data.session.on('accepted', function (data) {
console.info('onAccepted - ', data);
if (data.originator == 'remote' && currentSession == null) {
currentSession = incomingSession;
incomingSession = null;
console.info("setCurrentSession - ", currentSession);
}
});
data.session.on('confirmed', function (data) {
console.info('onConfirmed - ', data);
if (data.originator == 'remote' && currentSession == null) {
currentSession = incomingSession;
incomingSession = null;
console.info("setCurrentSession - ", currentSession);
}
});
data.session.on('sdp', function (data) {
console.info('onSDP, type - ', data.type, ' sdp - ', data.sdp);
});
data.session.on('progress', function (data) {
console.info('onProgress - ', data.originator);
if (data.originator == 'remote') {
console.info('onProgress, response - ', data.response);
}
});
data.session.on('peerconnection', function (data) {
console.info('onPeerconnection - ', data.peerconnection);
data.peerconnection.onaddstream = function (ev) {
console.info('onaddstream from remote ----------- ', ev);
videoView.src = URL.createObjectURL(ev.stream);
};
});
});
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 = {
'eventHandlers': eventHandlers,
'mediaConstraints': {
'audio': true, 'video': false,
mandatory: {maxWidth: 640, maxHeight: 360}
},
'mediaStream': localStream
};
outgoingSession = userAgent.call(sip_phone_number_, options);
}
function reg() {
console.log('register----------->');
userAgent.register();
}
function unReg() {
console.log('unregister----------->');
userAgent.unregister(true);
}
function hangup() {
console.log('hangup----------->');
userAgent.terminateSessions();
}
</script>
</html>