首页 > 其他分享 >WebRTC + JsSIP + freeSWITCH一对一视频聊天

WebRTC + JsSIP + freeSWITCH一对一视频聊天

时间:2023-05-10 15:45:19浏览次数:32  
标签:info function console JsSIP freeSWITCH data WebRTC

之前几篇文件介绍了 freeSWITCH 和 WebRTC 结合在一起需要的各种环境,现在到了最关键的一篇,使用 JsSIP 来创建一个 DEMO 。这次我们需要写点 JS 代码。

准备 JsSIP 库文件
可以从 http://www.jssip.net/download/ 下载一个 min 版的 js 文件,我用的是 3.0.13 ,文件名是 jssip-3.0.13.min.js ,把它放在我们之前用 Node.js 建立的 https 服务器的 public/js 目录下,我们将在 html 文件内引用它。类似:

<script src="js/jssip-3.0.13.min.js" type="text/javascript"></script>
配置 freeSWITCH
我们之前下载的 freeSWITCH ,默认是不处理音视频编解码的,所以,要设置它采用 media proxy 模式来代理转发 WebRTC 的音视频,这样就可以基于 JsSIP 、 WebRTC 、 freeSWITCH 来一对一视频聊天。

修改vars.xml,加入:

<X-PRE-PROCESS cmd=="set" data="proxy_media=true"/>


修改sip_profiles/internal.xml,设置inbound-proxy-media和inbound-late-negotiation为true,类似下面:

<!--Uncomment to set all inbound calls to proxy media mode-->
<param name="inbound-proxy-media" value="true"/>

<!-- Let calls hit the dialplan before selecting codec for the a-leg -->
<param name="inbound-late-negotiation" value="true"/>

这样配置之后,freeSWITCH 会进入代理模式,不对media 做任何处理,直接在两个 end peer 之间转发(RTP包)。

JsSIP DEMO
JsSIP 的 API 文档参考下面链接:

http://www.jssip.net/documentation/3.0.x/api/
http://www.jssip.net/documentation/3.0.x/api/session/
http://www.jssip.net/documentation/3.0.x/api/ua/
注意, JsSIP 对 SIP 和 WebRTC 做了封装,比如你不需要自己调用 getUserMedia 来捕获音视频了, JsSIP 会根据你传给JsSIP.UA.call方法的参数来自己调用,用起来比较方便。

但是,你还是要了解 SIP 呼叫的流程和WebRTC的各种限制以及如何处理 RTCPeerConnection 发过来的音视频流。

关于 WebRTC JS 侧 API,看这里好了:http://w3c.github.io/webrtc-pc/。

想看更多资料,可以看我搜集的这些链接:WebRTC学习资料大全。

关于 SIP 的流程,参考《freeSWITCH权威指南》这本书吧,讲得很明白。

网上关于 JsSIP + freeSWITCH 的 demo 很少,而且基本跑不起来……我这个是验证过的啦!

直接给出我们的 demo.html 的所有代码:

<!DOCTYPE html>
<html>
<head>
<title>JsSIP + WebRTC + freeSWITCH</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="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" placeholder="SIP URI (i.e: sip:[email protected])"/></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" placeholder="SIP password"/></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)"/></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" placeholder="sip:[email protected]:5060"></td>
</tr>
<tr>
<td colspan="2" align="center"><button onclick="testStart()"> Initialize </button></td>
</tr>
<tr>
<td colspan="2" align="center"><button onclick="testCall()"> Call </button></td>
</tr>
<tr>
<td colspan="2" align="center"><button onclick="captureLocalMedia()"> Capture Local Media</button></td>
</tr>
</table>
</div>

<div 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: true,
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
};

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'){ //incoming call
console.info("incomingSession, answer the call");
incomingSession = data.session;
data.session.answer({'mediaConstraints' : { 'audio': true, 'video': true, 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.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);
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': true ,
mandatory: { maxWidth: 640, maxHeight: 360 }
},
'mediaStream': localStream
};

//outgoingSession = userAgent.call('sip:[email protected]:5060', options);
outgoingSession = userAgent.call(sip_phone_number_, options);
}
</script>
</html>

关于 JsSIP.UA 和 JsSIP.RTCSession 等类和 API 的使用,参考代码,对照 JsSIP 的官方文档,即可理解。

注意我在代码里调用 RTCSession 的 answer 方法做了自动接听。实际开发中,你需要弹出一个提示框,让用户选择是否接听。

一对一视频聊天的效果:
先运行 freeSWITCH , 验证启动正常(参看freeSWITCH安装、配置与局域网测试),再运行 npm start启动 https 服务器,最后 Chrome 内访问 https://192.168.40.131:8080/demo.html ,看到下面的结果:

 

 

注意,必须填写有效信息,然后先点击 Initialize 来初始化,代码中会到 freeSWITCH 那里注册 SIP 号码,然后才可以呼叫别人。

被呼叫方只需要填写信息,点击 Initialize 按钮,不需要点击 Call 按钮。

填写所有参数,点击 Initialize 按钮,可以注册一个 SIP 号码 1000(使用开发者工具可查看我输出的日志)。

然后到另一台电脑访问同一个链接,注册一个不同的账号 1002。

再回到初始的那台电脑,在 SIP Phone Info 后输入 sip:[email protected]:5060,然后点击 Call 按钮,即可呼叫 1002 ,接通后,可以看到对方视频。效果如下:

 

 

