- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 webRTC 连接两个对等点。我能够正确显示本地和远程视频,但是只要远程视频出现,候选对象就会变为 null
并且在控制台上它会记录以下错误消息。
TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex
我正在使用两台单独的笔记本电脑来测试连接,因为远程和本地视频都在显示,我认为我已经成功连接了两个对等点,但由于错误消息,我不确定。
知道为什么会发生这种情况吗?我是否成功连接了两个对等点?
下面是代码。
谢谢!
前端
import React, { Component } from 'react';
import io from 'socket.io-client';
class App extends Component {
constructor(props) {
super(props);
this.room = 'test-room';
this.socket = io.connect('http://localhost:5000');
this.localPeerConnection = new RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
});
this.remotePeerConnection = new RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
});
};
componentDidMount() {
this.socket.on('connect', () => {
this.socket.emit('join', this.room, err => {
if (err) {
console.error(err);
} else {
this.socket.on('offer', offer => {
console.log('OFFER RECEIVED: ', offer);
this.createAnswer(offer);
});
this.socket.on('candidate', candidate => {
console.log('CANDIDATE RECEIVED', candidate);
this.localPeerConnection.addIceCandidate(candidate).catch(error => console.error(error));
this.remotePeerConnection.addIceCandidate(candidate).catch(error => console.error(error));
});
this.socket.on('answer', answer => {
console.log('ANSWER RECEIVED:', answer);
this.localPeerConnection.setRemoteDescription(answer);
});
}
});
});
}
startCall = async () => {
this.localPeerConnection.onicecandidate = e => {
const iceCandidate = e.candidate;
this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
console.log('candidate generated', e.candidate);
};
this.localPeerConnection.ontrack = e => {
this.remoteVideo.srcObject = e.streams[0];
console.log('REMOTE STREAM?: ', e.streams[0]);
};
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false });
for (const track of stream.getTracks()) {
this.localPeerConnection.addTrack(track, stream);
}
this.localVideo.srcObject = stream;
console.log('LOCAL STREAMS: ', this.localPeerConnection.getLocalStreams())
return this.createOffer();
} catch (error) {
console.error(error);
}
}
createOffer = async () => {
try {
const offer = await this.localPeerConnection.createOffer();
await this.localPeerConnection.setLocalDescription(offer);
await this.remotePeerConnection.setRemoteDescription(offer);
this.socket.emit('offer', { room: this.room, offer });
console.log('SENDING OFFER: ', offer);
} catch (error) {
console.error(error);
}
}
createAnswer = async description => {
this.remotePeerConnection.onicecandidate = e => {
const iceCandidate = e.candidate;
this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
console.log('candidate generated', e.candidate);
};
this.remotePeerConnection.ontrack = e => {
this.remoteVideo.srcObject = e.streams[0];
};
this.remotePeerConnection.setRemoteDescription(description)
.then(() => navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false }))
.then(stream => {
for (const track of stream.getTracks()) {
this.remotePeerConnection.addTrack(track, stream);
}
this.localVideo.srcObject = stream;
return this.remotePeerConnection.createAnswer();
})
.then(answer => {
this.remotePeerConnection.setLocalDescription(answer);
return answer;
})
.then(answer => {
this.socket.emit('answer', { room: this.room, answer });
console.log('SENDING ANSWER: ', answer);
})
.catch(error => console.error(error))
}
render() {
return (
<div>
<h1>Webrtc</h1>
<div>
<button onClick={this.startCall}>CALL</button>
</div>
<div style={{ display: 'flex' }}>
<div>
<video id='localVideo' autoPlay muted playsInline ref={ref => this.localVideo = ref} />
<p>LOCAL VIDEO</p>
</div>
<div>
<video id='remoteVideo' autoPlay muted playsInline ref={ref => this.remoteVideo = ref} />
<p>REMOTE VIDEO</p>
</div>
</div>
</div>
);
}
}
export default App;
服务器
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const PORT = process.env.PORT || 5000;
const connections = [];
const clients = [];
io.set('origins', '*:*');
io.on('connection', socket => {
connections.push(socket);
clients.push({ socket_id: socket.id });
console.log('Connected: %s sockets connected ', connections.length);
socket.on('join', (room, callback) => {
const clients = io.sockets.adapter.rooms[room];
const numClients = (typeof clients !== 'undefined') ? clients.length : 0;
console.log('joined room', room);
if (numClients > 1) {
return callback('already_full');
}
else if (numClients === 1) {
socket.join(room);
io.in(room).emit('ready');
}
else {
socket.join(room);
}
callback();
});
socket.on('offer', (data) => {
const { room, offer } = data;
console.log('offer from: ', offer);
socket.to(room).emit('offer', offer);
});
socket.on('answer', (data) => {
const { room, answer } = data;
console.log('answer from: ', answer);
socket.to(room).emit('answer', answer);
});
socket.on('candidate', (data) => {
const { room, candidate } = data;
console.log('candidate: ', candidate);
socket.to(room).emit('candidate', candidate);
});
socket.on('disconnect', () => {
connections.splice(connections.indexOf(socket), 1);
console.log('Disconnected: %s sockets connected, ', connections.length);
clients.forEach((client, i) => {
if (client.socket_id === socket.id) {
clients.splice(i, 1);
}
});
});
});
server.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
更新
看了jib的评论,我修改了我的客户端js如下。
import React, { Component } from 'react';
import io from 'socket.io-client';
class App extends Component {
constructor(props) {
super(props);
this.room = 'test-room';
this.socket = io.connect('http://localhost:5000');
this.peerConnection = new RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
});
};
componentDidMount() {
this.socket.on('connect', () => {
this.socket.emit('join', this.room, err => {
if (err) {
console.error(err);
} else {
this.socket.on('offer', offer => {
console.log('OFFER RECEIVED: ', offer);
this.createAnswer(offer);
});
this.socket.on('candidate', candidate => {
this.peerConnection.addIceCandidate(candidate).catch(error => console.error(error));
console.log('CANDIDATE RECEIVED', candidate);
});
this.socket.on('answer', answer => {
console.log('ANSWER RECEIVED:', answer);
this.peerConnection.setRemoteDescription(answer);
});
}
});
});
}
startCall = async () => {
this.peerConnection.oniceconnectionstatechange = () => console.log('ICE CONNECTION STATE: ', this.peerConnection.iceConnectionState);
this.peerConnection.onicecandidate = e => {
const iceCandidate = e.candidate;
this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
console.log('candidate generated', e.candidate);
};
this.peerConnection.ontrack = e => {
this.remoteVideo.srcObject = e.streams[0];
console.log('REMOTE STREAMS: ', this.peerConnection.getRemoteStreams());
};
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false });
for (const track of stream.getTracks()) {
this.peerConnection.addTrack(track, stream);
}
this.localVideo.srcObject = stream;
console.log('LOCAL STREAMS: ', this.peerConnection.getLocalStreams())
return this.createOffer();
} catch (error) {
console.error(error);
}
}
createOffer = async () => {
try {
const offer = await this.peerConnection.createOffer();
await this.peerConnection.setLocalDescription(offer);
this.socket.emit('offer', { room: this.room, offer });
console.log('SENDING OFFER: ', offer);
} catch (error) {
console.error(error);
}
}
createAnswer = async description => {
this.peerConnection.onicecandidate = e => {
const iceCandidate = e.candidate;
this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
console.log('candidate generated', e.candidate);
};
this.peerConnection.ontrack = e => {
this.remoteVideo.srcObject = e.streams[0];
};
this.peerConnection.setRemoteDescription(description)
.then(() => navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false }))
.then(stream => {
for (const track of stream.getTracks()) {
this.peerConnection.addTrack(track, stream);
}
this.localVideo.srcObject = stream;
return this.peerConnection.createAnswer();
})
.then(answer => {
this.peerConnection.setLocalDescription(answer);
return answer;
})
.then(answer => {
this.socket.emit('answer', { room: this.room, answer });
console.log('SENDING ANSWER: ', answer);
})
.catch(error => console.error(error))
}
render() {
return (
<div>
<h1>Webrtc</h1>
<div>
<button onClick={this.startCall}>CALL</button>
</div>
<div style={{ display: 'flex' }}>
<div>
<video id='localVideo' autoPlay muted playsInline ref={ref => this.localVideo = ref} />
<p>LOCAL VIDEO</p>
</div>
<div>
<video id='remoteVideo' autoPlay muted playsInline ref={ref => this.remoteVideo = ref} />
<p>REMOTE VIDEO</p>
</div>
</div>
</div>
);
}
}
export default App;
我的控制台上的错误仍然存在...知道为什么吗?
最佳答案
The error on my console still persists... any idea why?
这是一个 known bug in Chrome (请★修好它!)
要查看它,请在 Chrome 78 的网络控制台中输入以下内容:
const pc = new RTCPeerConnection(); pc.setRemoteDescription(await pc.createOffer())
然后
pc.addIceCandidate(undefined)
它产生:
TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection':
Candidate missing values for both sdpMid and sdpMLineIndex
现在试试
pc.addIceCandidate()
它说:
TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection':
1 argument required, but only 0 present.
两者都违反了latest spec ,它表示 pc.addIceCandidate()
是 “候选结束指示” “适用于所有媒体描述。”/p>
您可以安全地忽略此错误,直到 Chrome 修复它,或者捕获 TypeError
并抑制它。
我建议不要将 if (candidate) pc.addIceCandidate(candidate)
作为解决方法,因为一旦 Chrome 修复了这个问题,它就会阻止 iceConnectionState
进入 "completed"
在 Chrome 或任何其他浏览器中的状态。
关于javascript - WebRTC 无法在控制台上的 RTCPeerConnection 错误上执行 'addIceCandidate' 但仍可以显示远程和本地视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58908081/
我正在开发一个在 VS 社区 2017 中开发并使用 IIS Express 10 的 .net Core MVVC 项目,我遇到了 TempData 无法在我开发的 3 台计算机中的两台上运行的问题
我是一名优秀的程序员,十分优秀!