繁体   English   中英

远程视频未在一端显示 - WebRTC 视频聊天应用

[英]Remote video not showing up on one end - WebRTC Video chat app

我是 webrtc 的新手并做出反应。 我正在开发一个点对点视频聊天应用程序。 在呼叫端,远程视频和本地视频都会出现。 但在被叫方,只显示本地视频。 我一直试图找出我做错的地方,但无法弄清楚。 当我在处理 ice 候选函数中的 console.log peerconnection 变量时我注意到的一件事是,'connectionState' 在被调用方仍然是 'connecting'。(在调用方是'connected')。

编辑:我修改了代码以提高可读性并使用异步等待。 现在我收到“无法在‘RTCPeerConnection’上执行‘addIceCandidate’:错误处理 ICE 候选”错误。

编辑 2:根据答案修改代码

这是我的原始代码

//refs for my video tag
const localVideoRef = useRef("");
const remoteVideoRef = useRef("");

//video elements
<video ref={remoteVideoRef} playsInline autoPlay className="remoteVideo"></video>
<video ref={localVideoRef} playsInline autoPlay muted className="localVideo"></video>

//button to start call
<button onClick={handleCall}>Call</button>

const handleCall = async () => {

    createPeerConnection();

    navigator.mediaDevices
      .getUserMedia({
        audio: true,
        video: true
      })
      .then(function(localStream) {
        let videoObj = localVideoRef.current;
        videoObj.srcObject = localStream;

        localStream
          .getTracks()
          .forEach(track => myPeerConnection.addTrack(track, localStream));
      })
      .catch("getUserMedia() error: ");
};


let myPeerConnection = null;

  function createPeerConnection() {
    myPeerConnection = new RTCPeerConnection({
      iceServers: [
        {
          urls: "stun:stun2.1.google.com:19302"
        }
      ]
    });

    myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
    myPeerConnection.onicecandidate = handleICECandidateEvent;
    myPeerConnection.ontrack = handleTrackEvent;
    myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
}

 var isNegotiating = false;

  const handleSignalingStateChangeEvent = () => {
    isNegotiating = myPeerConnection.signalingState != "stable";
  };



function handleNegotiationNeededEvent() {

    if (isNegotiating) {
      return;
    }

    isNegotiating = true;

    myPeerConnection
      .createOffer()
      .then(function(offer) {
        return myPeerConnection.setLocalDescription(offer);
      })
      .then(function() {
        socket.emit("video-offer", {
          from: authContext.user.name,
          to: connectedTo,
          sdp: myPeerConnection.localDescription
        });
      });  
}

//checking if socket is initialized
if (socket) {
    socket.on("gotOffer", data => {
      handleVideoOfferMsg(data);
    });

    socket.on("gotCandidate", data => {
      handleNewICECandidateMsg(data);
    });

    socket.on("gotAnswer", data => {
      console.log("inside got answer");
      handleGotAnswer(data);
    });
  }

function handleVideoOfferMsg(msg) {

    createPeerConnection();

    var desc = new RTCSessionDescription(msg.sdp);

    myPeerConnection
      .setRemoteDescription(desc)
      .then(function() {
        return navigator.mediaDevices.getUserMedia({
          audio: true,
          video: true
        });
      })
      .then(function(stream) {
        let localStream = stream;

        let videoObj = localVideoRef.current;
        videoObj.srcObject = stream;

        localStream
          .getTracks()
          .forEach(track => myPeerConnection.addTrack(track, localStream));
      })
      .then(function() {
        return myPeerConnection.createAnswer();
      })
      .then(function(answer) {
        return myPeerConnection.setLocalDescription(answer);
      })
      .then(function() {
        socket.emit("video-answer", {
          from: authContext.user.name,
          to: connectedTo,
          sdp: myPeerConnection.localDescription
        });

      })
      .catch("error");
  }

 async function handleGotAnswer(msg) {

    if (!myPeerConnection) return;

    // if (isNegotiating) return;
    //I don't know why it's not working (no remote video on the caller side too) when I add above line. So, I am checking signaling state in the below line

    if (myPeerConnection.signalingState == "stable") return;

    await myPeerConnection.setRemoteDescription(
      new RTCSessionDescription(msg.sdp)
    );

}

 function handleICECandidateEvent(event) {
    if (!myPeerConnection) return;

    if (isNegotiating) return;

    if (event.candidate) {
      socket.emit("candidate", {
        to: connectedTo,
        from: authContext.user.name,
        candidate: event.candidate
      });

    }
}

function handleNewICECandidateMsg(msg) {

    if (myPeerConnection.signalingState == "stable") return;

    var candidate = new RTCIceCandidate(msg.candidate);

    myPeerConnection.addIceCandidate(candidate).catch("error");

}

 function handleTrackEvent(event) {
    let videoObj = remoteVideoRef.current;
    videoObj.srcObject = event.streams[0];
}