我测试的摄像头有问题,出来的是抽象视频……

这是我们使用 JsSIP 和 freeSWITCH 构建视频聊天的简单 DEMO 。

想起来一点非常重要的:freeSWITCH 和 nodejs 实现的 https 服务器,使用的证书应该是一个,否则会报错哦。可以先跑 freeSWITCH ,然后把 cert/wss.pem 文件的内容分拆给 nodejs 使用。

相关阅读:

freeSWITCH安装、配置与局域网测试
使用nodejs为WebRTC+freeSWITCH搭建https服务
freeSWITCH + WebRTC 音视频会议
使用Zoiper与freeSWITCH开视频会议
Windows下编译freeSWITCH
使用freeSWITCH和Yate进行VoIP通话

标签:info,function,console,JsSIP,freeSWITCH,data,WebRTC
From: https://www.cnblogs.com/kn-zheng/p/17388180.html

相关文章

  • FreeSWITCH实现在视频通话中某一方视频翻转
    客户项目使用浏览器+webrtc+FreeSWITCH在各类国产化终端间(windows+kylin+android+emss等)实现音视频通信、状态呈现以及即时消息。本来实施挺顺利,但客户新引进了一批新FT终端,摄像头画面竟然向左翻转了90度,关键是,客户认为终端质量没有问题,让软件系统自行解决。翻遍了v4l2驱动配置和......
  • freeSWITCH 视频通话
    一,freeSWITCH安装  本实验基于CentOS6 源码编译安装FreeSWITCH,详细安装过程见下:http://990487026.blog.51cto.com/10133282/1921010   二,配置FreeSWITCH,加载H26x模块1,FreeSWITCH先处于关闭状态.2,修改配置文件:[root@CentOS ~]# vim ~/freeswitch/etc/fr......
  • 阿里云部署freeswitch的公网ip问题,nat穿越问题
    关于阿里云安装freeswitch的内容请参见本人另一篇博文。ubuntu14.04lts安装freeswitch这里把困扰本人很久的问题,阿里云使用“专有网络”后,系统获取公网ip错误,导致freeswitch无法连接及打通电话的问题解决方法记录一下:1、在var.xml中修改<!--X-PRE-PROCESScmd="stun-set"dat......
  • ubuntu14.04 lts 安装freeswitch
    ubuntu14.04lts安装freeswitch:0、安装一堆依赖包。apt-getinstalllibedit-devlibldns-devlibpcre3-devlibspeexdsp-devlibspeex-devlibcurl4-openssl-devlibopus-devlibncurses5-devlibtiff-devlibjpeg-devzlib1g-devlibssl-devlibsqlite3-devbuild-essential......
  • Freeswitch挂断原因汇总
    NORMAL_RELEASE正常释放NORMAL_CLEARING双方都由运营商挂断,正常CALL_REJECTED呼叫被拒绝,正常USER_BUSY用户占线繁忙,正常NO_ANSWER呼叫未应答,正常NO_USER_RESPONSE呼叫未应答超时,正常NORMAL_TEMPORARY_FAILURE呼叫线路超时TIMEOUT超时(一般是SIP超时)NO_RO......
  • Ubuntu安装FreeSWITCH亲测
    本人在安装FreeSWITCH的时候遇到了相当多的坑,网上很多方法都模棱两可,经常装失败,最后终于装成功后做一下总结最顺利的安装方式​1.下载压缩文件​下载地址:​ ​ ​http://files.freeswitch.org/freeswitch-releases/​​​  我选择的下载版本是freeswitch-1.8.5.tar.x......
  • 在Ubuntu18.04安装Freeswitch1.10
    在Ubuntu18.04安装Freeswitch1.10一、版本选择二、Ubuntu的安装1、修改Ubuntu镜像(1)进入配置的目录,并进行配置文件备份(2)打开和修改源三、Freeswitch安装1、添加第三方源2、添加第三方源3、安装部分依赖包4、配置使用gawk5、编译安装程序依赖包(1)安装cmake(2)安装libks(3)安装signalwire-......
  • freeswitch-ubuntu安装
    一,软件及环境准备:ubuntu版本18.04:https://releases.ubuntu.com/18.04.6/ubuntu-18.04.6-live-server-amd64.isofreeswitch版本1.10.7:https://files.freeswitch.org/freeswitch-releases/freeswitch-1.10.7.-release.tar.gzspandsp包:https://codeload.github.com/freeswitch/sp......
  • FreeSWITCH对接vosk实现实时语音识别
    环境:CentOS7.6_x64FreeSWITCH版本:1.10.9Python版本:3.9.2一、背景描述vosk是一个开源语音识别工具,可识别中文,之前介绍过python使用vosk进行中文语音识别,今天记录下FreeSWITCH对接vosk实现实时语音识别。vosk离线语音识别可参考我之前写的文章:python使用vosk进行中文......
  • freeswitch的任务引擎问题与解决方案
     概述freeswitch核心框架中有一个定时任务系统task,在开发过程中用来做一些延时操作和异步操作很方便。我们在VOIP的呼叫流程中,经常会有一些对实时性要求没那么高的操作,或者会有阻塞流程的操作,我们都可以开启一个定时任务子流程,来达到延时和异步的目标。但是在实际的生产应用......