WebRTC:切换相机

时间:2016-08-24 14:38:49

标签: webrtc

我希望能够在与WebRTC对话的过程中切换相机,而无需重新协商来电。

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

{
    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应用程序是一个离线应用程序,其中有crosswalk => webview是chrome

5 个答案:

答案 0 :(得分:5)

在撰写本文时,WebRTC specification非常有前景,但该规范的实现仍然因浏览器而异。目前Chrome的实施仍然很旧。不过感谢jib的评论和SO answer,以及对SDP (Session Description Protocol)的更多理解,我现在可以使用Chrome切换相机。

首先,对getUserMedia方法的约束是错误的,这就是我设法传递正确约束的方法:

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

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

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

这两行代码将触发onnegotiationneeded对象上的peerConnection,意味着对等方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));

我希望有一天能帮到某人。

答案 1 :(得分:1)

replaceTrack API就是为此而定义的。

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

它在本机API中已经在某种程度上可用。但它目前正在开发中,因此请自担风险。

答案 2 :(得分:0)

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

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 :(得分:0)

我们可以替换Track时不需要重新协商

以下为我工作了多个peerConnection。

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);
  });

'''

答案 4 :(得分:0)

我不知道为什么只有我有这个问题,但如果其他人因为您的相机忙(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()) : '';

远程对等点和您的本地流可能很忙。