【问题标题】:WebRTC: Switch cameraWebRTC:切换摄像头
【发布时间】:2016-12-31 18:26:18
【问题描述】:

我希望能够在与 WebRTC 通话的过程中切换摄像头,而无需重新协商通话。

假设我们有 2 个使用 MediaStreamTrack.getSources 方法的“mediaSources”对象:

{
    id: "id_source_1" | "id_source_2",
    facing: "user" | "environment",
    kind: "kind_1" | "kind_2",
    label: "label_1" | "label_2"
 }

我们以“source_1”开始调用(面向“用户”),我们希望用户能够切换到“source_2”(面向“环境”)。

在我当前的代码中,当用户单击“切换相机”按钮时,会执行以下操作:(callingSession 是当前的 WebRTC 会话)

var mediaParams = {
       audio: true,
       video: { deviceId : source_2.id},
       options: {
           muted: true,
           mirror: true
       },
       elemId: 'localVideo'
};

callingSession.getUserMedia(mediaParams, function (error, stream) {
if (error) {
   console.error('error getting user media');
} else {
          var oldVideoTracks = callingSession.localStream.getVideoTracks();      
          var newVideoTracks = stream.getVideoTracks();

          if (oldVideoTracks.length > 0 && newVideoTracks.length > 0) {
               callingSession.localStream.removeTrack(oldVideoTracks[0]);
               callingSession.localStream.addTrack(newVideoTracks[0]);
          } 
       }
 });

正如您所见,mediaParams 约束现在设置为“source_2”,我们将带有新约束的 mediaParams 传递给 getUserMedia 方法。然后我们从旧流和新流中获取视频轨道。

这段代码的主要问题是旧流仍然与新流完全相同,即使将新约束传递给getUserMedia 方法,所以显然是相同的视频轨道,当然什么也没有发生,而且摄像头没有切换!!!

我在这段代码中做错了吗?有没有什么方法可以在不重新协商 WebRTC 调用的情况下切换相机?实验方法applyConstraint()chrome怎么看不到?

谢谢。

更新 我的 WebRTC 应用程序是带有人行横道的离子应用程序 => webview 是 chrome

【问题讨论】:

  • 我会先隔离并确保您可以在本地同时查看两个摄像头(例如,许多手机不允许同时使用前置和后置摄像头),然后再搞乱对等连接。此外,使用video: {deviceId: {exact: source_2.id}} 强制约束。最后,MediaStreamTrack.getSources 已被navigator.mediaDevices.enumerateDevices 取代,所以试试吧。如果一切正常,请在弄乱之前尝试从对等连接中删除流,然后重新添加。
  • @jib 使用当前的实现,我可以在本地切换源(也适用于移动设备)。我无法切换发送到对等方的流。
  • 我不是 Chrome 专家(这在支持 replaceTrack 的 Firefox 中不是问题),但在 Chrome 中我认为你可能仍然需要 renegotiate,你可以重新做一个数据通道,所以它几乎是无缝的。
  • @jib 感谢提供非常有用的链接。我正在尝试使用数据通道重新协商。我可以用当前的 peerConnection 创建一个 offer,然后设置本地描述。仍然当我创建数据通道peerConnection.createDataChannel 时,它的状态永远不会打开。打开数据通道有什么额外的步骤吗?
  • pc.createDataChannelpc.addStream 类似,因为您在 创建报价之前调用它。 Sample.

标签: webrtc


【解决方案1】:

在撰写本文时,WebRTC specification 非常有前途,但该规范的实现仍因浏览器而异。目前,Chrome 实现仍然很旧。尽管如此,多亏了 jib cmets 和 SO answer 以及对 SDP (Session Description Protocol) 的更多了解,我现在可以使用 Chrome 切换相机了。

首先我的getUserMedia 方法的约束是错误的,下面是我如何设法通过正确的约束:

var mediaParams = {
            // the other constraints
            video: {mandatory: {sourceId: source_2.id}}
            // ...
        };

在使用mediaParams 参数调用getUserMedia 后,我们需要从对等连接中删除当前流,然后像这样添加新流:

peerConnection.removeStream(peerConnection.getLocalStreams()[0]);
peerConnection.addLocalStream(stream);

