首页 > 解决方案 > WebRTC over internet/heroku 不工作(视频、音频流)

问题描述

我基本上正在制作一个 WebRTC 视频聊天应用程序。您可以创建一个房间,用户可以使用提供的 6 位数代码加入。在房间里,你有视频聊天、共享记事本、聊天、屏幕共享和其他微小的细节。该应用程序在本地运行良好(当我打开 2 个浏览器选项卡并通过它们进行通信时),但在我完成 MVP 并将其部署到 heroku 后,我发现没有人可以连接到我。发生的情况是客人加入房间,他的本地视频和我的(主机)正在工作,但我们看不到对方,主机收到客人离开对话的警报(客人离开处理程序,当冰候选状态时调用已更改)。我试过实现一个 TURN 服务器,但没有帮助。这是用户加入时调用的代码片段。(我正在从 mongoDB 数据库中获取 ice 候选者):

const iceConfiguration = { }
iceConfiguration.iceServers = [];
//turn server
iceConfiguration.iceServers.push({
    url: 'turn:numb.viagenie.ca',
    credential: 'muazkh',
    username: 'webrtc@live.com'
},)
//stun  server
iceConfiguration.iceServers.push({
                urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'],
            })    
let peerConnection = new RTCPeerConnection(iceConfiguration.iceServers)
async function start(){
  localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
  remoteStream = new MediaStream()
  localStream.getTracks().forEach((track) => {
    peerConnection.addTrack(track, localStream);
  });

  peerConnection.ontrack = (event) => {
    console.log(event.streams[0])
    event.streams[0].getTracks().forEach((track) => {
      remoteStream.addTrack(track);
    });
  };

  webcamVideo.srcObject = localStream;
  remoteVideo.srcObject = remoteStream;
  webcamVideo.muted = true

  peerConnection.oniceconnectionstatechange = async function() {
    if(peerConnection.iceConnectionState == 'disconnected') {
        if (username == host){
          alert("guest left")
          await fetch(`/session/${interviewId}/removeGuest`, {method: "PUT"})
          peerConnection.close()
          peerConnection = new RTCPeerConnection(iceConfiguration.iceServers)
          start()
        }else{
          handleHostDisconnect()
        }
    }
  }
  if (username === host){
    peerConnection.onicecandidate = () =>  {
      console.log("New ice candidate")
    }

    const sendChannel = peerConnection.createDataChannel("sendChannel");

    sendChannel.onopen = () => canSendMessages = true;
    sendChannel.onclose = () => console.log("closed!!!!!!");

    const offerDescription = await peerConnection.createOffer();
    await peerConnection.setLocalDescription(offerDescription);
    const body = JSON.stringify({
      hostOffer: JSON.stringify(peerConnection.localDescription.toJSON())
    })
    fetch(`/session/${interviewId}/hostOffer`, {
      method: "PUT",
      headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        body
    })
    const controller = new AbortController()
    const interval = setInterval(async ()=>{
      const res = await fetch(`/session/${interviewId}/get`, {
        "signal": controller.signal
      })
      const data = await res.json()
      if (data.guestAnswer){
        controller.abort()
        clearInterval(interval)
        await peerConnection.setRemoteDescription(JSON.parse(data.guestAnswer))
      }
    }, 2500)
  }
//GUEST JOINING
  else{
    peerConnection.onicecandidate = async () =>{
        console.log("JSON.stringify(peerConnection.localDescription)")
        const body = JSON.stringify({
          guestAnswer: JSON.stringify(peerConnection.localDescription)
        })
        await fetch(`/session/${interviewId}/guestAnswer`, {
          method: "PUT",
          headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body
        })
    }

    peerConnection.ondatachannel = (ev) =>{
        const receiveChannel = ev.channel
        receiveChannel.onopen = () => canSendMessages = true;
        receiveChannel.onclose = () => console.log("closed!!!!!!");
        peerConnection.channel = receiveChannel;

    }
    const res = await fetch(`/session/${interviewId}/get`)
    const interview = await res.json()
    await peerConnection.setRemoteDescription(JSON.parse(interview.hostOffer))

    const localDesc = await peerConnection.createAnswer()
    await peerConnection.setLocalDescription(localDesc)
    otherNameLabel.textContent = interview.host
    otherUser = interview.host 
  }

}

抱歉,如果代码有点混乱,我尝试删除尽可能多的不相关行。不要被“面试”标签所迷惑,它是一个会话,因为这个网络应用程序旨在简化编码面试。堆栈:Node.js、MongoDB、Express、hbs

标签: javascriptherokuwebrtcvideo-streaming

解决方案


您使用的 TURN 凭据无效。这可以使用涓流冰测试页面来确认。如果证书有效,您将获得一名接力候选人。

您可以运行自己的 TURN 服务器,也可以付费购买托管服务器。

如果您从双方发布候选人,您将获得更高的连接成功率。现在你只有一个onicecandidate时间if (username !== host)


推荐阅读