2

I'm creating simple video-chat one-to-one using PeerJS and React. Everything is working fine except the camera muting. When participant A muting camera, it's muting on both clients but when participant A unmutes it, participant B can see only the static image instead of the opponent video.

I have a global state webcamState which changed when the corresponding button was clicked:

  const videoRef = useRef<any>(null);
  const webcamState = useSelector(getWebcamState);

  const [stream, setStream] = useState<MediaStream | null>(null);
  const [isWebcamLoading, setIsWebcamLoading] = useState(true);

  const loadUserMedia = () => {
    setIsWebcamLoading(true);
    getUserMedia(
      { video: webcamState, audio: micState },
      (newStream: MediaStream) => {
        videoRef.current.srcObject = newStream;
        setStream(newStream);
        setIsWebcamLoading(false);
      },
      (err: any) => {
        setIsWebcamLoading(false);
      },
    );
  };

useEffect(() => {
    if (videoRef?.current?.srcObject) {
      videoRef.current.srcObject.getVideoTracks().forEach((track: any) => (track.enabled = webcamState));

      if (!webcamState) {
        videoRef.current.srcObject.getVideoTracks().forEach((track: any) => track.stop());
        videoRef.current.pause();
      } else {
        loadUserMedia();
      }
    }
  }, [webcamState]);

Then this stream exporting from this hook and passed into another to initialize Peer call (and peer answer as well):

export interface IPeerOptions {
  opponentVideoRef: React.MutableRefObject<null>;
  currentVideoRef: React.MutableRefObject<null>;
  currentStream: MediaStream;
  userId: string;
}

export const initializePeerCall = (options: IPeerOptions): Peer => {
  const call = options.peer.call(options.opponentId, options.currentStream);
  call.on('stream', stream => {
    options.opponentVideoRef = setVideo(options.opponentVideoRef, stream);
  });

  call.on('error', err => {
    console.log(err);
  });
  call.on('close', () => {
    console.log('Connection closed');
  });
  return options.peer;
};

No errors appear in the console

But if I will remove the following line: videoRef.current.srcObject.getVideoTracks().forEach((track: any) => track.stop()); everything will work fine as expected

Maybe anyone faced something similar?

UPD: I tried this but the result was the same:

useEffect(() => {
    if (videoRef?.current?.srcObject) {
      videoRef.current.srcObject.getVideoTracks().forEach((track: any) => (track.enabled = webcamState));

      if (!webcamState) {
        videoRef.current.srcObject.getVideoTracks().forEach((track: any) => track.stop());
        loadUserMedia();
      } else {
        loadUserMedia();
      }
    }
  }, [webcamState]);

Notification:

 const onOpponentVideoStatusChanged = (newStatus: boolean) => {
    setOpponentState(prev => {
      return { microphoneState: !!prev?.microphoneState, webcamState: newStatus };
    });
    options.opponentVideoRef.current.srcObject.getVideoTracks().forEach((track: any) => (track.enabled = newStatus));
  };

As I understand after long investigation, user B still getting the same stream after user A created a new one. How can I fix it?

Lev Kostychenko
  • 147
  • 1
  • 10

1 Answers1

0

If you turn track off using track.stop(), you can not resume it.

I did get new stream when I have to resume it.

React.useEffect(()=>{
  if(mediaStream) { // mediaStream could be state locally or globally.
     const videoTrack = mediaStream.getVideoTracks();
      videoTrack.forEach((t) => {
        t.enabled = false;
        t.stop();
      });
  }
  // get MediaStream again with contraints 

}, [isAudioOnly]);
kyun
  • 9,710
  • 9
  • 31
  • 66