WebRTC 切换摄像头

WebRTC switch camera

我目前正在为 WebRTC 多点连接工作。我想实现在通话时将摄像头从前面切换到后面的功能。 这是我用来切换相机的代码

async function changevideo() {
   
   const audioSource = audioInputSelect.value;
   const videoSource = videoSelect.options[videoSelect.selectedIndex].value;
   var tempconstraints ={
       video: {
           deviceId: videoSource ? { exact: videoSource } : undefined,
           width: { max: 320 },
           height: { max: 240 }
       },
       audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
   };
   var newstream = await navigator.mediaDevices.getUserMedia(tempconstraints);
  
   if (connections[socketId]) {
       Promise.all(connections[socketId].getSenders().map(function (sender) {
           debugger;
           return sender.replaceTrack(newstream.getTracks().find(function (track) {
               debugger;
               return track.kind === sender.track.kind;
           })).then(data =>
           {
               console.log(data);
           });;
       }));

       var track = localStream.getTracks().find(function (track) { return track.kind == videoTrack.kind });
       localStream.removeTrack(track);
       localStream.addTrack(videoTrack);

       connections[tempsocketid].onnegotiationneeded = function () {
           connections[tempsocketid].createOffer().then(function (offer) {
               return connections[tempsocketid].setLocalDescription(offer);
           }).then(function () {
               socket.emit('signal', socketId, JSON.stringify({ 'sdp': connections[tempsocketid].localDescription, 'room': roomNumber }), roomNumber);
           }).catch(e => console.log(e));
       }
   }
}

这里 connections 包含所有已连接连接类型的 RTCpeerconnection 详细信息。

socketId 是我要切换摄像头的主用户的id。因此,connections[socketId] 为我提供了具有 socketId 的用户的 RTCPeerConnection 详细信息。

newstream为切换摄像头后的码流

如果我直接将视频的 src 更新为新闻流,那么我的相机只会在我的设备上发生变化。

我已经搜索了很多,但到处都能找到使用 replaceTrack 的解决方案,但在我的情况下它并不奏效。每次我使用它时,屏幕上都没有任何反应,而且我在控制台中也没有收到任何错误。

更新

我已经使用了 onnegotiationneeded 删除和添加曲目。

tempsocketid是另一个连接用户的socketId。 所以我有 2 个用户,一个将 socketid 存储在 socketId 中,另一个将 socketid 存储在 tempsocketid 中。所以目前我正在尝试使用 socketid socketId 切换用户的相机 当调用协商时,我在另一个用户控制台中收到错误消息。

DOMException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate

您可能无法重新协商,因此更改相机的 facingMode 不会影响其他对等方,但您并没有像我看到的那样明确使用它,而是 replaceTracks。但是您仍然可能无法触发重新协商。检查导致重新协商的事项:https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack#Usage_notes

通过使用 applyConstraints 应用约束来更改 facingMode 设置可能是不使用 replaceTracks 的解决方案。

我想到了一个奇怪的想法,即自己发送 negotiationneeded 事件,但在追寻无法通过更换轨道、更换相机和其他一切来重新协商的原因后,我会尝试这个。

关于另一个原因:至于导致重新协商的原因,你的后置摄像头分辨率应该比前置摄像头高,所以看起来是一个原因。如果你先从后置摄像头开始,然后再切换到前置摄像头,那可能是没有理由的。我怀疑你 maxwidthheight 的限制。根据上面链接页面中的列表,两个相机的它们可能非常低,因此导致相同的尺寸、分辨率等。我建议删除它们。

replaceTracks return 也是一个承诺,但是调用它的地图函数没有 return 任何东西,因此 undefined。您应该在 Promise.all 的数组参数中使用 promise。我建议 return map 函数中的那些 promises

我已经解决了 socketId 的问题,我将当前用户的 socketId 发送给不同的用户。

由于替换轨道不起作用,所以我使用 removeTrackaddTrack 强制协商。

这是我的工作代码

if (connections[socketId]) {
    localStream.getVideoTracks()[0].enabled = false;
    var track = localStream.getTracks().find(function (track) { return track.kind == videoTrack.kind });
    localStream.removeTrack(track);
    localStream.addTrack(videoTrack);

    connections[tempsocketid].onnegotiationneeded = function () {
        console.log('negotiationstarted');
        connections[tempsocketid].createOffer().then(function (offer) {
            return connections[tempsocketid].setLocalDescription(offer);
        }).then(function () {
            console.log('negotiation signal sent');
            socket.emit('signal', tempsocketid, JSON.stringify({ 'sdp': connections[tempsocketid].localDescription, 'room': roomNumber }), roomNumber);
        }).catch(e => console.log(e));
    }
    localStream.getVideoTracks()[0].enabled = true;
}