本文介绍了如何使用提供/回答交换来自两个对等连接的流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试设置视频聊天,其中两个对等连接交换视频.这是在创建数据通道之后发生的.所以这是事件的过程:

I am trying to setup video chat where two peer connections exchange video. This happens after creating a data channel. So this is the process of events:

  1. offerer 创建数据通道,offerer 创建并提供,answerer 创建答案.到现在为止还挺好.我们有一个数据通道.
  2. offerer通过getUserMedia获取视频流并添加到peer connection,然后offereronnegotiation事件> 触发,offerer 创建一个新的offer,而answerer 回复一个答案.还是一切都好.要约正在流式传输.
  3. answerer 通过 getUserMedia 获取视频流并将其添加到对等连接中,offerer 创建一个新的 offer,而 answerer 回答.还可以.回答者也在直播.
  1. offerer creates data channel, offerer creates and offer, answerer creates an answer. So far so good. We have a datachannel.
  2. offerer gets video stream through getUserMedia and adds it to the peer connection, then the onnegotiation event of offerer fires, offerer creates a new offer, while answerer responds with an answer. Still all good. The offerer is streaming.
  3. answerer gets video stream through getUserMedia and adds it to the peer connection, offerer creates a new offer, while answerer responds with an answer. Still ok. The answerer is streaming too.

但是,如果我切换第 2 步和第 3 步(因此回答者首先开始流式传输),那么事情就会开始出错.双方必须在步骤 1、3 和 2 都完成后才开始流式传输.

However, if I switch step 2 and 3 (so answerer starts streaming first) then things start to go wrong. Both sides have to start streaming only after steps 1, 3 and 2 all have taken place.

我很确定这与 SDP 提供和回答的顺序有关.

I am pretty sure it has something to do with the order of SDP offers and answers.

当我让回答者在有onnegotiationneeded 事件时创建新提议时,行为有所不同,但仍然不稳定.

When I let the answerer create a new offer when it has an onnegotiationneeded event the behavior is different but still erratic.

我现在对如何添加优惠和答案一无所知.

I'm quite clueless now about how to add the offers and answers.

代码如下:

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
PeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.RTCPeerConnection;
IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate || window.RTCIceCandidate;
SessionDescription =  window.mozRTCSessionDescription || window.webkitRTCSessionDescription || window.RTCSessionDescription;

var videoOfferer = document.getElementById('videoOfferer');
var videoAnswerer = document.getElementById('videoAnswerer');
var buttonOfferer = document.getElementById('buttonOfferer');
var buttonAnswerer = document.getElementById('buttonAnswerer');

var servers = {
    iceServers: [
        {url: "stun:23.21.150.121"},
        {url: "stun:stun.1.google.com:19302"}
    ]
};

var offerer = new PeerConnection(servers), answerer = new PeerConnection(servers);
var channelOfferer = null, channelAnswerer = null;

offerer.onicecandidate = function(e) {
    if(e.candidate == null) return;
    answerer.addIceCandidate(new IceCandidate(e.candidate), function(){}, error);
};

offerer.onaddstream = function(e) {
    videoOfferer.src = URL.createObjectURL(e.stream);
    videoOfferer.play();
};

answerer.onicecandidate = function(e) {
   if(e.candidate == null) return;
    offerer.addIceCandidate(new IceCandidate(e.candidate), function(){}, error);
};

answerer.onaddstream = function(e) {
    videoAnswerer.src = URL.createObjectURL(e.stream);
    videoAnswerer.play();
};

function offerCreated(sdp) {
    console.log('offer');
    offerer.setLocalDescription(new SessionDescription(sdp), function() {
        answerer.setRemoteDescription(new SessionDescription(sdp), function() {
            answerer.createAnswer(answerCreated, error);
        }, error);
    }, error);
}

function answerCreated(sdp) {
    console.log('answer');
    answerer.setLocalDescription(new SessionDescription(sdp), function() {
    }, error);
    offerer.setRemoteDescription(new SessionDescription(sdp), function() {
    }, error);
}

