react-native-webrtc / react-native-webrtc

The WebRTC module for React Native
https://react-native-webrtc.discourse.group
MIT License
4.63k stars 1.23k forks source link

react-native-webrtc Mic not closing after video call on iOS #1019

Closed zaid296imtiaz closed 3 years ago

zaid296imtiaz commented 3 years ago

Our iOS app has audio video calling implemented using the following technologies:

"react-native": "0.63.4" "react": "16.13.1" "react-native-webrtc": "^1.87.3" "react-native-incall-manager": "^3.3.0" iOS version 14.4.1

Our calling module works like the following:

First request and initiate audio call Then request and initiate video call On the code side things work like this:

We call the getStream() function which gets the user media for audio call i.e Audio only Then we call the startStream() function which connects the peer connection On requesting video we call the getVideoStream() method to get Audio and Video streams Call startStream() again to start peer connection with video The scenario is as follows:

We start off by connecting an audio call. On success the audio call is connected and works fine as expected We request for video and connect video call, all works fine as expected and I receive video on both ends When I disconnect the call and stop tracks using this.state.localStream.getTracks(), the mic does not close. An orange indicator for mic is visible on iOS. Important Notes:

Disconnecting from the audio call closes the mic just fine

Even if we get video stream on audio call and disconnect without connecting video it still works fine and closes both tracks

Its only when I connect the video is when the issue arises

Calling InCallManager.stop() closes the mic but does not open it on second call. The mic does not open on second call and the orange mic indicator on iOS is not shown.

Get User Media Audio Call

getStream() { InCallManager.setSpeakerphoneOn(false); InCallManager.setMicrophoneMute(false);

mediaDevices.enumerateDevices().then((sourceInfos) => {

  let videoSourceId;

  for (let i = 0; i < sourceInfos.length; i++) {
    const sourceInfo = sourceInfos[i];
    if (
      sourceInfo.kind === 'videoinput' &&
      sourceInfo.facing === (true ? 'front' : 'back')
    ) {
      videoSourceId = sourceInfo.deviceId;
    }
  }
  mediaDevices
    .getUserMedia({
      audio: true,
    })
    .then((stream) => {
      this.setState({
        localStream: stream,
      });
    })
    .catch((error) => {
      // Log error
      console.log('stream get error', error);
    });
});

} Get User Media for Video Call

getVideoStream() { this.state.peerConn.removeStream(this.state.localStream); InCallManager.setSpeakerphoneOn(false); InCallManager.setMicrophoneMute(false);

mediaDevices.enumerateDevices().then((sourceInfos) => {

  let videoSourceId;

  for (let i = 0; i < sourceInfos.length; i++) {
    const sourceInfo = sourceInfos[i];
    if (
      sourceInfo.kind === 'videoinput' &&
      sourceInfo.facing === (true ? 'front' : 'back')
    ) {
      videoSourceId = sourceInfo.deviceId;
    }
  }
  mediaDevices
    .getUserMedia({
      audio: true,
      mirror: true,
      video: {
        mandatory: {
          minWidth: 500,
          minHeight: 300,
          minFrameRate: 30,
        },
        facingMode: true ? 'user' : 'environment',
        optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
      },
    })
    .then((stream) => {
      this.setState(
        {
          localStream: stream,
        },
        () => {
          this.startStream();
        },
      );
    })
    .catch((error) => {
      // Log error
      console.log('stream get error', error);
    });
});

} Start Stream Function

startStream() { console.log('start Stream');

this.newPeerConnection();

setTimeout(() => {
  this.state.peerConn
    .createOffer()
    .then((sessionDescription) =>
      this.setLocalAndSendMessage(sessionDescription),
    )
    .catch((error) => this.defaultErrorCallback(error));
}, 3000);

} newPeerConnection()

newPeerConnection() { var peerConn = new RTCPeerConnection({ iceServers: turnServer, });

peerConn.onicecandidate = (evt) => {

  console.log(`OnIceCan`);
  if (evt.candidate) {
    this.state.connection.invoke(
      'addIceCandidate',
      parseInt(this.state.ticket.pkTicketId),
      JSON.stringify({
        type: 'candidate',
        sdpMLineIndex: evt.candidate.sdpMLineIndex,
        sdpMid: evt.candidate.sdpMid,
        candidate: evt.candidate.candidate,
      }),
    );
  }
};

peerConn.addStream(this.state.localStream);

peerConn.addEventListener(
  'addstream',
  (stream) => {

    InCallManager.setForceSpeakerphoneOn(false);
    this.setState({
      isSpeakerEnabled: false,
    });

    this.setState({
      remoteStream: stream,
      showAudioCallTimer: true,
    });
  },
  false,
);

this.setState(
  {
    peerConn,
  });

} Close Tracks

if (this.state.localStream) { const tracks = this.state.localStream.getTracks();

      tracks.map((track, index) => {
        track.stop();
      });
    }
    if (this.state.peerConn) {
      this.state.peerConn.removeStream(this.state.localStream);
      this.state.peerConn.close();
      if (!this.state.callRatingSubmitted && this.state.remoteStream) {
        this._handleCallFeedbackModal(true);
      }
    }
JeroenVanSteijn commented 3 years ago

I experience the same but with audio calls in #1028

saghul commented 3 years ago

I can’t go through your code, sorry. Make sure you call release() on the track.

ashhadullah19 commented 3 years ago

I am also facing the same issue. Any update?

JeroenVanSteijn commented 3 years ago

Yes release() was not properly called on the stream. Make sure you add a .catch() to your .then to see if there's a mistake above your release call.