0

I am working on peer to peer video conference application, using below code, the local Stream video is showing but the remote is not working, it is just showing black video in RTCView tag. it is like

it is like the ice server is getting reached, peerconnection. oniceconnectionstatechange is only showing checking state , am working on wireless mobile hotspot, not sure if the issue is with iceservers

export default function CallerJoinScreen({}) {
  const state = useSelector(state => state);
  const [localStream, setlocalStream] = useState(null);

  const [remoteStream, setRemoteStream] = useState(null);

  const [type, setType] = useState('JOIN');

  const [callerId] = useState(state.chat.selectedRoom);
  const [socket, setSocket] = useState();
  var mediaConstraints = {
    mandatory: {
      OfferToReceiveAudio: true,
      OfferToReceiveVideo: true,
    },
  };
  let otherUserId = {current: state.chat.selectedRoom};
  const [remoteRTCMessage, setRemoteRTCMessage] = useState({current: null});
  const localVideoRef = useRef(null);
  const remoteVideoRef = useRef(null);

  const [localMicOn, setlocalMicOn] = useState(true);

  const [localWebcamOn, setlocalWebcamOn] = useState(true);

  let isFront = false;
  const onSuccess = data => console.log('fx success data=', data);
  const onError = data => console.log('fx error data=', data);

  const configuration = {
    iceServers: [{urls: 'stun:stun.l.google.com:19302'}],
  };
  const peerConnection = useRef(new RTCPeerConnection(configuration));

  useEffect(() => {
    async function setSocketIoFx() {
      let socketVar = await socketClient();
      setSocket(socketVar);
    }

    setSocketIoFx();
    return () => console.log('CallerJoinScreen cleanup useEffect');
  }, []);
  useEffect(() => {
    if (socket) {
      socket.emit('newRoomId', state.chat.selectedRoom);
      socket.on('newCall', data => {
        //console.log('socket newCall data=', data);
        setRemoteRTCMessage({current: data.rtcMessage});
        //remoteRTCMessage.current = data.rtcMessage;
        otherUserId.current = data.callerId;
        //console.log('socket newCall data=', data);
        setType('INCOMING_CALL');
      });

      socket.on('callAnswered', data => {
        //remoteRTCMessage.current = data.rtcMessage;
        setRemoteRTCMessage({current: data.rtcMessage});
        peerConnection?.current.setRemoteDescription(
          new RTCSessionDescription(data.rtcMessage),
        );
        setType('WEBRTC_ROOM');
      });

      socket.on('ICEcandidate', data => {
        let message = data.rtcMessage;

        if (peerConnection.current) {
          peerConnection?.current
            .addIceCandidate(
              new RTCIceCandidate({
                candidate: message.candidate,
                sdpMid: message.id,
                sdpMLineIndex: message.label,
              }),
            )
            .then(data => {
              console.log('SUCCESS');
            })
            .catch(err => {
              console.log('Error', err);
            });
        }
      });

      mediaDevices.enumerateDevices().then(async sourceInfos => {
        let videoSourceId;
        for (let i = 0; i < sourceInfos.length; i++) {
          const sourceInfo = sourceInfos[i];
          if (
            sourceInfo.kind == 'videoinput' &&
            sourceInfo.facing == (isFront ? 'user' : 'environment')
          ) {
            videoSourceId = sourceInfo.deviceId;
          }
        }

        let stream = await mediaDevices.getUserMedia({
          audio: true,
          video: {
            mandatory: {
              minWidth: 500, // Provide your own width, height and frame rate here
              minHeight: 300,
              minFrameRate: 30,
            },
            facingMode: isFront ? 'user' : 'environment',
            optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
          },
        });
        //let loc = await peerConnection.current.getLocalStreams();
        console.log(
          'before stream.getTracks peerConnection.current.addStream=',
        );
        //await peerConnection.current.addStream(stream);
        // Got stream!
        console.log(
          'before setting local stream stream.getTracks()=',
          stream.getTracks(),
        );
        stream.getTracks().forEach(track => {
          peerConnection.current.addTrack(track, stream);
        });
        let remote = new MediaStream();
        setRemoteStream(remote);
        setlocalStream(stream);

        /*
            for (const track of stream.getTracks()) {
              stream.addTrack(track);
              peerConnection.current.getLocalStreams()[0].addTrack(track);
            }
            */
      });
      if (peerConnection) {
        peerConnection.current.onnegotiationneeded = async () => {
          let offer = await peerConnection.current.createOffer({
            offerToReceiveAudio: 1,
            offerToReceiveVideo: 1,
          });

          // signaling and invite
          console.log('before setting setLocalDescription offer=', offer);
          await peerConnection.current.setLocalDescription(offer);
        };
      }

      if (peerConnection.current.ontrack !== undefined) {
        peerConnection.current.ontrack = ev => {
          console.log('peerConnection.current.ontrack ev.track=', ev.track);
          const newStream = new MediaStream();
          newStream.addTrack(ev.track);
          setRemoteStream(newStream);
        };
      } else {
        peerConnection.current.onaddstream = ev => {
          setRemoteStream(ev.stream);
        };
      }

      // Setup ice handling
      peerConnection.current.onicecandidate = event => {
        console.log('onicecandidate event=', event);
        if (event.candidate) {
          sendICEcandidate({
            calleeId: otherUserId.current,
            rtcMessage: {
              label: event.candidate.sdpMLineIndex,
              id: event.candidate.sdpMid,
              candidate: event.candidate.candidate,
            },
          });
        } else {
          console.log('End of candidates.');
        }
      };
      peerConnection.current.oniceconnectionstatechange = function (newState) {
        console.log('oniceconnectionstatechange newState=', newState);
        console.log('ICE state: ', peerConnection.current.iceConnectionState);
        if (peerConnection.current.iceConnectionState === 'failed') {
          /* possibly reconfigure the connection in some way here */
          /* then request ICE restart */
          peerConnection.current.restartIce();
        }
      };

      return () => {
        socket.off('newCall');
        socket.off('callAnswered');
        socket.off('ICEcandidate');
      };
    }
  }, [socket]);

  useEffect(() => {
    InCallManager.start();
    InCallManager.setKeepScreenOn(true);
    InCallManager.setForceSpeakerphoneOn(true);

    return () => {
      InCallManager.stop();
    };
  }, []);

  function sendICEcandidate(data) {
    socket.emit('ICEcandidate', data);
  }

  async function processCall() {
    const sessionDescription = await peerConnection.current.createOffer({
      iceRestart: true,
      offerToReceiveAudio: 1,
      offerToReceiveVideo: 1,
    });
    await peerConnection.current.setLocalDescription(sessionDescription);
    sendCall({
      calleeId: otherUserId.current,
      rtcMessage: sessionDescription,
    });
  }

  async function processAccept() {
    //console.log('processAccept remoteRTCMessage', remoteRTCMessage);
    peerConnection.current
      .setRemoteDescription(new RTCSessionDescription(remoteRTCMessage.current))

      .then(async function () {
        //console.log('processAccept stream =', stream);

        return await peerConnection.current.createAnswer();
      })
      .then(function (answer) {
        //signaling to caller and send answer
        return answerCall({
          callerId: otherUserId.current,
          rtcMessage: answer,
        });
      })
      .catch(function (err) {
        //console.log(err.message);
      });

    //const sessionDescription = await peerConnection.current.createAnswer();
    //await peerConnection.current.setLocalDescription(sessionDescription);
  }

  function answerCall(data) {
    //console.log('function answerCall data=', data);
    socket.emit('answerCall', data);
    peerConnection.current.setLocalDescription(rtcMessage.answer);
  }

  function sendCall(data) {
    socket.emit('call', data);
  }

  const JoinScreen = () => {
    return (
      <KeyboardAvoidingView
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
        style={{
          flex: 1,
          backgroundColor: '#050A0E',
          justifyContent: 'center',
          paddingHorizontal: 42,
        }}>
        <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
          <>
            <View
              style={{
                padding: 35,
                backgroundColor: '#1A1C22',
                justifyContent: 'center',
                alignItems: 'center',
                borderRadius: 14,
              }}>
              <Text
                style={{
                  fontSize: 18,
                  color: '#D0D4DD',
                }}>
                Your Caller ID
              </Text>
              <View
                style={{
                  flexDirection: 'row',
                  marginTop: 12,
                  alignItems: 'center',
                }}>
                <Text
                  style={{
                    fontSize: 32,
                    color: '#ffff',
                    letterSpacing: 6,
                  }}>
                  {callerId}
                </Text>
              </View>
            </View>

            <View
              style={{
                backgroundColor: '#1A1C22',
                padding: 40,
                marginTop: 25,
                justifyContent: 'center',
                borderRadius: 14,
              }}>
              <Text
                style={{
                  fontSize: 18,
                  color: '#D0D4DD',
                }}>
                Enter call id of another user
              </Text>
              <TextInputContainer
                placeholder={'Enter Caller ID'}
                value={otherUserId.current}
                setValue={text => {
                  //otherUserId.current = text;
                  otherUserId.current = state.chat.selectedRoom;
                  //console.log('TEST', otherUserId.current);
                }}
                keyboardType={'number-pad'}
              />
              <TouchableOpacity
                onPress={() => {
                  setType('OUTGOING_CALL');
                  processCall();
                }}
                style={{
                  height: 50,
                  backgroundColor: '#5568FE',
                  justifyContent: 'center',
                  alignItems: 'center',
                  borderRadius: 12,
                  marginTop: 16,
                }}>
                <Text
                  style={{
                    fontSize: 16,
                    color: '#FFFFFF',
                  }}>
                  Call Now
                </Text>
              </TouchableOpacity>
            </View>
          </>
        </TouchableWithoutFeedback>
      </KeyboardAvoidingView>
    );
  };

  const OutgoingCallScreen = () => {
    return (
      <View
        style={{
          flex: 1,
          justifyContent: 'space-around',
          backgroundColor: '#050A0E',
        }}>
        <View
          style={{
            padding: 35,
            justifyContent: 'center',
            alignItems: 'center',
            borderRadius: 14,
          }}>
          <Text
            style={{
              fontSize: 16,
              color: '#D0D4DD',
            }}>
            Calling to...
          </Text>

          <Text
            style={{
              fontSize: 36,
              marginTop: 12,
              color: '#ffff',
              letterSpacing: 6,
            }}>
            {otherUserId.current}
          </Text>
        </View>
        <View
          style={{
            justifyContent: 'center',
            alignItems: 'center',
          }}>
          <TouchableOpacity
            onPress={() => {
              setType('JOIN');
              otherUserId.current = null;
            }}
            style={{
              backgroundColor: '#FF5D5D',
              borderRadius: 30,
              height: 60,
              aspectRatio: 1,
              justifyContent: 'center',
              alignItems: 'center',
            }}>
            <Text>CallEnd</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  };

  const IncomingCallScreen = () => {
    return (
      <View
        style={{
          flex: 1,
          justifyContent: 'space-around',
          backgroundColor: '#050A0E',
        }}>
        <View
          style={{
            padding: 35,
            justifyContent: 'center',
            alignItems: 'center',
            borderRadius: 14,
          }}>
          <Text
            style={{
              fontSize: 36,
              marginTop: 12,
              color: '#ffff',
            }}>
            {otherUserId.current} is calling..
          </Text>
        </View>
        <View
          style={{
            justifyContent: 'center',
            alignItems: 'center',
          }}>
          <TouchableOpacity
            onPress={() => {
              processAccept();
              setType('WEBRTC_ROOM');
            }}
            style={{
              backgroundColor: 'green',
              borderRadius: 30,
              height: 60,
              aspectRatio: 1,
              justifyContent: 'center',
              alignItems: 'center',
            }}>
            <Text color="#fff'">CallAnswer </Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  };

  function switchCamera() {
    localStream.getVideoTracks().forEach(track => {
      track._switchCamera();
    });
  }

  function toggleCamera() {
    localWebcamOn ? setlocalWebcamOn(false) : setlocalWebcamOn(true);
    localStream.getVideoTracks().forEach(track => {
      localWebcamOn ? (track.enabled = false) : (track.enabled = true);
    });
  }

  function toggleMic(localStream) {
    localMicOn ? setlocalMicOn(false) : setlocalMicOn(true);
    localStream.getAudioTracks().forEach(track => {
      localMicOn ? (track.enabled = false) : (track.enabled = true);
    });
  }

  function leave() {
    peerConnection.current.close();
    setlocalStream(null);
    setType('JOIN');
  }

  const WebrtcRoomScreen = () => {
    return (
      <View
        style={{
          flex: 1,
          backgroundColor: '#050A0E',
          paddingHorizontal: 12,
          paddingVertical: 12,
        }}>
        {localStream ? (
          <RTCView
            objectFit={'cover'}
            style={{flex: 1, backgroundColor: '#050A0E'}}
            streamURL={localStream.toURL()}
          />
        ) : null}
        {remoteStream ? (
          typeof remoteStream.toURL === 'function' ? (
            <>
              {
                <RTCView
                  objectFit={'cover'}
                  style={{
                    flex: 1,
                  }}
                  streamURL={remoteStream?.toURL()}
                />
              }
            </>
          ) : null
        ) : null}
        <View
          style={{
            marginVertical: 12,
            flexDirection: 'row',
            justifyContent: 'space-evenly',
          }}>
          <View
            style={{backgroundColor: 'red'}}
            onPress={() => {
              leave();
            }}
            Icon={() => {
              return <Text color="#FFF">CallEnd </Text>;
            }}
          />
          <View
            style={{
              borderWidth: 1.5,
              borderColor: '#2B3034',
            }}
            backgroundColor={!localMicOn ? '#fff' : 'transparent'}
            onPress={() => {
              toggleMic();
            }}
            Icon={() => {
              return localMicOn ? (
                <Text color="#FFF">MicOn </Text>
              ) : (
                <Text color="1D2939">MicOff </Text>
              );
            }}
          />
          <View
            style={{
              borderWidth: 1.5,
              borderColor: '#2B3034',
            }}
            backgroundColor={!localWebcamOn ? '#fff' : 'transparent'}
            onPress={() => {
              toggleCamera();
            }}
            Icon={() => {
              return localWebcamOn ? (
                <Text color="#FFF">VideoOn</Text>
              ) : (
                <Text color="1D2939">VideoOff</Text>
              );
            }}
          />
          <View
            style={{
              borderWidth: 1.5,
              borderColor: '#2B3034',
            }}
            backgroundColor={'transparent'}
            onPress={() => {
              switchCamera();
            }}
            Icon={() => {
              return <Text color="#FFF">CameraSwitch</Text>;
            }}
          />
        </View>
      </View>
    );
  };
  console.log(
    'Before render remoteStream=',
    remoteStream ? JSON.stringify(remoteStream) : '',
  );
  console.log('Before render remoteStream.toURL=', remoteStream?.toURL());
  console.log(
    'Before render localStream=',
    localStream ? JSON.stringify(localStream) : '',
  );
  console.log('Before render localStream.toURL=', localStream?.toURL());
  switch (type) {
    case 'JOIN':
      return JoinScreen();
    case 'INCOMING_CALL':
      return IncomingCallScreen();
    case 'OUTGOING_CALL':
      return OutgoingCallScreen();
    case 'WEBRTC_ROOM':
      return WebrtcRoomScreen();
    default:
      return null;
  }
}

I run the app on my physical android device and call the emulator but I can see remote video

olikom
  • 1

0 Answers0