function error() {}

buttonOfferer.addEventListener('click', function() {
    navigator.getUserMedia({audio: true, video: true}, function(stream) {
        offerer.addStream(stream);
    }, function(){});
});

buttonAnswerer.addEventListener('click', function() {
    navigator.getUserMedia({audio: true, video: true}, function(stream) {
        answerer.addStream(stream);
    }, function(){});
});

channelOfferer = offerer.createDataChannel('channel', {reliable: true});

offerer.createOffer(offerCreated, error);

answerer.ondatachannel = function(e) {
    channelOfferer = e.channel;
    channelOfferer.onmessage = function(e) {
        console.log(e.data);
    };
    channelOfferer.onmessage = function(e) {
        console.log(e.data);
    };

      // these are added later
    offerer.onnegotiationneeded = function() {
        offerer.createOffer(offerCreated, error);
    };

    answerer.onnegotiationneeded = function() {
        offerer.createOffer(offerCreated, error);
    };
};

推荐答案

我认为问题出在步骤 3 中,当回答者添加视频时,您让提供者启​​动.在真正的远程通话中,提供者怎么会知道这样做?

I think the problem is in step 3 where you have the offerer initiating when the answerer adds a video. In a real remote call, how would the offerer even know to do that?

据我所知,当回答者需要重新谈判时,角色实际上是颠倒过来的,因为为了重新谈判的目的:重新谈判者充当提议者,而非重新谈判者充当提议者回答者.

As I understand it, when the answerer needs to renegotiate then the roles are effectively reversed, because for the purposes of renegotiation: the renegotiator acts as the offerer, while the non-renegotiator acts as the answerer.

换句话说:对pc.onnegotiationneeded的响应总是:

In other words: the response to pc.onnegotiationneeded is always:

  1. createOffer(),
  2. 然后 setLocalDescription(description)
  3. 然后将pc.localDescription发送给对方
  1. createOffer(),
  2. then setLocalDescription(description)
  3. then send pc.localDescription to the other side

无论哪一边.

我不是 SDP 的权威,所以我不确定这是正确的方法,但是 规范中的示例至少向我表明上述步骤是正确的,并且我以这种方式工作.

I'm not an authority on SDP, so I don't know for sure that this is the right approach, but the examples in the spec at least suggest to me that the steps above are right, and I got it working this way.

我已经在 这个 Firefox jsfiddle 中对此进行了测试,它似乎有效.小提琴使用说明:

I've tested this out in this Firefox jsfiddle and it seems to work. Instructions for using the fiddle:

  1. 没有服务器(因为它是一个小提琴),所以按 Offer 按钮并复制报价.
  2. 将优惠粘贴到另一个选项卡或另一台机器上同一小提琴中的同一位置.
  3. 按 ENTER,然后复制您得到的答案并将其粘贴回第一个小提琴.
  4. 您现在连接了两个数据通道:一个用于聊天,另一个用于发送信号.
  5. 现在在任一端按 addTrack,视频应显示在另一端.
  6. 在另一个方向按 addTrack,您应该可以双向播放视频.
  1. There is no server (since it's a fiddle), so press the Offer button and copy the offer.
  2. Paste the offer to the same spot in the same fiddle in another tab or on another machine.
  3. Press ENTER, then copy the answer you get and paste it back in the first fiddle.
  4. You are now connected with two data-channels: one for chat and another for signaling.
  5. Now press addTrack on either end and video should show up on the other end.
  6. Press addTrack in the other direction, and you should have video going both ways.

这会产生眩光吗?我敢肯定,而且可能有更好的方法来处理这个问题,但这似乎对我有用.

Can this produce glare? I'm sure, and there may be better ways of handling this, but this seems to work for me.

这篇关于如何使用提供/回答交换来自两个对等连接的流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 02:48
查看更多