我正在使用WebRTC(HTML + Javascript)编写语音/音频会议应用程序 当我运行2个客户端时,它们成功连接到同一房间,第一个是该房间的发起者或创建者,另一个是具有不同ID的“客人”。如果我从来宾端开始网络摄像头通话,则可以正常工作,我既可以看到网络摄像头,也可以听到每一面的声音,但是如果我从发起方端开始,则会出现此错误:
Uncaught TypeError: Cannot read property 'sdpMid' of null main.js:178
at handleIceCandidate (main.js:178)
at RTCPeerConnection.wrappedCallback (adapter-latest.js:5259)
可能是什么问题? 代码:
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="shortcut icon" href="">
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<div>
<!-- style 'display = none' -->
<button id = "chiama" onclick = "chiama()">Chiama</button><br/>
<button id = "createId" onclick = "createId()">create ID</button>
</div>
<div id="videos">
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
</div>
<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>
main.js:
'use strict';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var constraints = { audio: true, video: true };
var pcConfig = {
'iceServers': [{
'urls': 'stun:stun.l.google.com:19302'
}]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
/////////////////////////////////////////////
var 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;
window.location.hash = 1111;
});
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;
let clientId = Math.floor(Math.random() * 100) + 1;
window.location.hash = clientId;
});
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');
//BOTTONE CHIAMA
function chiama() {
// document.getElementById('chiama').addEventListener('click', function() {
navigator.mediaDevices.getUserMedia(constraints)
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}
//BOTTONE GENERA ID
function createId(){
// document.getElementById('createId').addEventListener('click', function() {
let clientId = Math.floor(Math.random() * 1000) + 1;
window.location.hash = clientId;
// getId.style.display = "none";
// chiama.style.display = "inline";
// })
}
function gotStream(stream) {
console.log('Adding local stream.');
localStream = stream;
localVideo.srcObject = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
console.log('Getting user media with constraints', constraints);
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(null);
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.');
}
console.log('ICEID: ' + event.candidate.sdpMid)
}
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());
}
//Turn Server, viene usato da ICECandidate se Stun non tiene la connessione
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;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
console.log('Session terminated.');
stop();
isInitiator = false;
}
function stop() {
isStarted = false;
pc.close();
pc = null;
}
index.js(服务器):
'use strict';
var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');
var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
fileServer.serve(req, res);
}).listen(8096);
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);
// for a real app, would be room-only (not broadcast)
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;
if (numClients === 0) {
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
log('Room ' + room + ' now has ' + (numClients+1) + ' client(s)');
socket.emit('created', room, socket.id);
} else if (numClients === 1) {
log('Client ID ' + socket.id + ' joined room ' + room);
log('Room ' + room + ' now has ' + (numClients+1) + ' client(s)');
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} 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);
}
});
}
});
});
删除console.log('ICEID:'+ event.candidate.sdpMid) 我解决了启动器错误,但现在在来宾端出现了另一个错误。两者都一直在向我显示网络摄像头,它们在同一个房间里,但我看不到另一个摄像头。 错误代码:
Uncaught TypeError: Cannot read property 'setRemoteDescription' of undefined
at n.<anonymous> (main.js:78)
at n.emit (universalModuleDefinition:2)
at n.onevent (universalModuleDefinition:3)
at n.onpacket (universalModuleDefinition:3)
at n.<anonymous> (universalModuleDefinition:3)
at n.emit (universalModuleDefinition:2)
at n.ondecoded (universalModuleDefinition:1)
at s.<anonymous> (universalModuleDefinition:3)
at s.r.emit (universalModuleDefinition:1)
at s.add (universalModuleDefinition:1)
答案 0 :(得分:0)
event.candidate可以为null。移动
console.log('ICEID: ' + event.candidate.sdpMid)
在if (event.candidate)
块内,您应该会很好。