代码之家  ›  专栏  ›  技术社区  ›  pinoyyid

如何在WebRTC呼叫后释放摄像头和麦克风?

  •  0
  • pinoyyid  · 技术社区  · 4 年前

    在我结束一个WebRTC呼叫后,我似乎什么也不做就会删除浏览器选项卡上的红色图标,该图标表示相机或麦克风正在使用。

    我从中迭代轨迹 videoElement.srcObject.getTracks() 打电话 track.stop() 每一个都有。然后我从DOM中删除了videoElement,但仍然有红色图标。

    0 回复  |  直到 4 年前
        1
  •  2
  •   pinoyyid    4 年前

    在我的例子中,这个问题是由于我对WebRTC和getUserMedia()的误解而导致代码中的错误造成的。我实际上调用了getUserMedia()两次,一次是本地的 <video>

    修复方法当然是只调用getuserMedia()一次,并在两个位置使用返回的流。

        2
  •  0
  •   kenji tanaka    4 年前

    (BroadcastChannel无法与堆栈溢出代码段一起使用,因此请使用代码笔提供示例代码) 在多个选项卡中打开链接,通过单击任意一侧的Cnnect按钮检查WebRTC连接,然后切换到Close按钮,因此单击Close按钮释放Cam

    https://codepen.io/gtk2k/pen/NWxzgKo?editors=1111

    // open 2 tabs this page
    
    const signalingChannel = new BroadcastChannel('signalingChannel');
    
    let pc = null;
    signalingChannel.onmessage = async evt => {
      const msg = JSON.parse(evt.data);
      if(msg.close) {
        releaseStream();
        return;
      }
      if(!pc)
        await setupPC();
      if(msg.sdp) {
        console.log(`Receive ${msg.type}`);
        await pc.setRemoteDescription(msg);
        if(msg.type === 'offer') {
          const answer = await pc.createAnswer();
          await pc.setLocalDescription(answer);
          sendSignaling(answer);
        }
      } else if(msg.candidate) {
        console.log(`Receive candidate`);
        await pc.addIceCandidate(msg);
      }
    }
    
    async function setupPC(isCaller) {
      pc = new RTCPeerConnection();
      pc.onconnectionstatechange = evt => {
        console.log(pc.connectionState);
        if(pc.connectionState === 'disconnected')
        {  
          releaseStream();
        }
      }
      pc.onicecandidate = evt => {
        if(evt.candidate)
          sendSignaling(evt.candidate);
      }
      pc.ontrack = evt => {
        vidRemote.srcObject = evt.streams[0];
      }
      const stream = await navigator.mediaDevices.getUserMedia({video:true});
      stream.getTracks().forEach(track => pc.addTrack(track, stream));
      vidLocal.srcObject = stream;
      if(isCaller) {
        const offer = await pc.createOffer();
        await pc.setLocalDescription(offer);
        sendSignaling(offer);
      }
    }
    
    (async _ => {
      const stream = await navigator.mediaDevices.getUserMedia({video:true});  
      vidLocal.srcObject = stream;
    });
    
    btnConnect.onclick = evt => {
      if(btnConnect.textContent === 'Connect') {
        btnConnect.textContent = 'Close';
        setupPC(true);
      } else {
        btnConnect.textContent = 'Connect';
        pc.close();
        pc = null;
        releaseStream();
        sendSignaling({close: true});
      }
    }
    
    function sendSignaling(data) {
      signalingChannel.postMessage(JSON.stringify(data));
    }
    
    function releaseStream() {
      [vidLocal, vidRemote].forEach(vid => {
        if(!vid.srcObject) return;
        let stream = vid.srcObject;
        vid.pause();
        vid.srcObject = null;
        stream.getTracks().forEach(track => track.stop());
        stream = null;
      });
    }
    video {
      width: 360px;
      height: 240px;
    }
    <button id="btnConnect">Connect</button>
    <div>
    <video id="vidLocal" muted autoplay></video>
    <video id="vidRemote" muted autoplay></video>
    </div>