我最近开始为我的学校使用 WebRTC 和 PeerJs 在我学校的服务器 (VPS) 上进行远程在线课程学习和构建应用程序。到目前为止,我能够建立 1 对 1 对等连接,但很难暂停和恢复流传输。
我正在寻求有关如何在活动连接时暂停和恢复自己和远程用户的视频和音频流的帮助。当我执行 localStream.getVideoTracks()[0].enabled = false
时,它只为我禁用视频(不是为远程用户)。
正如一些人所建议的 replaceTrack API,但由于我是新手,所以我无法找到有关它的教程。
我的代码(感谢 Link)如下所示:
var url = new URL(window.location.href)
var disableStreamInBeginning = url.searchParams.get("disableStreamInBeginning")
var passwordProtectedRoom = url.searchParams.get("passwordProtectedRoom")
var muteAllInBeginning = url.searchParams.get("muteAllInBeginning")
var isVideoCall = url.searchParams.get("isVideoCall")
var singleOrConference = url.searchParams.get("singleOrConference")
const conferenceView = document.getElementById('conference')
const loader = document.getElementById('loader')
const localVideoView = document.getElementById('local-video')
const remoteVideoView = document.getElementById('remote-video')
const remoteVideoDiv = document.getElementById('remote-video-div')
if(typeof disableStreamInBeginning !== 'undefined' && disableStreamInBeginning == 'true'){
var disbaleSelfStream = true
} else {
var disbaleSelfStream = false
}
if(typeof passwordProtectedRoom !== 'undefined' && passwordProtectedRoom == 'true'){
var passwordProtected = true
} else {
var passwordProtected = false
}
if(typeof muteAllInBeginning !== 'undefined' && muteAllInBeginning == 'true'){
var muteAll = true
} else {
var muteAll = false
}
if(typeof isVideoCall !== 'undefined' && isVideoCall == 'true'){
var videoCall = true
} else {
var videoCall = false
}
if(typeof singleOrConference !== 'undefined' && singleOrConference == 'conference'){
var isConference = true
conferenceView.style.display = 'block'
} else {
var isConference = false
localVideoView.style.opacity = 0
remoteVideoView.style.opacity = 0
remoteVideoDiv.style.opacity = 0
}
var selectedCamera = 'user'
let localStream;
const socket = io('/');
localVideoView.muted = true;
const peers = {};
const peer = new Peer(undefined, {
host: '/',
port: '443',
path: '/myapp',
secure: true
})
// Handelling incoming call connection
peer.on("call", async (call) => {
let stream = null;
try {
stream = await navigator.mediaDevices.getUserMedia(
{
video: {
facingMode: selectedCamera
},
audio: true
});
call.answer(stream);
call.on("stream", (remoteVideoStream) => {
addVideoStream(remoteVideoView, remoteVideoStream);
});
} catch (err) {
console.log('peer.on("call": ' + err);
};
});
// On new user connected
socket.on("user-connected", async (userId) => {
connectDataToNewUser(userId);
try {
stream = await navigator.mediaDevices.getUserMedia(
{
audio: true,
video: true,
})
}
catch (err) {
console.log('socket.on("user-connected": ' + err);
};
connectMediaToNewUser(userId, stream);
});
// Show own Video on own device screen
(async () => {
try {
localStream= await navigator.mediaDevices.getUserMedia(
{
video: {
facingMode: selectedCamera
},
audio: true
});
addVideoStream(localVideoView, localStream);
} catch (err) {
console.log('(async () =>: ' + err);
}
})();
peer.on("open", (id) => {
socket.emit("join-room", ROOM_ID, id);
});
peer.on("error", (err) => {
console.log('peer.on("error": ' + err);
})
socket.on("user-disconnected", (userId) => {
if (peers[userId]) {
peers[userId].close();
}
});
// Set up event listener for an "another user" data connection established event
peer.on("connection", (conn) => {
conn.on("data", (data) => {
console.log('Received data ' + data);
});
// Set up event listener for connection conn established event
conn.on("open", () => {
conn.send('Hello!');
});
});
// Initiate a Data call (Messages) to user
const connectDataToNewUser = (userId) => {
let conn = peer.connect(userId);
conn.on("data", (data) => {
console.log('Received data: ' + data);
});
conn.on("open", () => {
conn.send('hi!');
});
};
// Initiate a Media call (Audio/Video) to user
const connectMediaToNewUser = (userId, stream) => {
const call = peer.call(userId, stream);
call.on("stream", (userVideoStream) => {
addVideoStream(remoteVideoView, userVideoStream);
});
call.on("close", () => {
remoteVideoView.remove();
});
call.on("error", (error) => {
console.log('connectMediaToNewUser' + error);
});
peers[userId] = call;
};
const addVideoStream = (video, stream) => {
video.srcObject = stream;
video.addEventListener("loadedmetadata", () => {
if(disbaleSelfStream){
systemStream.getVideoTracks()[0].enabled = false
systemStream.getAudioTracks()[0].enabled = false
} else {
loader.style.opacity = 0
video.style.opacity = 1
video.play()
remoteVideoDiv.style.opacity = 0
}
});
};
服务器端代码:
const express = require('express')
const app = express()
const httpPort = process.env.PORT || 80
const httpsPort = 443
const { ExpressPeerServer } = require('peer')
const path = require('path')
const http = require('http')
const https = require('https')
const fs = require('fs')
// Certificate & credentials
const privateKey = fs.readFileSync(path.join(__dirname, 'certs', 'key.pem'))
const certificate = fs.readFileSync(path.join(__dirname, 'certs', 'cert.pem'))
const credentials = {
key: privateKey,
cert: certificate
}
const httpsServer = https.createServer(credentials, app).listen(httpsPort, () => { console.log('Peer Server listening to port ' + httpsPort) })
const peerServer = ExpressPeerServer(httpsServer, {
debug: true,
path: '/myapp'
})
app.use(peerServer)
const io = require('socket.io')(httpsServer, {
forceNew: true,
transports: ["polling"],
})
const { v4: uuidV4 } = require('uuid')
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.get('/', (req, res) => {
res.redirect(`/${uuidV4()}`)
})
app.get('/:room', (req, res) => {
res.render('room', { roomId: req.params.room })
})
io.on('connection', (socket) => {
socket.on('join-room', (roomId, userId) => {
socket.join(roomId)
socket.broadcast.to(roomId).emit('user-connected', userId)
socket.on('disconnect', () => {
socket.broadcast.to(roomId).emit('user-disconnected', userId)
})
socket.on('text-message', message => {
socket.broadcast.to(roomId).emit('text-message-received', message)
})
socket.on('system-stream-updated', remoteUserId => {
socket.broadcast.to(roomId).emit('new-remote-stream', remoteUserId)
})
})
})
和 room.ejs(如果需要)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script>
const ROOM_ID = "<%= roomId %>"
</script>
<script src="peer.min.js" defer></script>
<script src="/socket.io/socket.io.js" defer></script>
<script src="client.js" defer></script>
<title>Interface</title>
<style type="text/css">
html, body {
padding: 0;
margin: 0;
}
.container, .local-video {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
}
.remote-video-div {
position: absolute;
max-width: 30%;
width: 30%;
margin: 16px;
}
.remote-video {
max-width: 100%;
width: 100%;
margin-bottom: -5px;
}
.video-inset {
outline: unset;
visibility: hidden;
position: relative;
margin:0;
padding:0;
}
.background-black {
background-color: #000000 !important;
}
.display-none {
display: none;
}
.loader {
margin: 250px auto;
border: 7px solid #9e9c9c;
border-radius: 50%;
border-top: 7px solid #ffffff;
width: 40px;
height: 40px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<link rel="icon" type="image/ico" href="favicon.ico"/>
</head>
<body>
<div class="local-video-div background-black">
<video class="local-video" autoplay></video>
</div>
<div class="container background-black display-none" id="loader">
<div class="loader"></div>
</div>
<div class="remote-video-div background-black">
<video class="remote-video" autoplay onclick="remoteVideoClick()"></video>
</div>
<div class="container background-black display-none" id="conference">
</div>
</body>
</html>
提前致谢。