Video streaming 使用webrtc的双向视频流
我想从两台电脑上传输视频,并在浏览器上查看这两个视频。使用下面的代码,我在一台电脑上获得两个视频,但在另一台电脑上没有,这意味着一台电脑正在将视频流传输到远程电脑,而另一台电脑没有将其视频流传输到第一个对等机。我在这个程序中使用单个peerconnection对象。谁能告诉我这里出了什么问题吗Video streaming 使用webrtc的双向视频流,video-streaming,webrtc,two-way,Video Streaming,Webrtc,Two Way,我想从两台电脑上传输视频,并在浏览器上查看这两个视频。使用下面的代码,我在一台电脑上获得两个视频,但在另一台电脑上没有,这意味着一台电脑正在将视频流传输到远程电脑,而另一台电脑没有将其视频流传输到第一个对等机。我在这个程序中使用单个peerconnection对象。谁能告诉我这里出了什么问题吗 var constraints = { audio: true, video: true }; var configuration = { iceServers:[{
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);
});
}
我看到两个问题:
照相机正在加速
在接到来电时,似乎没有及时添加摄像头:
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候选人,他们可能会在报价后立即到达
Ice候选人比你想象的来得早
除此之外,您的handleNewICECandidateMsg
看起来是错误的:
function handleNewICECandidateMsg(candidate) {
if (pc.localDescription !== null && pc.remoteDescription !== null && pc.signalingState === "stable")
pc.addIceCandidate(new RTCIceCandidate(candidate))
涓流冰与报价/应答交换重叠,不等待<代码>稳定代码>状态
当对等方的setLocalDescription
成功回调完成后,ICE候选方就开始从对等方启动,这意味着您的信令通道(只要它保持顺序)如下所示:offer,candidate,candidate,candidate
,一种方式,和应答,candidate,candidate,candidate
另一种方式。基本上,如果您看到候选人,请添加:
function handleNewICECandidateMsg(candidate) {
pc.addIceCandidate(new RTCIceCandidate(candidate))
希望对您有所帮助。我还强烈推荐官方WebRTC polyfill处理浏览器更改。这将大大简化此代码。我尝试了上述建议,但没有得到结果。我还有一个疑问。我正在使用单个PeerConnection对象来连接对等点a和对等点B。对等点a和对等点B将在各自的端口上侦听。现在,我可以使用此单一连接将视频流从对等点A传输到对等点B,反之亦然,还是需要创建另一个对等连接对象以将视频流从对等点B传输到对等点A,我们是否需要两个PeerConnection对象在使用WebRTC的P2P视频连接中来回传输视频,或者一个就足够了。当我使用上述代码中所述的单个PeerConnection对象时,我会在创建报价或应答之前调用onaddstream。尽管如此,两个视频都只在一个对等点上显示,但在另一个对等点上,我没有收到远程流。无法理解这里出了什么问题。我的问题解决了,我的上述疑问也消除了。创建对等连接对象并设置其处理程序(如onaddstream、OnGotiationRequired)后,仅当您确定其他对等方已联机并准备就绪时,才可以使用onicecandidate发送要约。为了实现这一点,我使用信令服务器来维护一个变量,该变量将由第一个对等方设置。然后第二个对等方确定另一个对等方已经在线,因此它可以发送要约消息。通过这种方式,我使用信令服务器来协调提供/应答交换。在发送报价/应答之前,我正在调用addStream方法。