简述

本文是以FreeSwitch作为信令服务器,通过sipjs(基于webRtc) 进行媒体协商,网络协商后,进行P2P媒体传输。

参考知识:

效果图

HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title>视频通话demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="shortcut icon" th:href="@{/ico/logo.ico}" type="image/x-icon" />
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
<link rel="stylesheet" th:href="@{/css/baiban.css}">
<script th:inline="javascript">
const pub = [[${pub}]];
</script>
</head>
<body>
<div id="app">
<!--头部导航-->
<ul class="layui-nav layui-bg-blue" lay-bar="disabled">
<li class="layui-nav-item"><a target="_blank">视频通话</a></li>
</ul>
<!--音视频通话-->
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>音视频通话</legend>
</fieldset>
<div class="layui-row" style="border: 1px solid #f0f0f0;margin-left: 10px;margin-right: 10px;padding-top: 10px">
<div class="layui-col-xs12 layui-col-md4" >
<form class="layui-form">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">我的号码:</label>
<div class="layui-input-inline">
<input id="myNumber" value="1000" autocomplete="off" class="layui-input">
</div>
</div>
</div> <div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">信令地址:</label>
<div class="layui-input-inline">
<input id="sipAddr" th:value="${fs}" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Websocket端口:</label>
<div class="layui-input-block">
<input type="radio" name="wsUrl" value="5066" title="http" checked>
<input type="radio" name="wsUrl" value="7443" title="https">
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">拨打号码</label>
<div class="layui-input-inline">
<input id="sip_phone_number" value="1001" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item" style="text-align: center">
<div class="layui-inline">
<button type="button" class="layui-btn layui-btn-primary" onclick="softPhone.start()"
id="register">注册
</button>
<button type="button" class="layui-btn layui-btn-normal operate layui-btn-disabled" disabled
onclick="PHONE.call()" id="call">拨打
</button>
<button type="button" class="layui-btn layui-btn-normal operate layui-btn-disabled" disabled
onclick="PHONE.hangUp()" id="hangup">挂断
</button>
</div>
</form>
</div>
<!--视频展示区-->
<div class="layui-col-xs12 layui-col-md4" style="border:5px solid #3385FF;width: 615px;height: 600px">
<div id="playVideo">
<video id="youVideo" style="padding-right:10px" width="600px" height="600px" muted autoplay
onmousedown="move(this)" th:poster="@{/img/webrtc.png}"
playsinline></video>
</div>
<div style="position:relative;top:-168px;left:1px">
<video id="meVideo" width="100px" height="100px" th:poster="@{/img/webrtc.png}" autoplay playsinline></video>
</div>
</div>
</div>
</div>
<canvas id="capture" style="display: none"></canvas>
<script th:src="@{/javascript/jquery-2.1.4.js}" type="application/javascript"></script>
<script th:src="@{/javascript/sip-0.15.10.js}" type="application/javascript"></script>
<script th:src="@{/layui/layui.js}" type="application/javascript"></script>
<!--操作-->
<script th:src="@{/javascript/sip-pc/operation.js}" type="application/javascript"></script>

javaScript

  let table = null;
