我正在写一个多同步的WebRTC视频聊天。 两个对等连接没有问题,控制台没有错误或警告,视频效果很好,但我无法成功地为聊天添加第三方。
在主机(第一个参与者,Firefox)上,在尝试创建答案时,错误显示为“无法在州稳定状态下设置远程答案”。在第二个参与者(Chrome),错误是“无法设置远程回答sdp:调用错误的状态:STATE_INPROGRESS”。在第三个对等点,错误是“错误”无法设置远程回答sdp:调用错误状态:STATE_RECEIVEDINITIATE“。
事实证明,第一个对等方无法与第三个对等方拥有视频。其他两个链接显得很好。
通常,我的通信模型如下所示,self_id
是会话中每个对等方的唯一ID,locate_peer_connection()
将返回我们收到消息的特定对等方的本地peer_connection: / p>
新客户端使用信令服务器向会话发送“peer_arrival”
所有同行已在会话setlocaldescription中,创建商品并发送给新客户
新客户创建所有其他同行的答案和setremotedescription
新客户即将推出视频
使用node.js服务器上的WebSocket完成信令。
我有一些核心代码,更多注意事项:
self_id是会话中每个客户的唯一ID
peer_connection将peerConnection存储到其他节点,peer_id存储这些对象的相应user_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);
}
答案 0 :(得分:1)
我可能错了,但这两个一定是你的主要问题:
DateTime.parse("2015-05-08 02:30:01 +0800").strftime("%Y-%m-%d %H")
=> "2015-05-08 02"
我在这里没有看到任何目标,因此猜测服务器只是向所有人广播此消息。当您将sdp发送到已建立的对等连接时,您必然会收到您现在获得的错误。我的建议是在消息中添加目标id,服务器可以将其转发给特定的对等体,或者服务器可以只是广播,但是对等体的signaling_server.send(...
可以检查消息的目标id是否与它是自己的id,如果没有,只需忽略该消息。
event_handler
事件,您正在向所有远程对等体广播ICE候选者,同样这是针对单个对等体的,另一个问题可能是PeerConnection上的onicecandidate
,然后再将其设置为本地和远程description会抛出错误,你需要某种机制来处理这个问题(仅在设置连接描述后添加ICE候选者)。
终于有了一个建议。我猜addIceCandidate
是一个数组,如果你更改它,你可以删除peer_connection
的冗余,
locate_peer_connection
答案 1 :(得分:1)
当我实施一对多rtc广播时,我遇到了同样的问题,而mido22说的是正确的。您可能正在使用其他传入客户端发送/重置现有已建立的对等对象。您必须为每个新传入的客户端创建新的RTCPeerConenction对象。一般工作流程如下。
peerconnection=[];//empty array
然后使用getUserMedia初始化您的媒体设备 并将媒体流存储到全局变量中,以便在创建商品时添加它 完成此操作后,您可以通过唯一的身份通知您的服务器 然后,您的信令服务器可以将此广播到所有客户端,除非接收到它。然后,每个客户端将检查是否存在peerconnection数组中的唯一ID,并且像mido22表示你可以这样做
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
}