【问题标题】:WebRTC onicecandidate: Am getting ICE candidates with sdpMid=audio only but not for videoWebRTC onicecandidate:我正在使用 sdpMid=audio 获得 ICE 候选人,但不适用于视频
【发布时间】:2016-08-18 18:08:36
【问题描述】:

使用的浏览器是 Chrome...我有调用者和接收者代码来生成 SDP 和 ICE 候选者。 我得到调用者代码来生成正确的 SDP 和 ICE 候选者与 sdpMid=video 但对于接收者,我得到的 ICE 候选者只为 sdpMid=audio 生成。

UPDATE这里是修改后接收方的 localSessionDescription SDP,建议如下:

 v=0
 o=- 7912682607537349212 2 IN IP4 127.0.0.1
 s=-
 t=0 0
 a=group:BUNDLE audio video
 a=msid-semantic: WMS 9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd
 m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126
 c=IN IP4 0.0.0.0
 a=rtcp:9 IN IP4 0.0.0.0
 a=ice-ufrag:0D1hLEwxnqReQosQ
 a=ice-pwd:Nsc4EAtefrfgzTetHjJA5lsg
 a=fingerprint:sha-256 6C:85:D8:33:D8:C6:CB:CE:D4:8E:B4:7A:C2:F5:2F:D0:67:04:25:B2:74:F9:C6:3A:2E:96:E6:56:E7:27:B0:F8
 a=setup:active
 a=mid:audio
 a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
 a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
 a=sendrecv
 a=rtcp-mux
 a=rtpmap:111 opus/48000/2
 a=fmtp:111 minptime=10; useinbandfec=1
 a=rtpmap:103 ISAC/16000
 a=rtpmap:104 ISAC/32000
 a=rtpmap:9 G722/8000
 a=rtpmap:0 PCMU/8000
 a=rtpmap:8 PCMA/8000
 a=rtpmap:106 CN/32000
 a=rtpmap:105 CN/16000
 a=rtpmap:13 CN/8000
 a=rtpmap:126 telephone-event/8000
 a=maxptime:60
 a=ssrc:2958641119 cname:Iu8s16HLxglPDg9k
 a=ssrc:2958641119 msid:9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd bb63739b-cca2-4aa5-90a6-cf4bbaa199af
 a=ssrc:2958641119 mslabel:9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd
 a=ssrc:2958641119 label:bb63739b-cca2-4aa5-90a6-cf4bbaa199af
 m=video 9 UDP/TLS/RTP/SAVPF 100 101 116 117 96
 c=IN IP4 0.0.0.0
 a=rtcp:9 IN IP4 0.0.0.0
 a=ice-ufrag:0D1hLEwxnqReQosQ
 a=ice-pwd:Nsc4EAtefrfgzTetHjJA5lsg
 a=fingerprint:sha-256 6C:85:D8:33:D8:C6:CB:CE:D4:8E:B4:7A:C2:F5:2F:D0:67:04:25:B2:74:F9:C6:3A:2E:96:E6:56:E7:27:B0:F8
 a=setup:active
 a=mid:video
 a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
 a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
 a=extmap:4 urn:3gpp:video-orientation
 a=sendrecv
 a=rtcp-mux
 a=rtpmap:100 VP8/90000
 a=rtcp-fb:100 ccm fir
 a=rtcp-fb:100 nack
 a=rtcp-fb:100 nack pli
 a=rtcp-fb:100 goog-remb
 a=rtcp-fb:100 transport-cc
 a=rtpmap:101 VP9/90000
 a=rtcp-fb:101 ccm fir
 a=rtcp-fb:101 nack
 a=rtcp-fb:101 nack pli
 a=rtcp-fb:101 goog-remb
 a=rtcp-fb:101 transport-cc
 a=rtpmap:116 red/90000
 a=rtpmap:117 ulpfec/90000
 a=rtpmap:96 rtx/90000
 a=fmtp:96 apt=100
 a=ssrc-group:FID 3143004909 4248148453
 a=ssrc:3143004909 cname:Iu8s16HLxglPDg9k
 a=ssrc:3143004909 msid:9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd 778ef702-e7fc-47ea-bb3a-477e0b4262ba
 a=ssrc:3143004909 mslabel:9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd
 a=ssrc:3143004909 label:778ef702-e7fc-47ea-bb3a-477e0b4262ba
 a=ssrc:4248148453 cname:Iu8s16HLxglPDg9k
 a=ssrc:4248148453 msid:9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd 778ef702-e7fc-47ea-bb3a-477e0b4262ba
 a=ssrc:4248148453 mslabel:9f0MAtEwYGWY3pdBDI8ZtTu4dVu92R6IpEFd
 a=ssrc:4248148453 label:778ef702-e7fc-47ea-bb3a-477e0b4262ba

