簡(jiǎn)述
本文是以FreeSwitch作為信令服務(wù)器,通過sipjs(基于webRtc) 進(jìn)行媒體協(xié)商,網(wǎng)絡(luò)協(xié)商后,進(jìn)行P2P媒體傳輸。
參考知識(shí):
- sip.js https://sipjs.com/
- webRtc開發(fā)手冊(cè) https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API
效果圖:
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">
<!--頭部導(dǎo)航-->
<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">我的號(hào)碼:</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">撥打號(hào)碼</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">注冊(cè)
</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>
<!--視頻展示區(qū)-->
<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;
// 撥打電話確認(rèn)框
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');
// 遠(yuǎn)端視頻
var remoteVideo = document.getElementById('youVideo');
var softPhoneUA = null;
var currentSession = null;
/**
* 注冊(cè)模塊
* @type {{logout(): void, start(): void, unregister(): void, UAEvent(*): void, register(): void, sessionEvent(*): void}}
*/
const softPhone = {
/**
* 登陸軟電話
*/
start() {
// 獲取我的號(hào)碼
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]
},
// 授權(quán)號(hào)
authorizationUser: myNumber,
// 登陸密碼
password: '1234',
displayName: myNumber,
register: true
};
//v 就緒軟電話、監(jiān)聽軟電話連接狀態(tài)、監(jiān)聽電話呼入、撥打電話、登出軟電話系統(tǒng)
softPhoneUA = new SIP.UA(config);
softPhone.UAEvent(softPhoneUA);
// 有電話呼入
softPhoneUA.on('invite', function (session) {
currentSession = session;
softPhone.sessionEvent(session);
layer.confirm('有電話呼入 ... 請(qǐng)注意是否接聽)', {
btn: ['取消', '接聽', '拒絕'],
btn1: function () {
layer.close(index);
},
btn2: function () {
PHONE.answer();
},
btn3: function () {
softPhone.hangUp();
}
});
})
},
/** 就緒 */
register() {
softPhoneUA.register({ // 注冊(cè)
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');
});
// 主動(dòng)取消注冊(cè)或注冊(cè)后定期重新注冊(cè)失敗
ua.on('unregistered', function (response, cause) {
$('#register').removeClass("layui-btn-disabled").removeAttr('disabled');
console.log('%c unregistered - 主動(dòng)取消注冊(cè)或注冊(cè)后定期重新注冊(cè)失敗', 'color: #f00');
});
// 注冊(cè)成功
ua.on('registered', function () {
layer.msg("注冊(cè)成功", {icon: 1, time: 1500});
console.log('%c registered -- 注冊(cè)成功', '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;
});
// 會(huì)話被接入
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,
// 設(shè)置降噪
echoCancellation: true
},
video: true
},
alwaysAcquireMediaFirst: true // 此參數(shù)是sip.js官方修復(fù)在firefox遇到的bug所設(shè)置
})
confirmIndex = layer.confirm('呼叫中....', {
btn: ['取消'],
btn1: function (index) {
currentSession.cancel();
layer.close(index);
}
});
// 撥打后 監(jiān)聽
currentSession.on("rejected", function (response, cause) {
layer.msg("請(qǐng)求通話被拒絕", {icon: 1, time: 1500});
console.log(response)
console.log(cause)
});
// 本次通話結(jié)束
currentSession.on("bye", function (response, cause) {
layer.msg("本次通話結(jié)束", {icon: 1, time: 1500});
localVideo.srcObject = null;
remoteVideo.srcObject = null;
});
// 對(duì)方接聽
currentSession.on("accepted", function (response, cause) {
layer.msg("對(duì)方接聽", {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,
// 設(shè)置降噪
echoCancellation: true
},
video: true
},
alwaysAcquireMediaFirst: true, // 此參數(shù)是sip.js官方修復(fù)在firefox遇到的bug所設(shè)置
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)
}
}
撥打
接聽
通話中
文章來源:http://www.zghlxwxcb.cn/news/detail-416718.html
數(shù)據(jù)流程圖
文章來源地址http://www.zghlxwxcb.cn/news/detail-416718.html
到了這里,關(guān)于Web網(wǎng)頁音視頻通話之基于Sipjs的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!