这是我的新代码:

let pc1 = new RTCPeerConnection({
    iceServers: [
      {
        urls: "stun:stun2.1.google.com:19302"
      }
    ]
  });

  let pc2 = new RTCPeerConnection({
    iceServers: [
      {
        urls: "stun:stun2.1.google.com:19302"
      }
    ]
  });

  const handleCall = async () => {

    let stream = await navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true
    });

    let videoObj = localVideoRef.current;
    videoObj.srcObject = stream;

    let localStream = stream;

    stream
      .getTracks()
      .forEach(async track => await pc1.addTrack(track, localStream));

    pc1.onnegotiationneeded = async function() {
      let offer = await pc1.createOffer();
      await pc1.setLocalDescription(offer);
      socket.emit("video-offer", {
        from: authContext.user.name,
        to: connectedTo,
        sdp: pc1.localDescription
      });

      pc1.onicecandidate = function(event) {
        if (event.candidate) {
          socket.emit("candidate", {
            pc: "pc1",
            to: connectedTo,
            from: authContext.user.name,
            candidate: event.candidate
          });
        }
      };
    };

    pc1.ontrack = function(event) {
      let videoObj = remoteVideoRef.current;
      videoObj.srcObject = event.streams[0];
    };
  };

  //listening to socket emits from server related to video chat

  if (socket) {
    socket.on("gotOffer", data => {
      //step 1 of callee
      handleVideoOfferMsg(data);
    });

    socket.on("gotCandidate", data => {
      handleNewICECandidateMsg(data);
    });

    socket.on("gotAnswer", data => {
      handleGotAnswer(data);
    });

  }

  async function handleVideoOfferMsg(msg) {
    var desc = new RTCSessionDescription(msg.sdp);

    await pc2.setRemoteDescription(desc);

    let stream = await navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true
    });

    let videoObj = localVideoRef.current;
    videoObj.srcObject = stream;

    let localStream = stream;

    stream
      .getTracks()
      .forEach(async track => await pc2.addTrack(track, localStream));

    let answer = await pc2.createAnswer();

    await pc2.setLocalDescription(answer);

    socket.emit("video-answer", {
      from: authContext.user.name,
      to: connectedTo,
      sdp: pc2.localDescription
    });

    pc2.ontrack = function(event) {
      let videoObj = remoteVideoRef.current;
      videoObj.srcObject = event.streams[0];
    };

    pc2.onicecandidate = function(event) {
      if (event.candidate) {
        socket.emit("candidate", {
          pc: "pc2",
          to: connectedTo,
          from: authContext.user.name,
          candidate: event.candidate
        });
      }
    };
  }

  async function handleGotAnswer(msg) {
    if (pc1.signalingState == "stable") {
      console.log("negotiating");
      return;
    }

    await pc1.setRemoteDescription(new RTCSessionDescription(msg.sdp));

    //INSERTED THIS
    if (candidatesArray.length) {
      candidatesArray.forEach(async msg => {
        var candidate = new RTCIceCandidate(msg.candidate);
        await pc1.addIceCandidate(candidate);
      });
    }

  }

 let candidatesArray = [];


  async function handleNewICECandidateMsg(msg) {
    if (msg.pc == "pc1") {
      var candidate = new RTCIceCandidate(msg.candidate);

      await pc2.addIceCandidate(candidate);
    }

    if (msg.pc == "pc2") {
      try {

        if (pc1.connectionState != "stable" && !pc1.remoteDescription) {
          candidatesArray.push(msg);
          return;
        }
        var candidate = new RTCIceCandidate(msg.candidate);

        await pc1.addIceCandidate(candidate);
      } catch (error) {
//this is where error is triggered.
        console.log("error adding ice candidate: " + error);
      }
    }
  }

我没有把我的服务器端代码,因为我发现它没有问题。

据我了解,该错误是因为调用 addicecandidate 时未设置 remotedescription。 可能是因为我在 SignalingState 稳定时跳过了设置远程描述。 但是如果我删除那行代码,我会收到另一个错误 - “无法设置远程应答 sdp:在错误状态下调用:kStable”

我哪里错了?

在调用pc.setLocalDescription() ,PeerConnection 将立即开始发出onicecandidate事件,这要归功于 Trickle ICE。 但是,这意味着第一个候选对象可能生成得太快,甚至在发送 SDP Offer/Answer 之前它们就被发送到远程对等方!

也许这就是你的情况,第一批候选人从另一边来得太早了。 出于这个原因,检查 PeerConnection 信号状态是个好主意:如果它稳定并且已经设置了远程描述,那么您可以调用pc.addIceCandidate() 如果没有,则将候选人存储在队列中。

稍后,当远程描述最终到达时,在设置它之后,您手动添加队列中等待的所有候选者。

在这里你可以看到带有这个想法的代码。 候选者首先排队,然后当 PeerConnection 信令状态stable加入排队的项目

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM