Удаленное видео не отображается на одном конце - приложение для видеочата WebRT C - PullRequest
0 голосов
/ 25 февраля 2020

Я новичок в webrt c и реагирую. Я разрабатываю одноранговое приложение для видеочата. На вызывающей стороне отображаются как удаленное, так и локальное видео. Но на стороне вызываемого абонента показывается только местное видео. Я пытался выяснить, где я делаю неправильно, но не смог понять это. Одна вещь, которую я заметил, когда я установил переменную peerconnection console.log внутри функции-кандидата дескриптора, это то, что «connectionState» все еще «соединяется» на стороне вызываемого абонента («подключено» на стороне вызывающей стороны).

РЕДАКТИРОВАТЬ: я изменил код для удобства чтения и использования asyn c await. Теперь я получаю сообщение «Не удалось выполнить addIceCandidate» при ошибке «RTCPeerConnection»: Ошибка обработки кандидата 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);
      }
    }
  }

I Я не помещал свой серверный код, потому что я не нахожу в нем проблем.

Насколько я понимаю, ошибка в том, что удаленное описание не устанавливается при вызове addicecandidate. Может быть потому, что я пропускаю настройку удаленного описания, когда signalingState стабильно. Но если я удаляю эту строку кода, я получаю еще одну ошибку - «Не удалось установить удаленный ответ sdp: Вызывается в неправильном состоянии: kStable»

Куда я иду не так?

1 Ответ

0 голосов
/ 25 февраля 2020

Сразу после вызова pc.setLocalDescription(), PeerConnection начнет излучать события onicecandidate, благодаря Trickle ICE. Однако это означает, что, возможно, первые кандидаты генерируются слишком быстро, и они отправляются удаленному узлу даже перед отправкой предложения / ответа SDP!

Может быть, это то, что происходит в вашем случае, и первые кандидаты тоже приходят рано с другой стороны. По этой причине рекомендуется проверить состояние сигнализации PeerConnection: если оно стабильно и удаленное описание уже задано, вы можете вызвать pc.addIceCandidate(). Если нет, вы сохраняете кандидата в очереди.

Позже, когда наконец появится удаленное описание, после его установки вы вручную добавляете всех кандидатов, ожидающих в очереди.

Здесь вы можете увидеть код с этой идеей. Сначала кандидаты ставятся в очередь, а затем, когда состояние сигнализации PeerConnection становится stable, элементы в очереди добавляются .

...