[英]Remote video not showing up on one end - WebRTC Video chat app
我是 webrtc 的新手并做出反应。 我正在开发一个点对点视频聊天应用程序。 在呼叫端,远程视频和本地视频都会出现。 但在被叫方,只显示本地视频。 我一直试图找出我做错的地方,但无法弄清楚。 当我在处理 ice 候选函数中的 console.log peerconnection 变量时我注意到的一件事是,'connectionState' 在被调用方仍然是 'connecting'。(在调用方是'connected')。
编辑:我修改了代码以提高可读性并使用异步等待。 现在我收到“无法在‘RTCPeerConnection’上执行‘addIceCandidate’:错误处理 ICE 候选”错误。
编辑 2:根据答案修改代码
这是我的原始代码
//refs for my video tag
const localVideoRef = useRef("");
const remoteVideoRef = useRef("");
//video elements
<video ref={remoteVideoRef} playsInline autoPlay className="remoteVideo"></video>
<video ref={localVideoRef} playsInline autoPlay muted className="localVideo"></video>
//button to start call
<button onClick={handleCall}>Call</button>
const handleCall = async () => {
createPeerConnection();
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function(localStream) {
let videoObj = localVideoRef.current;
videoObj.srcObject = localStream;
localStream
.getTracks()
.forEach(track => myPeerConnection.addTrack(track, localStream));
})
.catch("getUserMedia() error: ");
};
let myPeerConnection = null;
function createPeerConnection() {
myPeerConnection = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun2.1.google.com:19302"
}
]
});
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.ontrack = handleTrackEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
}
var isNegotiating = false;
const handleSignalingStateChangeEvent = () => {
isNegotiating = myPeerConnection.signalingState != "stable";
};
function handleNegotiationNeededEvent() {
if (isNegotiating) {
return;
}
isNegotiating = true;
myPeerConnection
.createOffer()
.then(function(offer) {
return myPeerConnection.setLocalDescription(offer);
})
.then(function() {
socket.emit("video-offer", {
from: authContext.user.name,
to: connectedTo,
sdp: myPeerConnection.localDescription
});
});
}
//checking if socket is initialized
if (socket) {
socket.on("gotOffer", data => {
handleVideoOfferMsg(data);
});
socket.on("gotCandidate", data => {
handleNewICECandidateMsg(data);
});
socket.on("gotAnswer", data => {
console.log("inside got answer");
handleGotAnswer(data);
});
}
function handleVideoOfferMsg(msg) {
createPeerConnection();
var desc = new RTCSessionDescription(msg.sdp);
myPeerConnection
.setRemoteDescription(desc)
.then(function() {
return navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
})
.then(function(stream) {
let localStream = stream;
let videoObj = localVideoRef.current;
videoObj.srcObject = stream;
localStream
.getTracks()
.forEach(track => myPeerConnection.addTrack(track, localStream));
})
.then(function() {
return myPeerConnection.createAnswer();
})
.then(function(answer) {
return myPeerConnection.setLocalDescription(answer);
})
.then(function() {
socket.emit("video-answer", {
from: authContext.user.name,
to: connectedTo,
sdp: myPeerConnection.localDescription
});
})
.catch("error");
}
async function handleGotAnswer(msg) {
if (!myPeerConnection) return;
// if (isNegotiating) return;
//I don't know why it's not working (no remote video on the caller side too) when I add above line. So, I am checking signaling state in the below line
if (myPeerConnection.signalingState == "stable") return;
await myPeerConnection.setRemoteDescription(
new RTCSessionDescription(msg.sdp)
);
}
function handleICECandidateEvent(event) {
if (!myPeerConnection) return;
if (isNegotiating) return;
if (event.candidate) {
socket.emit("candidate", {
to: connectedTo,
from: authContext.user.name,
candidate: event.candidate
});
}
}
function handleNewICECandidateMsg(msg) {
if (myPeerConnection.signalingState == "stable") return;
var candidate = new RTCIceCandidate(msg.candidate);
myPeerConnection.addIceCandidate(candidate).catch("error");
}
function handleTrackEvent(event) {
let videoObj = remoteVideoRef.current;
videoObj.srcObject = event.streams[0];
}
这是我的新代码:
let pc1 = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun2.1.google.com:19302"
}
]
});
let pc2 = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun2.1.google.com:19302"
}
]
});
const handleCall = async () => {
let stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
let videoObj = localVideoRef.current;
videoObj.srcObject = stream;
let localStream = stream;
stream
.getTracks()
.forEach(async track => await pc1.addTrack(track, localStream));
pc1.onnegotiationneeded = async function() {
let offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
socket.emit("video-offer", {
from: authContext.user.name,
to: connectedTo,
sdp: pc1.localDescription
});
pc1.onicecandidate = function(event) {
if (event.candidate) {
socket.emit("candidate", {
pc: "pc1",
to: connectedTo,
from: authContext.user.name,
candidate: event.candidate
});
}
};
};
pc1.ontrack = function(event) {
let videoObj = remoteVideoRef.current;
videoObj.srcObject = event.streams[0];
};
};
//listening to socket emits from server related to video chat
if (socket) {
socket.on("gotOffer", data => {
//step 1 of callee
handleVideoOfferMsg(data);
});
socket.on("gotCandidate", data => {
handleNewICECandidateMsg(data);
});
socket.on("gotAnswer", data => {
handleGotAnswer(data);
});
}
async function handleVideoOfferMsg(msg) {
var desc = new RTCSessionDescription(msg.sdp);
await pc2.setRemoteDescription(desc);
let stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
let videoObj = localVideoRef.current;
videoObj.srcObject = stream;
let localStream = stream;
stream
.getTracks()
.forEach(async track => await pc2.addTrack(track, localStream));
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
socket.emit("video-answer", {
from: authContext.user.name,
to: connectedTo,
sdp: pc2.localDescription
});
pc2.ontrack = function(event) {
let videoObj = remoteVideoRef.current;
videoObj.srcObject = event.streams[0];
};
pc2.onicecandidate = function(event) {
if (event.candidate) {
socket.emit("candidate", {
pc: "pc2",
to: connectedTo,
from: authContext.user.name,
candidate: event.candidate
});
}
};
}
async function handleGotAnswer(msg) {
if (pc1.signalingState == "stable") {
console.log("negotiating");
return;
}
await pc1.setRemoteDescription(new RTCSessionDescription(msg.sdp));
//INSERTED THIS
if (candidatesArray.length) {
candidatesArray.forEach(async msg => {
var candidate = new RTCIceCandidate(msg.candidate);
await pc1.addIceCandidate(candidate);
});
}
}
let candidatesArray = [];
async function handleNewICECandidateMsg(msg) {
if (msg.pc == "pc1") {
var candidate = new RTCIceCandidate(msg.candidate);
await pc2.addIceCandidate(candidate);
}
if (msg.pc == "pc2") {
try {
if (pc1.connectionState != "stable" && !pc1.remoteDescription) {
candidatesArray.push(msg);
return;
}
var candidate = new RTCIceCandidate(msg.candidate);
await pc1.addIceCandidate(candidate);
} catch (error) {
//this is where error is triggered.
console.log("error adding ice candidate: " + error);
}
}
}
我没有把我的服务器端代码,因为我发现它没有问题。
据我了解,该错误是因为调用 addicecandidate 时未设置 remotedescription。 可能是因为我在 SignalingState 稳定时跳过了设置远程描述。 但是如果我删除那行代码,我会收到另一个错误 - “无法设置远程应答 sdp:在错误状态下调用:kStable”
我哪里错了?
在调用pc.setLocalDescription()
,PeerConnection 将立即开始发出onicecandidate
事件,这要归功于 Trickle ICE。 但是,这意味着第一个候选对象可能生成得太快,甚至在发送 SDP Offer/Answer 之前它们就被发送到远程对等方!
也许这就是你的情况,第一批候选人从另一边来得太早了。 出于这个原因,检查 PeerConnection 信号状态是个好主意:如果它稳定并且已经设置了远程描述,那么您可以调用pc.addIceCandidate()
。 如果没有,则将候选人存储在队列中。
稍后,当远程描述最终到达时,在设置它之后,您手动添加队列中等待的所有候选者。
在这里你可以看到带有这个想法的代码。 候选者首先排队,然后当 PeerConnection 信令状态stable
, 加入排队的项目。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.