<!DOCTYPE html>
|
<html>
|
<head>
|
<meta charset="utf-8" />
|
<title>SIP电话Demo</title>
|
<meta http-equiv="Pragma" content="no-cache">
|
<meta http-equiv="Expires" content="-1">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="cache-control" content="no-store">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<script language="javascript" type="text/javascript" src="./jssip-3.10.0.js"></script>
|
</head>
|
<body style="background-color:darkgray;">
|
|
|
|
<table border="1" cellspacing="0" bordercolor="gray" style="width:auto;">
|
<tr>
|
<td style="text-align:center;width:120px;">
|
<span> 服务器地址:</span>
|
</td>
|
<td style="text-align: center; width: 250px;">
|
<input id="tbServer" type="text" style="width:200px;text-align:left;" value="192.168.1.96" />
|
</td>
|
</tr>
|
<tr>
|
<td style="text-align:center;width:120px;">
|
<span> 分机号:</span>
|
</td>
|
<td style="text-align: center; width: 250px;">
|
<input id="tbExtension" type="text" style="width:200px;text-align:left;" value="8005" />
|
</td>
|
</tr>
|
<tr>
|
<td style="text-align:center;width:120px;">
|
<span> 密码:</span>
|
</td>
|
<td style="text-align: center; width: 250px;">
|
<input id="tbPassword" type="text" style="width:200px;text-align:left;" value="8005" />
|
</td>
|
</tr>
|
<tr>
|
<td style="text-align:center;width:120px;" colspan="2">
|
<button onclick="f_register()">注册</button>
|
</td>
|
</tr>
|
<tr>
|
<td style="text-align:center;width:120px;">
|
<span> 呼叫号码:</span>
|
</td>
|
<td style="text-align: center; width: 250px;">
|
<input id="tbPhoneNo" type="text" style="width:200px;text-align:left;" value="013958077789" />
|
</td>
|
</tr>
|
<tr>
|
<td style="text-align:center;width:120px;" colspan="2">
|
<button onclick="f_makecall()">软外拨</button>
|
<button onclick="f_hangup()">挂断</button>
|
</td>
|
</tr>
|
</table>
|
|
|
<script type="text/javascript">
|
var cur_session = undefined;
|
var ua = null;
|
|
String.prototype.Right = function (i) {
|
return this.slice(this.length - i, this.length);
|
};
|
Date.prototype.Format = function (fmt) { //author: meizz
|
var o = {
|
"M+": this.getMonth() + 1, //月份
|
"d+": this.getDate(), //日
|
"h+": this.getHours(), //小时
|
"m+": this.getMinutes(), //分
|
"s+": this.getSeconds(), //秒
|
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
|
"S": ('00' + this.getMilliseconds()).Right(3) //毫秒
|
};
|
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
|
for (var k in o)
|
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
|
return fmt;
|
};
|
|
function logger_info(message, ...optionalParams) {
|
console.log("[" + new Date().Format("yyyy-MM-dd hh:mm:ss.S") + "]" + message, optionalParams);
|
}
|
|
function f_register() {
|
|
var server = document.getElementById('tbServer').value;
|
var username = document.getElementById('tbExtension').value;
|
var passwd = document.getElementById('tbPassword').value;
|
|
var uristr = 'sip:' + username;
|
if (uristr.indexOf('@') < 1)
|
uristr = uristr + '@' + server;
|
//
|
logger_info(`开始注册到服务器:${server},分机号:${username}...`);
|
var socket = new JsSIP.WebSocketInterface('wss://' + server +':7443');
|
var configuration = {
|
sockets: [socket],
|
uri: uristr,
|
password: passwd,
|
display_name: username,
|
iceServers: [
|
{ urls: '' }
|
]
|
};
|
|
ua = new JsSIP.UA(configuration);
|
|
ua.on('registered', function (e) { logger_info("注册成功。", e); });
|
ua.on('unregistered', function (e) { logger_info('取消注册。', e); });
|
ua.on('registrationFailed', function (e) { logger_info('注册失败的。', e); });
|
ua.on('newRTCSession', function (e) {
|
logger_info('newRTCSession', e);
|
//
|
cur_session = e.session;
|
cur_session.on("accepted", function (e) { logger_info("呼叫已接通。", e); });
|
cur_session.on("ended", function (e) { logger_info("呼叫结束。", e); });
|
|
cur_session.oniceconnectionstatechange = function () {
|
if (cur_session.connection.iceConnectionState === 'completed') {
|
// 获取本地SDP
|
const localSdp = cur_session.connection.localDescription;
|
|
// 修改SDP(这里以修改音频编解码器优先级为例)
|
const sdp = localSdp.sdp;
|
const modifiedSdp = sdp.replace(/m=audio (\d+) RTP\/AVP (\d+) (.*)/g, (match, port, payloadTypes, formats) => {
|
// 假设我们想将PCMU编解码器的优先级提高
|
const preferredPayloadType = '0'; // PCMU的payload type
|
const preferredFormat = formats.split(' ').find(format => format.startsWith(preferredPayloadType));
|
if (preferredFormat) {
|
const newFormats = formats.split(' ').filter(format => format !== preferredFormat).concat(preferredFormat);
|
return `m=audio ${port} RTP/AVP ${newFormats.join(' ')}`;
|
}
|
return match;
|
});
|
|
// 创建新的RTCSessionDescription对象
|
const newDescription = new RTCSessionDescription({
|
type: localSdp.type,
|
sdp: modifiedSdp
|
});
|
|
// 设置修改后的SDP
|
cur_session.connection.setLocalDescription(newDescription).catch(error => {
|
logger_info('Failed to set local description:', error);
|
});
|
}
|
}
|
//
|
if (e.originator == "remote")
|
e.session.answer();
|
});
|
ua.start();
|
}
|
|
function f_hangup() {
|
if (cur_session != undefined) {
|
cur_session.terminate();
|
cur_session = undefined;
|
}
|
}
|
|
function f_makecall() {
|
if (ua == undefined)
|
return;
|
//
|
var eventHandlers = {
|
'progress': function (e) {
|
logger_info('call is in progress', e);
|
},
|
'failed': function (e) {
|
logger_info('call failed with cause: ', e);
|
},
|
'ended': function (e) {
|
logger_info('呼叫结束,挂机原因: ', e);
|
},
|
'confirmed': function (e) {
|
logger_info('call confirmed', e);
|
}
|
};
|
|
var options = {
|
'eventHandlers': eventHandlers,
|
'mediaConstraints': { 'audio': true, 'video': false },
|
sessionTimersExpires: 1800
|
};
|
|
var server = document.getElementById('tbServer').value;
|
var phone = document.getElementById('tbPhoneNo').value;
|
logger_info("开始呼叫号码:", phone);
|
cur_session = ua.call('sip:' + phone + '@' + server, options);
|
|
}
|
|
</script>
|
</body>
|
</html>
|