这两行代码将触发peerConnection 对象上的onnegotiationneeded,这意味着对等体 1 必须告诉对等体 2 他更改了流,因此他需要新的描述。这就是为什么我们需要创建一个报价,设置新的描述并将这个新的描述发送给对等点:

peerConnection.createOffer()
.then(function (offer) {
      peerConnection.setLocalDescription(offer);
})
.then(function () {
      send(JSON.stringify({ "sdp": peerConnection.localDescription }));
});

此时,您要如何send SDP 由您决定。 (在我的用例中,我必须使用 WebSockets 发送它们。)

一旦其他对等方收到新的 SDP,他必须在自己的对等连接中设置它:

var obj = JSON.parse(answer).sdp;
peerConnection.setRemoteDescription(new RTCSessionDescription(obj));

我希望有一天这会对某人有所帮助。

【讨论】:

  • 谢谢你!我在这上面浪费了 12 个小时,而你的回答让我很开心。但是在切换相机面时我面临一个问题。如果我切换相机朝向 3-4 次,我的视频通话就会中断。问题出在 oneconnectionstatechanged 中。
  • @hamzox 我不记得确切的细节,但由于某种原因,onnegotiationneeded 被触发了两次。我刚刚创建了一个布尔值,我在onnegotiationneeded 回调中切换布尔值的状态,如果布尔值是true,那么我执行peerConnection.createOffer,这确保createOffer 被执行1 出2。这确实是一个 hack,但我不想在这上面浪费更多时间;)希望这会有所帮助
  • 嗨@dafriskymonkey,在你的帮助下,相机正在切换,但流改变部分没有发生。请您帮我提供更详细的对等连接代码。纠结这个很久了
【解决方案2】:

为了替换当前的相机,不丢失候选进程必须使用下面的示例代码,显然替换了你自己的变量:

navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream){
    localVideo.srcObject = stream;
    stream.getVideoTracks().forEach(function(track) {
        var sender = peerConnCallee.getSenders().find(function(s) {
          return s.track.kind == track.kind;
        });
        sender.replaceTrack(track);
    });
})
.catch(function(e) { });

您可以在下一个站点中看到它的工作原理: Can Peek

【讨论】:

    【解决方案3】:

    replaceTrack API 就是为此而定义的。

    最终,chrome 将支持 RTCRtpSender.replaceTrack 方法 (http://w3c.github.io/webrtc-pc/#rtcrtpsender-interface),该方法可用于替换轨道而无需重新协商。您可以在此处跟踪 Chrome 中该功能的开发:https://www.chromestatus.com/feature/5347809238712320

    在某种程度上它已经在原生 API 中可用。但它目前正在开发中,因此使用风险自负。

    【讨论】:

    • 我不知道如何使用replaceTrack ,因为getUserMedia 中的streamcallingSession.localStream 完全相同
    【解决方案4】:

    何时可以更换Track,无需重新协商

    以下为我工作了多个 peerConnections。

    https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack

    '''

    navigator.mediaDevices
      .getUserMedia({
        video: {
          deviceId: {
            exact: window.selectedCamera
          }
        }
      })
      .then(function(stream) {
        let videoTrack = stream.getVideoTracks()[0];
        PCs.forEach(function(pc) {
          var sender = pc.getSenders().find(function(s) {
            return s.track.kind == videoTrack.kind;
          });
          console.log('found sender:', sender);
          sender.replaceTrack(videoTrack);
        });
      })
      .catch(function(err) {
        console.error('Error happens:', err);
      });
    

    '''

    【讨论】:

      【解决方案5】:

      我不知道为什么只有我有这个问题,但是如果其他人因为您的相机忙(notReadableError: 无法启动视频源)而无法调用 navigator.mediaDevices.getUserMedia(),请尝试在此之前添加调用 getUserMedia:

                  for(let p in pc){
                      let pName = pc[p];
                      pc[pName] && pc[pName].getSenders().forEach(s => s.track && s.track.stop());
                  }
                  myStream.getTracks().length ? myStream.getTracks().forEach(track => track.stop()) : '';
      

      远程对等方以及您的本地流可能很忙。

      【讨论】:

        猜你喜欢
        • 2020-11-14
        • 2020-10-04
        • 2018-09-04
        • 1970-01-01
        • 1970-01-01
        • 2018-07-11
        • 2020-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多