上一章JsSIP初识已经介绍了JsSIP的基本使用方法,这一章将使用FreeSWITCH+JsSIP实现视频通讯,截止本文FreeSWITCH使用1.6.18版本,JsSIP使用的3.0.15版本(经过测试使用3.1.x版本不适用本章的例子)
<X-PRE-PROCESS cmd="set" data="default_password=1234"/>
<X-PRE-PROCESS cmd=="set" data="proxy_media=true"/>
<param name="inbound-proxy-media" value="true"/>
<param name="inbound-late-negotiation" value="true"/>
reloadxml
,然后打开两台机器上的eyeBeam
测试视频通话。如果一直报视频编解码问题的错误,可以尝试修改/conf/var.xml的配置项
global_codec_prefs
和outbound_codec_prefs
如下(增加可用编解码器)
<X-PRE-PROCESS cmd="set" data="global_codec_prefs=OPUS,G722,PCMU,PCMA,GSM,H263,H264,VP8,H263-1998"/>
<X-PRE-PROCESS cmd="set" data="outbound_codec_prefs=OPUS,G722,PCMU,PCMA,GSM,H263,H264,VP8,H263-1998"/>
<html>
<head>
<title>JsSIP + freeSWITCHtitle>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="Author" content="Fly" />
<meta name="description" content="JsSIP Test DEMO" />
<script src="js/jssip-3.0.15.js" type="text/javascript">script>
<style type="text/css">
style>
head>
<body>
<div style="padding-bottom:10px;font-weight:bold;">控制台div>
<div id="login-page" style="width: 500px; border: 1px solid #f0f0f0;">
<table border="0">
<tr>
<td width="160px" align="left"><label for="sip_uri">自己的SIP URI:label>td>
<td width="300px"><input style="width:250px" id="sip_uri" type="text" value="sip:[email protected]"/>td>
tr>
<tr>
<td align="left"><label for="sip_password">SIP Password:label>td>
<td><input style="width:250px" id="sip_password" type="password" value="0000"/>td>
tr>
<tr>
<td align="left"><label for="ws_uri">WSS URI:label>td>
<td><input style="width:250px" id="ws_uri" class="last unset" type="text" value="wss://192.168.17.96:7443"/>td>
tr>
<tr>
<td align="left"><label class="input_label" for="sip_phone_number">拨打SIP URI:label>td>
<td><input style="width:250px" id="sip_phone_number" type="text" value="sip:[email protected]">td>
tr>
<tr>
<td align="left"><input type="button" onclick="testStart()" value="注册"/>td>
<td align="left"><input type="button" onclick="testCall()" value="拨打"/>td>
tr>
table>
div>
<div style="padding-top:10px;padding-bottom:10px;font-weight:bold;">视频展示区div>
<div style="padding-top:10px;" align="left">
<table border="0">
<tr>
<video id="meVideo" style="padding-right:10px">video>
<video id="youVideo">video>
tr>
table>
div>
body>
<script type="text/javascript">
var outgoingSession = null;
var incomingSession = null;
var currentSession = null;
var meVideo = document.getElementById('meVideo');
meVideo.setAttribute('autoplay', '');
//meVideo.setAttribute('muted', '');
meVideo.setAttribute('playsinline', '');
meVideo.style.width = '360px';
var youVideo = document.getElementById('youVideo');
youVideo.setAttribute('autoplay', '');
//meVideo.setAttribute('muted', '');
youVideo.setAttribute('playsinline', '');
youVideo.style.width = '360px';
var constraints = {
audio: true,
video: {
faceMode: 'user'
}
};
var localStream = null;
var userAgent = null;
navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
meVideo.srcObject = stream;
document.body.addEventListener('click', function() {
meVideo.play();
});
// wait until the video stream is ready
var interval = setInterval(function(){
if(!meVideo.videoWidth){
return;
}
//stage.appendChild(videoView);
clearInterval(interval);
}, 1000 / 50);
}).catch(function(error){
onError({
name: error.name,
message: error.message
});
});
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);
});
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' : constraints, 'mediaStream': null});
} 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.sdp = data.sdp.replace('UDP/TLS/RTP/SAVPF', 'RTP/SAVPF');
//console.info('onSDP, changed 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);
youVideo.srcObject = ev.stream;
document.body.addEventListener('click', function() {
meVideo.play();
});
// wait until the video stream is ready
var interval = setInterval(function(){
if(!meVideo.videoWidth){
return;
}
//stage.appendChild(videoView);
clearInterval(interval);
}, 1000 / 50);
};
});
});
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);
alert('呼叫失败');
},
'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' : constraints,
'mediaStream': null
};
outgoingSession = userAgent.call(sip_phone_number, options);
}
script>
html>
由于WebRTC要求HTTPS服务,所以我们的页面不能直接访问,而是要通过放入服务中去运行。并且要使用与FreeSWITCH相同的证书(FreeSWITCH证书在其安装目录下的cert文件夹下的wss.pem)。可以参考KOA2 提供HTTPS安全服务
由于此类文章不是很受欢迎,所以FreeSWITCH类文章截止此文章为止