let form = null;
let layer = null;
let laydate = null;
let upload = null;
// 拨打电话确认框
let confirmIndex = null;
layui.use(['table', 'form', 'layer', 'laydate', 'upload'], function () {
$(function () {
table = layui.table;
form = layui.form;
layer = layui.layer;
upload = layui.upload;
if (window.location.protocol === 'http:') {
$('input[name="wsUrl"][value="5066"]').attr("checked", true);
} else {
$('input[name="wsUrl"][value="7443"]').attr("checked", true);
}
});
}); // 本地视频
var localVideo = document.getElementById('meVideo');
// 远端视频
var remoteVideo = document.getElementById('youVideo'); var softPhoneUA = null;
var currentSession = null; /**
* 注册模块
* @type {{logout(): void, start(): void, unregister(): void, UAEvent(*): void, register(): void, sessionEvent(*): void}}
*/
const softPhone = {
/**
* 登陆软电话
*/
start() {
// 获取我的号码
var myNumber = $.trim($('#myNumber').val());
// 获取sip地址
var sipAddr = $.trim($('#sipAddr').val());
// sip url 拼接
var sip_uri = myNumber + '@' + sipAddr
// 信令密码
var sip_password = $.trim($('#sipPassword').val());
// 获取WS连接端口
var wsPort = $.trim($('input[name="wsUrl"]:checked').val());
// var wsPort = "5060";
var ws_uri = wsPort == '5066' ? 'ws://' + sipAddr + ':' + wsPort : 'wss://' + sipAddr + ':' + wsPort var config = {
uri: sip_uri,
transportOptions: {
wsServers: [ws_uri]
},
// 授权号
authorizationUser: myNumber,
// 登陆密码
password: '1234',
displayName: myNumber,
register: true
};
//v 就绪软电话、监听软电话连接状态、监听电话呼入、拨打电话、登出软电话系统
softPhoneUA = new SIP.UA(config);
softPhone.UAEvent(softPhoneUA);
// 有电话呼入
softPhoneUA.on('invite', function (session) {
currentSession = session;
softPhone.sessionEvent(session);
layer.confirm('有电话呼入 ... 请注意是否接听)', {
btn: ['取消', '接听', '拒绝'],
btn1: function () {
layer.close(index);
},
btn2: function () {
PHONE.answer();
},
btn3: function () {
softPhone.hangUp();
}
});
})
}, /** 就绪 */
register() {
softPhoneUA.register({ // 注册
register: true
});
}, /**
* 绑定ua事件
* @param {*} ua
*/
UAEvent(ua) {
// 开始尝试连接
ua.on('connecting', function (args) {
console.log('%c connecting - 开始尝试连接', 'color: #f00');
});
// 连接完毕
ua.on('connected', function () {
console.log('%c connected - 连接完毕', 'color: #f00');
});
// 主动取消注册或注册后定期重新注册失败
ua.on('unregistered', function (response, cause) {
$('#register').removeClass("layui-btn-disabled").removeAttr('disabled');
console.log('%c unregistered - 主动取消注册或注册后定期重新注册失败', 'color: #f00');
});
// 注册成功
ua.on('registered', function () {
layer.msg("注册成功", {icon: 1, time: 1500});
console.log('%c registered -- 注册成功', 'color: #f00');
btnHide(['register','shard'])
btnShow(['call'])
})
// websocket 连接失败
ua.on('disconnected', function () {
console.log('%c disconnected - 连接失败', 'color: #f00');
})
}, /**
* 绑定session事件
* @param {} session
*/
sessionEvent(session) {
session.on("rejected", function (response, cause) {
layer.close(confirmIndex);
});
session.on("bye", function (response, cause) {
// 不可用
btnHide(['hangup','mute','unmute','openVideo','closeVideo','shard'])
localVideo.srcObject = null;
remoteVideo.srcObject = null;
});
// 会话被接入
session.on("accepted", function (response, cause) {
layer.close(confirmIndex);
btnShow(['hangup','mute','unmute','openVideo','closeVideo','shard','capturePic'])
var pc = session.sessionDescriptionHandler.peerConnection;
var remoteStream = new MediaStream();
pc.getReceivers().forEach(function (receiver) {
remoteStream.addTrack(receiver.track);
});
remoteVideo.srcObject = remoteStream;
if (pc.getSenders()) {
var localStream = new MediaStream();
pc.getSenders().forEach(function (sender) {
localStream.addTrack(sender.track);
});
localVideo.srcObject = localStream;
}
});
session.on("cancel", function (response, cause) {
layer.close(confirmIndex);
});
}
}

operation.js