这是为相应的 getUserMedia 生成的,如下所示:

 navigator.getUserMedia({ audio: true, video: { width: 1280, height: 720 } },...

ICE 候选生成代码为:

pc.onicecandidate = function (event) {
   console.log("Generated Icecandidate:" );
   console.log(event);
   ...
 };

在 console.log 上,我看到了 ICE 候选者,如下所示:

RTCIceCandidate
candidate: "candidate:211156821 1 udp 2122260223 192.168.1.5 41811 typ host generation 0 ufrag kV5Snl0LQhJlYujt"
sdpMLineIndex:0
sdpMid:"audio"

不用说,我无法显示远程视频。我正在本地网络上尝试这个,所以真的不需要 STUN。

我想知道,为什么我没有获得 sdpMid=video 的任何 ICE 候选人。此外,在生成的四个 ICE 候选中,三个 ICE 候选的 sdpMLineIndex:0 和一个 ICE 候选的候选属性为空!

更新 1:我得到了上一期的答案...候选属性为 Null。 “注意:RTCPeerConnection.onicecandidate 将使用空的候选属性调用一次,以表示涓流 ICE 事件的结束。”这是解释here

在呼叫方方面,我收到了 10 多个 ICE 候选人,其中一些有音频,一些有视频。

我哪里错了?

更新 2: 这是接收器部分的代码,它不会为视频生成 ICE 候选者。我已经剥离了身份验证和其他部分,只关注相关部分。我删除了 ICE 候选者的缓存并按原样发送:

$(document).ready(function () {

  var socket = io.connect();
  var pc = new RTCPeerConnection ({
    "iceServers": [{"url": "stun:stun.l.google.com:19302"}]
  });

  pc.onicecandidate = function (event) {
    socket.emit('candidateFromReceiver',event.candidate);
    console.log("Candidate Generated:");
    console.log(event.candidate);
  }; 

  pc.onaddstream = function(ev) {
    stream = ev.stream;        
    var video = $('#vid2'); 
    video.attr('src', URL.createObjectURL(stream));
    video.onloadedmetadata = function(e) {
      video.play();
    }
  };

  socket.on('connect',function() { console.log("Socket connected"); });
  socket.on('candidateFromCaller', function (data) {
      pc.addIceCandidate(new RTCIceCandidate(data));
  });

  navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
                       navigator.mozGetUserMedia;
  if (navigator.getUserMedia) {
    navigator.getUserMedia({ audio: true, video: { width: 1280, height: 720 } },
      function(stream) {
         var video = $('#vid1'); 
         video.attr('src', URL.createObjectURL(stream));
         video.onloadedmetadata = function(e) {
           video.play();
         }
     pc.addStream(stream);
      },error);

    socket.on('sdpOffer', function(data) {
      var sdpOffer = new RTCSessionDescription(data.sdpOffer);
      pc.setRemoteDescription(sdpOffer, function() {
        pc.createAnswer(function(sdpAnswer) {
          localSessionDescription = new RTCSessionDescription(sdpAnswer);
          pc.setLocalDescription(localSessionDescription, function() {
            socket.emit('sdpAnswer',localSessionDescription);
          },error);
        }, error);
      },error);
    });
  }

  function error(err) {
    console.log("ERROR!!!!");
    console.log(err);
  }

}); // End of document.ready function

如果我在获取用户媒体之后插入代码以生成报价(就像我在调用者代码中所做的那样),生成的 ICE 候选也包括视频。当然,这只是为了测试,因为在那之后的其余代码炸弹,正如预期的那样。

【问题讨论】:

  • 很难说有什么问题,因为你还没有发布任何代码来查看。您发布的 sdp(答案)似乎对音频和视频都有 m= 行,所以这看起来很好。您是否尝试过切换呼叫者和接收者?
  • @jib 切换呼叫者和接收者机器没有帮助。代码太长,无法发送。摘要:两个 SDP 都有 m=audio 和 m=video 线。在 CALLER&RECEIVER 中,我将 ICE-candidates 存储在一个数组中。此代码刚刚创建 PeerConnection 之后。在 CALLER 中,收到 answer-SDP 后,我设置 remoteDescription 并将数组发送到接收器。在 RECEIVER 中,收到 offer-SDP 后,我设置 remoteDescription 并将数组发送给调用者。我怀疑这个序列有问题。我正在阅读 RFC 5245。如果您能提供一些指示,那将有所帮助。Thnx
  • 整个数组的东西似乎很可疑。你为什么这样做?涓流 ICE 的全部意义在于涓涓细流,即一旦候选人可用,就立即发送。不要缓存它们。在呼叫方方面,等到您得到答复只会浪费大量时间。相反,在接收方,我怀疑数组是空的,因为你太早了。 ICE 收集在 setLocalDescription 之后开始,在接收方发生在 setRemoteDescription 之后。
  • 这不是纯 JavaScript。是jQuery吗?对video.attr('src', URL.createObjectURL(stream));不熟悉,你试过video.src = URL.createObjectURL(stream);吗?另外,这是本地视频...远程视频设置在哪里?您在远程收听音频吗?
  • @jib 我认为 Jquery 部分无关紧要,只在设置 HTML 元素的属性时发挥作用,它工作正常。还有其他未包含的代码依赖于 JQuery。我认为本地或远程流如何在本地显示与查看 ICE 候选人无关?或者是吗?无论如何,在呼叫者和接收者中,我都可以看到 div 中显示的本地视频(本​​地)。我听到从呼叫者到接收者的音频,但不是相反。我没有设置远程视频,因为我还没有看到 ICE 候选人。你的每一条评论都非常有用。

标签: javascript webrtc


【解决方案1】:

(从 cmets 看来,您正在缓存 ICE 候选人。不要那样做。我还怀疑时间问题可能是失去一些候选人的原因。)

Trickle ICE 的全部意义在于对候选人进行涓流,即候选人一可用就发送。

使用 WebRTC,您的应用负责在对等点之间发送信号,这是时间敏感的。所以:

  1. setLocalDescription 成功回调之前发送pc.localDescription
  2. 期望pc.onicecandidate 在回调后立即开始触发。发送给他们。

这在双方都是正确的(提供和回答)。你想在电线上看到的是:

offer, candidate, candidate, candidate

反之:

answer, candidate, candidate, candidate

不该做什么:

  • 不要缓存 ICE 候选对象。
  • 不要等到得到答复,那只会浪费时间。
  • 无论出于何种原因在接收端收到报价时,请不要延迟致电setRemoteDescription,否则将无法接收候选人。

更新 2:

您的 sdp 显示 a=recvonly 而不是 a=sendrecv,这意味着接收者只接受接收,不发送任何回报。两种情况之一可能会导致此问题:

  1. 调用方设置 createOffer 选项,例如 offerToReceiveVideo:false 和/或 offerToReceiveAudio:false
  2. 接收方未及时致电pc.addStream(之前)pc.setLocalDescription

如果getUserMedia 和收到报价之间存在竞争,则可能发生第二次。

更新 3:

如果所有其他方法都失败,请与工作代码进行比较。我之前在其他答案中分享过一个交叉表演示,但它只发送了视频,没有收到任何内容。

这里是a modified version of that demo,它只接收来自远程摄像头的视频。像往常一样,在同一个浏览器的两个选项卡中打开它。

请注意,在 Firefox 中,在您点击 Call 后,您必须在物理上聚焦另一个选项卡,然后它才能访问相机。

【讨论】:

  • 我遵循了您的指示,但仍然没有运气...提供了剥离代码...我只是想看看合适的 ICE 候选人。此代码仅用于解决该有限问题。多谢。您的 cmets 帮了大忙。看到远程视频显示会很令人兴奋...希望尽快到达那里!
  • 我将您的答案标记为已接受。我已更新代码以显示来自呼叫者的视频。正如预期的那样,来自接收方的视频仍然没有在呼叫方看到,因为接收方的本地 ICE 候选不包括视频。您的任何指点将不胜感激。
  • 抱歉,我错过了您之前的更新 2。我昨天根据您的建议做了一些更改,现在为接收器的音频和视频设置了 a=sendrecv。更改后我昨天没有检查这部分。阅读您的更新 2 后现在检查它们。我认为昨天问题的原因是您的更新 2 的第 2 点。
  • 我已为接收器添加了最新的 SDP。如果你有时间看看……我就到了。你帮了大忙。
  • @Sam 用sendrecv 显示的音频和视频 m 线的最新答案,我希望在您 setLocalDescription(answer) 之后立即为音频和视频生成 ICE 候选者。我没看出有什么问题,抱歉。 - 唯一突出的是您的pc.onicecandidate,它通常与侧面无关(两端的代码相同),但您将其连接为仅以一种方式工作('candidateFromReceiver'),希望这只是减少这篇文章的例子。
【解决方案2】:

您正在处理的内容称为捆绑。提供者和回答者同意将所有 ICE 传输捆绑到单个 ICE 传输中。因此,对于第一个 m 部分,您只会得到一个 ICE 候选人。

提供者仍在为您提供视频 m 部分的所有这些 ICE 候选者,因为它不知道回答者是否会同意使用捆绑包。

正如 AlexD 在他的回答中指出的那样,您可以通过“bundlePolicy”在提供方方面影响这种行为。例如,“maxBundle”作为策略将导致提供者假设应答者将理解捆绑,因此只为单个传输创建 ICE 候选者。

但只要提供者提供捆绑包,回答者就会使用它(如果它支持的话)。

【讨论】:

    【解决方案3】:

    我可以通过设置解决这个问题:

    rtcConfiguration.bundlePolicy = "max-compat"
    

    见:http://w3c.github.io/webrtc-pc/#dom-rtcbundlepolicy-max-compat

    【讨论】:

      猜你喜欢
      • 2023-02-21
      • 1970-01-01
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-08
      • 2023-03-20
      • 2013-06-27
      相关资源
      最近更新 更多