Javascript WebRTC未能添加第三个对等方:“0”;无法将远程应答设置为“状态稳定”;
我正在写一个多点WebRTC视频聊天。 两个对等方在连接上没有问题,控制台中没有错误或警告,视频工作正常,但我无法成功地将第三方添加到聊天中 在主机(第一个参与者Firefox)上,尝试创建应答时,错误显示为“无法将远程应答设置为状态稳定”。在第二个参与者(Chrome)处,错误为“未能设置远程应答sdp:调用错误状态:状态\u INPROGRESS”。在第三个对等点,错误为“错误为”,无法设置远程应答sdp:在错误状态下调用:state_RECEIVEDINITIATE“ 结果是,第一个对等方无法与第三个对等方进行视频连接。其他两个链接看起来都很好 通常,我的通信模型如下,Javascript WebRTC未能添加第三个对等方:“0”;无法将远程应答设置为“状态稳定”;,javascript,html,websocket,webrtc,Javascript,Html,Websocket,Webrtc,我正在写一个多点WebRTC视频聊天。 两个对等方在连接上没有问题,控制台中没有错误或警告,视频工作正常,但我无法成功地将第三方添加到聊天中 在主机(第一个参与者Firefox)上,尝试创建应答时,错误显示为“无法将远程应答设置为状态稳定”。在第二个参与者(Chrome)处,错误为“未能设置远程应答sdp:调用错误状态:状态\u INPROGRESS”。在第三个对等点,错误为“错误为”,无法设置远程应答sdp:在错误状态下调用:state_RECEIVEDINITIATE“ 结果是,第一个对等方
self\u id
是会话中每个对等方的唯一id,locate\u peer\u connection()
将返回我们从中接收消息的特定对等方的本地对等方连接:
- 新客户端使用信令服务器向会话发送“对等到达”
- 会话中已存在的所有对等方setlocaldescription,创建报价并发送到新客户端
- 新客户端创建对所有其他对等方和setremotedescription的应答
- 新客户的视频即将发布
- self_id是会话中每个客户端的唯一id
- 对等连接存储与其他节点的对等连接,对等id存储这些对象各自的用户id
- local_stream是来自getUserMedia的本地视频流(已被视为不同的浏览器)
// locate a peer connection according to its id
function locate_peer_connection(id) {
var index = peer_id.indexOf(id);
// not seen before
if (index == -1) {
add_peer_connection();
peer_id.push(id);
index = peer_id.length - 1;
}
return index;
}
// add a peer connection
function add_peer_connection() {
console.log('add peer connection');
// add another peer connection for use
peer_connection.push(new rtc_peer_connection({ "iceServers": [{ "url": "stun:"+stun_server }]}));
// generic handler that sends any ice candidate to the other peer
peer_connection[peer_connection.length - 1].onicecandidate = function (ice_event) {
if (ice_event.candidate) {
signaling_server.send(
JSON.stringify({
type: "new_ice_candidate",
candidate: ice_event.candidate,
id: self_id,
token:call_token
})
);
console.log('send new ice candidate, from ' + self_id);
}
};
// display remote video streams when they arrive using local <video> MediaElement
peer_connection[peer_connection.length - 1].onaddstream = function (event) {
video_src.push(event.stream); // store this src
video_src_id.push(peer_connection.length - 1);
if (video_src.length == 1) { // first peer
connect_stream_to_src(event.stream, document.getElementById("remote_video"));
// video rotating function
setInterval(function() {
// rorating video src
var video_now = video_rotate;
if (video_rotate == video_src.length - 1) {
video_rotate = 0;
} else {
video_rotate++;
}
var status = peer_connection[video_src_id[video_rotate]].iceConnectionState;
if (status == "disconnected" || status == "closed") { // connection lost, do not show video
console.log('connection ' + video_rotate + ' liveness check failed');
} else if (video_now != video_rotate) {
connect_stream_to_src(video_src[video_rotate], document.getElementById("remote_video"));
}
}, 8000);
// hide placeholder and show remote video
console.log('first remote video');
document.getElementById("loading_state").style.display = "none";
document.getElementById("open_call_state").style.display = "block";
}
console.log('remote video');
};
peer_connection[peer_connection.length - 1].addStream(local_stream);
}
// handle new peer
function new_peer(signal) {
// locate peer connection
var id = locate_peer_connection(signal.id);
console.log('new peer ' + id);
// create offer
peer_connection[id].createOffer(function(sdp) {
peer_connection[id].setLocalDescription(sdp,
function() { // call back
console.log('set local, send offer, connection '+ id);
signaling_server.send(
JSON.stringify({
token: call_token,
id: self_id,
type:"new_offer",
sdp: sdp
})
);
}, log_error);
}, log_error);
}
// handle offer
function new_offer_handler(signal) {
var id = locate_peer_connection(signal.id);
console.log('new offer ' + id);
// set remote description
peer_connection[id].setRemoteDescription(
new rtc_session_description(signal.sdp),
function() { // call back
peer_connection[id].createAnswer(function(sdp) {
peer_connection[id].setLocalDescription(sdp, function () {
console.log('set local, send answer, connection '+ id);
signaling_server.send(
JSON.stringify({
token: call_token,
id: self_id,
type:"new_answer",
sdp: sdp
})
);
},
log_error);
}, log_error);
}, log_error);
}
// handle answer
function new_answer_handler(signal) {
var id = locate_peer_connection(signal.id);
console.log('new answer ' + id);
peer_connection[id].setRemoteDescription(new rtc_session_description(signal.sdp),
function() {
console.log('receive offer answer, set remote, connection '+ id);
}
, log_error);
}
// handle ice candidate
function ice_candidate_handler(signal) {
var id = locate_peer_connection(signal.id);
console.log('get new_ice_candidate from ' + id);
if (typeof(RTCIceCandidate) != "undefined") {
peer_connection[id].addIceCandidate(
new RTCIceCandidate(signal.candidate)
);
} else { // firefox
peer_connection[id].addIceCandidate(
new mozRTCIceCandidate(signal.candidate)
);
}
}
function event_handler(event) {
var signal = JSON.parse(event.data);
if (signal.type === "peer_arrival") {
new_peer(signal);
} else if (signal.type === "new_ice_candidate") {
ice_candidate_handler(signal);
} else if (signal.type === "new_offer") { // get peer description offer
new_offer_handler(signal);
} else if (signal.type === "new_answer") { // get peer description answer
new_answer_handler(signal);
} else if (signal.type === "new_chat_message") { // chat message and file sharing info
add_chat_message(signal);
} else if (signal.type === "new_file_thumbnail_part") { // thumbnail
store_file_part(signal.name, "thumbnail", signal.id, signal.part, signal.length, signal.data);
if (file_store[signal.id].thumbnail.parts.length == signal.length) {
document.getElementById("file_list").innerHTML = get_file_div(signal.id, signal.name)+document.getElementById("file_list").innerHTML;
document.getElementById("file-img-"+signal.id).src = file_store[signal.id].thumbnail.parts.join("");
}
} else if (signal.type === "new_file_part") { // file
console.log('get new_file_part ' + signal.id);
store_file_part(signal.name, "file", signal.id, signal.part, signal.length, signal.data);
update_file_progress(signal.name, signal.id, file_store[signal.id].file.parts.length, signal.length);
}
}
// generic error handler
function log_error(error) {
console.log(error);
}
//根据对等连接的id定位对等连接
功能定位\u对等\u连接(id){
var指数=对等id.indexOf(id);
//前所未见
如果(索引==-1){
添加对等连接();
对等推送(id);
索引=peer_id.length-1;
}
收益指数;
}
//添加对等连接
函数add_peer_connection(){
log(“添加对等连接”);
//添加另一个对等连接以供使用
peer_connection.push(新的rtc_peer_连接({“iceServers”:[{“url”:“stun:+stun_server}]}));
//将任何ice候选者发送到其他对等方的通用处理程序
对等连接[peer\u connection.length-1]。onicecandidate=函数(ice\u事件){
if(ice_事件候选){
信令发送(
JSON.stringify({
类型:“新的ice候选人”,
候选人:ice_事件候选人,
id:self_id,
令牌:呼叫令牌
})
);
console.log('从'+self_id'发送新的ice候选者);
}
};
//使用本地MediaElement显示到达时的远程视频流
对等连接[peer\u connection.length-1]。onaddstream=函数(事件){
video_src.push(event.stream);//存储此src
视频\u src\u id.push(peer\u connection.length-1);
如果(video_src.length==1){//第一个对等点
将_stream_连接到_src(event.stream,document.getElementById(“远程视频”));
//视频旋转功能
setInterval(函数(){
//旋转视频
var video\u now=video\u rotate;
if(video\u rotate==video\u src.length-1){
视频旋转=0;
}否则{
视频旋转++;
}
var status=peer\u connection[video\u src\u id[video\u rotate]]。iceConnectionState;
如果(状态==“断开连接”| |状态==“关闭”){//连接丢失,则不显示视频
console.log('connection'+video_rotate+'liveness check failed');
}else if(视频\u now!=视频\u旋转){
将_stream_连接到_src(video_src[video_rotate],document.getElementById(“remote_video”);
}
}, 8000);
//隐藏占位符并显示远程视频
console.log(“第一个远程视频”);
document.getElementById(“加载状态”).style.display=“无”;
document.getElementById(“打开\调用\状态”).style.display=“块”;
}
console.log(“远程视频”);
};
对等连接[peer\u connection.length-1].addStream(本地\u流);
}
//处理新同事
功能新对等(信号){
//定位对等连接
var id=定位对等连接(signal.id);
console.log('newpeer'+id);
//创建报价
对等连接[id]。createOffer(函数(sdp){
对等连接[id]。setLocalDescription(sdp,
函数(){//回调
log('set local,send offer,connection'+id);
信令发送(
JSON.stringify({
令牌:调用令牌,
id:self_id,
键入:“新报价”,
sdp:sdp
})
);
},日志错误);
},日志错误);
}
//处理报价
新功能\u提供\u处理程序(信号){
var id=定位对等连接(signal.id);
console.log(“新报价”+id);
//设置远程描述
对等连接[id]。setRemoteDescription(
新的rtc会话描述(signal.sdp),
函数(){//回调
对等连接[id]。createAnswer(函数(sdp){
对等连接[id]。setLocalDescription(sdp,函数(){
log('set local,send answer,connection'+id);
信令发送(
JSON.stringify({
令牌:调用令牌,
id:self_id,
键入:“新答案”,
sdp:sdp
})
);
},
日志错误);
},日志错误);
},日志错误);
}
//处理答案
功能新应答处理器(信号){
var id=定位对等连接(signal.id);
console.log('new answer'+id);
对等连接[id].setRemoteDescription(新的rtc_会话_说明(signal.sdp),
函数(){
console.log('receive offer answer,set remote,connection
if(peer_connection[signal.id]){
//do something...
}else{
peer_connection[signal.id] = new PeerConnection(...
}
peerconnection=[];//empty array
if(peerconnection[signal.id])
{
//do something with existing peerconnections
}
else
{
peerconnection[signal.id]=new RTCPeerConnection({"stun_server_address"});
//register peerconnection[signal.id].onicecandidate callback
//register peerconnection[signal.id].onaddstream callback
//createoffer
//if local stream is ready then
peerconnection[signal.id].addStream(localStream);
//and rest of the stuff go as it is like in one-to-one call..
//setLocalDescriptor setRemoteDescriptor
}