/**
* 拨打、接听、挂断 模块
* @type {{call(): void, answer(): void, hangUp(): void}}
*/
const PHONE = {
/**
* 拨打
*/
call() {
const telNumber = $.trim($('#sip_phone_number').val());
var sipAddr = $.trim($('#sipAddr').val());
const inviteUrl = telNumber + '@' + sipAddr
currentSession = softPhoneUA.invite(inviteUrl, {
sessionDescriptionHandlerOptions: {
constraints: {
audio: {
autoGainControl: true,
// 噪音消除
noiseSuppression: true,
// 设置降噪
echoCancellation: true
},
video: true
},
alwaysAcquireMediaFirst: true // 此参数是sip.js官方修复在firefox遇到的bug所设置
})
confirmIndex = layer.confirm('呼叫中....', {
btn: ['取消'],
btn1: function (index) {
currentSession.cancel();
layer.close(index);
}
});
// 拨打后 监听
currentSession.on("rejected", function (response, cause) {
layer.msg("请求通话被拒绝", {icon: 1, time: 1500});
console.log(response)
console.log(cause)
});
// 本次通话结束
currentSession.on("bye", function (response, cause) {
layer.msg("本次通话结束", {icon: 1, time: 1500});
localVideo.srcObject = null;
remoteVideo.srcObject = null;
});
// 对方接听
currentSession.on("accepted", function (response, cause) {
layer.msg("对方接听", {icon: 1, time: 1500});
$('#call').addClass('layui-btn-disabled').attr('disabled', 'disabled');
$('#hangup').removeClass('layui-btn-disabled').removeAttr('disabled');
$('#mute').removeClass('layui-btn-disabled').removeAttr('disabled');
$('#unmute').removeClass('layui-btn-disabled').removeAttr('disabled');
var pc = currentSession.sessionDescriptionHandler.peerConnection;
var remoteStream = new MediaStream();
pc.getReceivers().forEach(function (receiver) {
remoteStream.addTrack(receiver.track);
});
remoteVideo.srcObject = remoteStream;
if (pc.getSenders()) {
var localStream = new MediaStream();
pc.getSenders().forEach(function (sender) {
localStream.addTrack(sender.track);
});
localVideo.srcObject = localStream;
}
});
// 取消通话
currentSession.on("cancel", function (response, cause) {
layer.msg("取消通话", {icon: 1, time: 1500});
});
},
/**
* 挂断
*/
hangUp() {
if (currentSession instanceof Object) {
if (currentSession.hasAnswer) {
currentSession.bye();
} else if (currentSession.isCanceled === false) {
currentSession.cancel();
} else {
currentSession.reject();
}
}
},
/**
* 接听
*/
answer() {
var option = {
sessionDescriptionHandlerOptions: {
constraints: {
audio: {
autoGainControl: true,
// 噪音消除
noiseSuppression: true,
// 设置降噪
echoCancellation: true
},
video: true
},
alwaysAcquireMediaFirst: true, // 此参数是sip.js官方修复在firefox遇到的bug所设置
rtcConfiguration: {
iceServers: [
{
url: "stun:124.222.83.153:3478",
username: "test",//可选
credential: "test123"//可选
},
{
url: "turn:124.222.83.153:3478",
"username": "test",//可选
"credential": "test123"//可选
}
]
}
}
}
currentSession.accept(option)
}
}

拨打

接听

通话中

数据流程图

Web网页音视频通话之基于Sipjs的更多相关文章

  1. 基于 Web SDK 实现视频通话场景 | 声网 SDK 教程

    声网视频 SDK 被广泛应用于多种实时互动场景中,例如视频会议.视频通话.音视频社交.在线教育等.为了让刚刚接触声网 SDK 的开发者,可以更顺畅地实现基础的视频通话功能,我们基于声网 Web SDK ...

  2. iOS下WebRTC音视频通话(一)

    在iOS下做IM功能时,难免都会涉及到音频通话和视频通话.QQ中的QQ电话和视频通话效果就非常好,但是如果你没有非常深厚的技术,也没有那么大的团队,很难做到QQ那么快速和稳定的通话效果. 但是利用We ...

  3. 一、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-项目引言

    项目文章索引 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展面板的 ...

  4. web网页中使用vlc插件播放相机rtsp流视频

    可参考: 使用vlc播放器做rtsp服务器 使用vlc播放器播放rtsp视频 使用vlc进行二次开发做自己的播放器 vlc功能还是很强大的,有很多的现成的二次开发接口,不需配置太多即可轻松做客户端播放 ...

  5. iOS下WebRTC音视频通话(二)-局域网内音视频通话

    这里是iOS 下WebRTC音视频通话开发的第二篇,在这一篇会利用一个局域网内音视频通话的例子介绍WebRTC中常用的API. 如果你下载并编译完成之后,会看到一个iOS 版的WebRTC Demo. ...

  6. web网页练习

    一. HTML部分 1. XHTML和HTML有什么区别 HTML是一种基本的WEB网页设计语言,XHTML是一个基于XML的置标语言最主要的不同: XHTML 元素必须被正确地嵌套. XHTML 元 ...

  7. TI IPNC Web网页之网页修改教程

    web网页程序修改 打开gStudio之后,点击菜单栏中Help->Contents.先把这个诡异的编程语言看一遍吧.这里搬一些东西出来. GoDB简介 从第一副图片中,我们可以看出,从源文件到 ...

  8. TI IPNC Web网页之GoDB开发环境

    介绍 下面介绍DM8127/DM385 IPNC RDK中网页制作相关的东东. 具体来说,各位获得这个RDK包时有以下文件: IPNC_RDK_DM812x_DM385_Version3.5.0.ta ...

  9. WinForm嵌入Web网页的解决方案

    企业级信息化系统绝大部分采用BS架构实现,如门户网站.OA系统.电商网站等,通过浏览器输入Web网址即可访问,对于使用者来说非常便捷,对于开发维护者来说也非常方便,程序维护只需更新服务器即可,使用者无 ...

  10. C#开发BIMFACE系列49 Web网页中加载模型与图纸的技术方案

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在BIMFACE二次系列博客中详细介绍了服务器端API的调用方式,如下列表 C#开发BIMFACE系列1   BIMFAC ...

随机推荐

  1. YII文件上传

    <span style="font-size:14px;">use yii\web\UploadedFile; public function actionDoarta ...

  2. RDIFramework.NET敏捷开发框架助力企业BPM业务流程系统的开发与落地

    现如今,很多企事业单位集团都自己有一套独特严密的业务生产经营流程,各个环节紧密相连.前后对应,一旦某个环节疏忽,整个流程就会出现问题.如何保证业务流程的标准化和规范化运营.减少人为差错,这就需要用到B ...

  3. Spring的Factories机制介绍

    Java 的 SPI 机制 Java SpringBoot 加载 yml 配置文件中字典项 Spring的Factories就是Spring版本的Java Spi. Spring Factories的 ...

  4. 笔记:设置redhat 7.2 默认root用户启动以及网络服务自启动

    笔记:设置redhat 7.2 默认root用户启动以及网络服务自启动 1.root用户启动    root用户下打开 /etc/gdm/custom.conf文件,添加字段如下:    [daemo ...

  5. LeetCode 周赛 343(2023/04/30)结合「下一个排列」的贪心构造问题

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 今天是五一假期的第二天,打周赛的人数比前一天的双周赛多了,难道大家都只玩一天吗?这场周赛 ...

  6. NUXT3.0实现网络请求二次封装

    最近在开发基于nuxt3.0的项目,看了官网的网络请求,感觉不太适合,就自己基于官网的useFetch()方法封装了一个网络请求,下面开始实现封装. 第一步:新建http.ts文件,用于编写封装代码 ...

  7. 2021-08-23:超级水王问题。扩展1:摩尔投票。扩展2:给定一个正数K,返回所有出现次数>N/K的数。

    2021-08-23:超级水王问题.扩展1:摩尔投票.扩展2:给定一个正数K,返回所有出现次数>N/K的数. 福大大 答案2021-08-23: 扩展1: 1.如果无候选,当前数就是候选,血为1 ...

  8. docker快速启动proxy_pool项目

    docker快速启动proxy_pool项目 项目地址:https://github.com/jhao104/proxy_pool 0x01 搭建redis 该项目是基于redis的,所以我们首先需要 ...

  9. PlayWright(二)

      上篇我们已经安装好了playwright和各个浏览器,那么现在我们直接开始吧   1.怎么使用palywright?   我们需要先导入sync_playwright,然后用start启动,sto ...

  10. K2C V21.4.6.12刷breed教程

    K2C V21.4.6.12刷breed教程(刷机方法源自qiao99) 原贴地址:K2C V21.4.6.12刷breed记录 http://www.right.com.cn/forum/threa ...