1、使用socket.io进行会话
2、为了方便,参数写死在前端了,前端界面1代码如下(由界面1发起视频):
<!DOCTYPE html>
<html><head><title>Socket.IO chat</title><meta charset="utf-8"><meta name="description" content="WebRTC code samples"><meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1"><meta itemprop="description" content="Client-side WebRTC code samples"><meta itemprop="name" content="WebRTC code samples"><meta name="mobile-web-app-capable" content="yes"><meta id="theme-color" name="theme-color" content="#ffffff"><!-- 引入2.0版本的socket.io文件 --><script src="socket.io.js"></script><script src="webrtc.js"></script><script src="jquery-3.6.4.js"></script><link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css"><link rel="stylesheet" href="css/main.css" /><link rel="stylesheet" href="css/c1/main.css" /></head><body><div id="container"><video id="localVideo" title="a" playsinline autoplay muted></video><video id="remoteVideo" title="b" playsinline autoplay></video><div><button id="prepareBtn" type="button" onclick="prepare()">准备</button><button id="callBtn" type="button" onclick="callVideo()">呼叫</button><button id="cancelBtn" type="button" onclick="cancelVideo()">挂断</button></div></div><script>const prepareButton = document.getElementById('prepareBtn');const callBtn = document.getElementById('callBtn');const cancelBtn = document.getElementById('cancelBtn');const localVideo = document.getElementById('localVideo');const remoteVideo = document.getElementById('remoteVideo');let localStream;let pc1;let pc2;let to = '2';var iceServer = {{// urls: 'stun:stun.l.google.com:19302'urls: 'stun:stun.services.mozilla.com'}]};const offerOptions = {offerToReceiveAudio: 1,offerToReceiveVideo: 0};//建立socket连接const socket = io("https://192.168.3.21", {reconnectionDelayMax: 10000,auth: {token: "123"},query: {"userId": "1"}});socket.on("connect", () => {console.log("通道已建立")});//监听服务端发送的 topic1 消息socket.on("exchange", (msg) => {console.log("收到新的数据了");var data = JSON.parse(msg)console.log(data);var type = data.typeif (type === 'invite') {to = data.from} else if (type === 'recive') {to = data.from} else if (type === 'canditate') {if (data.data != undefined) {pc1.addIceCandidate(new RTCIceCandidate(data.data))}} else if (type === 'offer') {//设置远程应答pc1.setRemoteDescription(new RTCSessionDescription(data.data))pc1.createAnswer().then(function (answer) {send({type: 'answer',data: answer})return pc1.setLocalDescription(answer)}).then(sdp => {})} else if (type === 'answer') {if (data.data != undefined) {pc1.setRemoteDescription(new RTCSessionDescription(data.data))}}});function gotStream(stream) {localVideo.srcObject = stream;localStream = stream;}async function init() {navigator.mediaDevices.getUserMedia({audio: true,video: false}).then(gotStream).catch(e => alert(`getUserMedia() error: ${e.name}`));}async function prepare() {init();var data = {type: 'invite',data: 'video',from: '1',toUserId: '2'};// send(data);prepareButton.disabled = true;}function call() {pc1 = new RTCPeerConnection(iceServer);pc1.onicecandidate = e => onIceCandidate(pc1, e);// 有远程视频流时,显示远程视频流 pc1.addEventListener('track', event => {// remoteVideo.srcObject = event.streams[0]// remoteVideo.onloadedmetadata = function (e) {// remoteVideo.play()// }})pc1.addEventListener('addstream', e => {console.log("AAAAAAAAAAA")console.log(e);remoteVideo.srcObject = e.stream})//初始化本人的视频流navigator.mediaDevices.getUserMedia({audio: false,video: true}).then(stream => {// 将视频流写入到video标签localVideo.srcObject = stream;localStream = stream;stream.getTracks().forEach(track => {pc1.addTrack(track, stream)});//向对方发送应答pc1.createOffer().then(sdp => {pc1.setLocalDescription(sdp)send({type: 'offer',data: sdp})})})localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));callBtn.disabled = true;}function onIceCandidate(pc, event) {if (event.candidate) {send({type: 'canditate',data: event.candidate})}}function send(data) {data.toUserId = data.toUserId || toconsole.log(data)socket.emit("exchange", JSON.stringify(data))}function callVideo() {call();}function cancelVideo() {if (pc1 != null && pc1 != undefined) {pc1.close();}}</script><style>#video {background-color: black;height: 30vh;}</style>
</body></html>
3、前端界面2代码如下(当界面1发起视频呼叫后,该界面被动应答):
<!DOCTYPE html>
<html lang="zh"><head><title>Socket.IO chat</title><meta charset="utf-8"><meta name="description" content="WebRTC code samples"><meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1"><meta itemprop="description" content="Client-side WebRTC code samples"><meta itemprop="name" content="WebRTC code samples"><meta name="mobile-web-app-capable" content="yes"><meta id="theme-color" name="theme-color" content="#ffffff"><!-- 引入2.0版本的socket.io文件 --><script src="socket.io.js"></script><script src="webrtc.js"></script><script src="jquery-3.6.4.js"></script><link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css"><link rel="stylesheet" href="css/main.css" /><link rel="stylesheet" href="css/c1/main.css" />
</head><body><div id="container"><video id="localVideo" title="a" playsinline autoplay muted></video><video id="remoteVideo" title="b" playsinline autoplay></video></div><script>const localVideo = document.getElementById('localVideo');const remoteVideo = document.getElementById('remoteVideo');let localStream;let pc1;let pc2;let to = '1';var iceServer = {{// urls: 'stun:stun.l.google.com:19302'urls: 'stun:stun.services.mozilla.com'}]};//建立socket连接const socket = io("https://192.168.3.21", {reconnectionDelayMax: 10000,auth: {token: "123"},query: {"userId": "2"}});socket.on("connect", () => {console.log("通道已建立")});//监听服务端发送的 topic1 消息socket.on("exchange", (msg) => {console.log("收到新的数据了");var data = JSON.parse(msg)var type = data.typeconsole.log({"name": "接收方",data});if (type === 'invite') {to = data.from//打开自己的摄像头init();} else if (type === 'recive') {to = data.from} else if (type === 'canditate') {if (pc1 == null) {init();}if (data.data != undefined) {pc1.addIceCandidate(new RTCIceCandidate(data.data))}} else if (type === 'offer') {if (pc1 == null) {init();}pc1.setRemoteDescription(new RTCSessionDescription(data.data))pc1.createAnswer().then(function (answer) {send({type: 'answer',data: answer})return pc1.setLocalDescription(answer)}).then(sdp => {})} else if (type === 'answer') {if (data.data != undefined) {pc1.setRemoteDescription(new RTCSessionDescription(data.data))}}});function gotStream(stream) {localVideo.srcObject = stream;localStream = stream;}function init() {navigator.mediaDevices.getUserMedia({audio: true,video: false}).then(gotStream).catch(e => alert(`getUserMedia() error: ${e.name}`));call();}function onIceCandidate(pc, event) {if (event.candidate) {send({type: 'canditate',data: event.candidate})}}function call() {pc1 = new RTCPeerConnection(iceServer);//获取本地网络信息,并发送给通信方 pc1.onicecandidate = e => onIceCandidate(pc1, e);// 有远程视频流时,显示远程视频流 // pc1.addEventListener('track', event => {// console.log("BBBBBBBBBBBBBBBBBBBBBBBBB")// console.log(event);// console.log(localVideo.srcObject)// console.log("BBBBBBBBBBBBBBBBBBBBBBBBB+")// remoteVideo.srcObject = event.streams[0]// remoteVideo.onloadedmetadata = function (e) {// console.log("加载完毕")// remoteVideo.play()// }// })pc1.addEventListener('addstream', e => {// 将对方的视频流写入到video标签中remoteVideo.srcObject = e.stream})//本地视频流量,打开视频流navigator.mediaDevices.getUserMedia({audio: false,video: true}).then(stream => {// 将视频流写入到video标签中 localVideo.srcObject = stream;localStream = stream;stream.getTracks().forEach(track => {pc1.addTrack(track, stream)})pc1.createOffer().then(sdp => {pc1.setLocalDescription(sdp)send({type: 'offer',data: sdp})})})}function send(data) {data.toUserId = data.toUserId || toconsole.log({"name": "发送",data})socket.emit("exchange", JSON.stringify(data))}</script><style>#video {background-color: black;height: 30vh;}</style>
</body></html>
WebRTC学习(六)端对端传输_51CTO博客_webrtc学习
webRTC结合webSocket实时通信 - 掘金
4、关于 coturn 信令服务的安装,参考以下文档:coturn安装以及报错“coturn/src/apps/relay/netengine.c:316:对‘SSL_CTX_up_ref’未定义的引用“_coturn 启动报错_Mango酱的博客-CSDN博客
5、coturn 安装后的测试:Trickle ICE (webrtc.github.io)
6、代码中进行如下设置:
//该参数为了var iceServer = {// iceServers: [{// urls: 'stun:stun.l.google.com:19302'// }]iceServers: [{urls: 'stun:stun.services.mozilla.com'},{urls: 'turn:ip:3478',credential: '密码',username: '账号'}]};