我想从两台电脑上传输视频并在浏览器上看到这两个视频。使用以下代码,我将两个视频都放在一台PC上但不放在另一台PC上,这意味着一台PC正在将视频流式传输到远程的视频,而另一台则不会将视频流式传输到第一台PC。我在这个程序中使用单个peerconnection对象。任何人都可以告诉我这里出了什么问题。
var constraints = { audio: true, video: true };
var configuration = { iceServers:[{
urls: "stun:stun.services.mozilla.com",
username: "louis@mozilla.com",
credential: "webrtcdemo"
}, {
urls:["stun:xx.xx.xx.xx:8085"]
}]
};
var pc;
function handlePeerAvailableCheckMsg(cmd) {
console.log('peer-availability ' + cmd);
start();
if (cmd === "peer-available-yes") {
addCameraMic();
}
}
function addCameraMic() {
var localStream;
var promisifiedOldGUM = function(constraints) {
var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia);
if(!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
if(navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if(navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = promisifiedOldGUM;
}
//get local media (camera, microphone) and render on screen.
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
var lvideo = document.getElementById('lvideo');
localStream = stream;
lvideo.src = window.URL.createObjectURL(localStream);
lvideo.onloadedmetadata = function(e) {
lvideo.play();
};
//Adding a track to a connection triggers renegotiation by firing an negotiationneeded event.
//localStream.getTracks().forEach(track => pc.addTrack(track,localStream));
pc.addStream(stream);
}).catch(function(err) {
console.log("getMediaUser Reject - " + err.name + ": " + err.message);
});
}
function start() {
var RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection;
pc = new RTCPeerConnection(configuration);
// send any ice candidates to the other peer, the local ICE agent needs to deliver a message to the other peer through the signaling server.
// this will be done once the local description is set successfully, icecandidate events will be generated by local ice agent.
pc.onicecandidate = function (evt) {
if (!evt.candidate) return;
console.log('onicecandidate: '+ evt.candidate);
wbClient.send(JSON.stringify({"cmd":"new-ice-candidate","candidate": evt.candidate }));
};
//Start negotiation by setting local description and sending video offer to the remote peer.
pc.onnegotiationneeded = function(evt){
pc.createOffer()
.then(function(offer){ //offer is nothing but SDP
console.log('Local description is '+ offer + ' and its status when offer created' + pc.signalingState);
return pc.setLocalDescription(offer);
})
.then(function(){
wbClient.send(JSON.stringify({"userName":userName,"roomId":roomId,"cmd":"video-offer","sdp":pc.localDescription}));
})
.catch(function(err) {
console.log("[INFO:whiteboard.js:start] - Offer could not be sent to the remote peer - " + err.name + ": " + err.message);
});
};
pc.onaddstream = function (evt) {
console.log('[INFO:whiteboard.js:start] - Receiving video stream from remote peer');
var rvideo = document.getElementById('rvideo');
rvideo.src = window.URL.createObjectURL(evt.stream);
rvideo.onloadedmetadata = function(e) {
console.log('remote video metadata loaded..');
rvideo.play();
};
};
}
//will be invoked when a message from remote peer arrives at this client
function handleVideoOfferMsg(obj) {
var RTCSessionDescription = window.webkitRTCSessionDescription || window.RTCSessionDescription;
//Session description based on received SDP from remote peer.
var desc = new RTCSessionDescription(obj.sdp);
var descJson = desc.toJSON();
console.log('Received session description (new offer) : ' + JSON.stringify(descJson));
//Set remote peer's capabilities based on received SDP
console.log('While processing incoming offer - LDT : ' + pc.localDescription + ' RDT ' + pc.remoteDescription);
addCameraMic();
pc.setRemoteDescription(desc)
.then(function(){
return pc.createAnswer();
})
.then (function(answer){
//var ansJson = answer.toJSON();
return pc.setLocalDescription(answer);
})
.then (function(){
console.log('sending answer to the remote peer ' + pc.localDescription);
wbClient.send(JSON.stringify({"userName":obj.userName,"roomId":roomId,"cmd":"video-answer","sdp":pc.localDescription}));
})
.catch(function(err) {
console.log("Error while sending answer " + err.name + ": " + err.message);
});
}
function handleVideoAnswerMsg(desc) {
if (pc.localDescription === null || pc.localDescription.type !== "offer") return;
var RTCSessionDescription = window.webkitRTCSessionDescription || window.RTCSessionDescription;
var des = new RTCSessionDescription(desc);
var descJson = des.toJSON();
console.log('Received answer session description (new answer) : ' + JSON.stringify(descJson));
pc.setRemoteDescription(des); //set remote description to incoming description object of remote peer
}
function handleNewICECandidateMsg(candidate) {
var RTCIceCandidate = window.webkitRTCIceCandidate || window.RTCIceCandidate;
if (pc.localDescription !== null && pc.remoteDescription !== null && pc.signalingState === "stable")
pc.addIceCandidate(new RTCIceCandidate(candidate))
.catch(function(err) {
console.log('handleNewICECandidateMsg ' + err.name + ": " + err.message);
});
}
答案 0 :(得分:1)
我看到两个问题:
在来电时看起来没有及时添加摄像头:
addCameraMic();
pc.setRemoteDescription(desc)
.then(function(){
return pc.createAnswer();
})
addCameraMic
是一个异步函数,但不返回任何承诺,因此可能会与pc.setRemoteDescription
竞争失败,这意味着pc.createAnswer
在添加流之前运行,导致它回答没有视频。
修复addCameraMic
以返回承诺,并尝试相反:
pc.setRemoteDescription(desc)
.then(addCameraMic)
.then(function() {
return pc.createAnswer();
})
这应确保在回答之前添加流。
注意:请务必先致电pc.setRemoteDescription
,让pc
准备接受ICE候选人,这可能会在优惠之后立即到达。
除此之外,您的handleNewICECandidateMsg
看起来错了:
function handleNewICECandidateMsg(candidate) {
if (pc.localDescription !== null && pc.remoteDescription !== null && pc.signalingState === "stable")
pc.addIceCandidate(new RTCIceCandidate(candidate))
Trickle ICE重叠了提议/答案交换,并且不等待stable
状态。
ICE候选人在对等方的setLocalDescription
成功回调完成后立即从对等方开始触发,这意味着您的信令通道 - 只要保留顺序 - 如下所示:offer, candidate, candidate, candidate
,单向和另一个answer, candidate, candidate, candidate
。基本上,如果您看到候选人,请添加它:
function handleNewICECandidateMsg(candidate) {
pc.addIceCandidate(new RTCIceCandidate(candidate))
希望有所帮助。