我正在尝试使用 WebRTC 创建一个用于视频/音频流的服务器。在使用 node.js 中的示例文件后,我设法在视频通话期间显示两个屏幕。然而,当网络发生变化时,双方的屏幕将不再出现。
我考虑在 WSL 上安装 Ubuntu 并应用 Coturn 来启用中继,因为我读到它允许外部访问。我按照说明进行操作,但结果仍然相同 - 当网络不同时它不起作用。
所以,我尝试在 VirtualBox 上安装 Ubuntu 并试了一下,但仍然不起作用。
我很困惑,不知道哪里出了问题。我可以向您展示很多东西,因此解释可能会很长。如果您有时间的话,如果您能看一下并提供一些建议,我将不胜感激。
谢谢你。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<div id="videos">
<video id="localVideo" autoplay muted playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
</div>
<!-- This file is automatically added/served when running "node index.js". -->
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
index.js
'use strict';
var os = require('os');
var nodeStatic = require('node-static');
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('./private.pem'),
cert: fs.readFileSync('./public.pem')
};
var socketIO = require('socket.io');
var fileServer = new(nodeStatic.Server)();
let app = https.createServer(options, (req,res)=>{
fileServer.serve(req, res);
}).listen(3000);
console.log('Started chating server...');
var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {
// convenience function to log server messages on the client
function log() {
var array = ['Message from server:'];
array.push.apply(array, arguments);
socket.emit('log', array);
}
socket.on('message', function(message) {
log('Client said: ', message);
if (message==="bye" && socket.rooms['foo']) {
io.of('/').in('foo').clients((error, socketIds) => {
if (error) throw error;
socketIds.forEach(socketId => {
io.sockets.sockets[socketId].leave('foo');
});
});
}
socket.broadcast.emit('message', message);
});
socket.on('create or join', function(room) {
log('Received request to create or join room ' + room);
var clientsInRoom = io.sockets.adapter.rooms[room];
var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
log('Room ' + room + ' now has ' + numClients + ' client(s)');
if (numClients === 0) {
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
console.log('created');
} else if (numClients === 1) {
log('Client ID ' + socket.id + ' joined room ' + room);
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
console.log('joined');
} else { // max two clients
socket.emit('full', room);
}
});
socket.on('ipaddr', function() {
var ifaces = os.networkInterfaces();
for (var dev in ifaces) {
ifaces[dev].forEach(function(details) {
if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
socket.emit('ipaddr', details.address);
}
});
}
});
socket.on('bye', function(){
console.log('received bye');
});
});
main.js
'use strict';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pcConfig = {
'iceServers': [
{"urls":["turn:publicIP:3478?transport=tcp"],"username":"test","credential":"test"}
]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
/////////////////////////////////////////////
var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');
var socket = io.connect();
if (room !== '') {
socket.emit('create or join', room);
console.log('Attempted to create or join room', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isChannelReady = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message);
}
// This client receives a message
socket.on('message', function(message) {
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
function gotStream(stream) {
console.log('Adding local stream.');
localStream = stream;
localVideo.srcObject = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
var constraints = {
video: true
};
console.log('Getting user media with constraints', constraints);
if (location.hostname !== 'localhost') {
requestTurn(
'https://publicIP:3000/turn?username=41784574&key=4080218913'
);
}
function maybeStart() {
console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
console.log('>>>>>> creating peer connection');
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function() {
sendMessage('bye');
};
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(pcConfig);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('icecandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log('End of candidates.');
}
}
function handleCreateOfferError(event) {
console.log('createOffer() error: ', event);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer().then(
setLocalAndSendMessage,
onCreateSessionDescriptionError
);
}
function setLocalAndSendMessage(sessionDescription) {
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message', sessionDescription);
sendMessage(sessionDescription);
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function requestTurn(turnURL) {
var turnExists = false;
for (var i in pcConfig.iceServers) {
if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
turnExists = true;
turnReady = true;
break;
}
}
if (!turnExists) {
console.log('Getting TURN server from ', turnURL);
// No TURN server. Get one from computeengineondemand.appspot.com:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var turnServer = JSON.parse(xhr.responseText);
console.log('Got TURN server: ', turnServer);
pcConfig.iceServers.push({
'urls': 'turn:' + turnServer.username + '@' + turnServer.turn,
'credential': turnServer.password
});
turnReady = true;
}
};
xhr.open('GET', turnURL, true);
xhr.send();
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteStream = event.stream;
remoteVideo.srcObject = remoteStream;
remoteVideo.classList.add("remoteVideoInChatting");
localVideo.classList.add("localVideoInChatting");
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
remoteVideo.classList.remove("remoteVideoInChatting");
localVideo.classList.remove("localVideoInChatting");
console.log('Session terminated.');
stop();
isInitiator = false;
}
function stop() {
isStarted = false;
pc.close();
pc = null;
}
我已经在 VirtualBox 上设置了 Ubuntu,网络处于桥接模式,并且我已经将端口 3478 和 5349 的端口转发配置到相应的 IP。运行 Node.js 的 Windows PC 使用端口 3000。
此外,Windows PC 防火墙允许端口 5349、3478 和 3000 的入站和出站访问。
这是我在 coturn 中创建用户时输入的命令 :~$ Turnadmin -a -u 测试 -p 测试 -r 测试名称
但是,当我使用 Trickle ICE 进行测试时,出现 600 错误。
我认为 coturn 服务器正在工作,并且我希望看到来自在 Node.js 上运行的 WebRTC 服务器的视频,甚至可以从外部网络看到。