PeerConnection's localDescription doesn't include ICE candidates, normal?

Hi.

I’m making some experiments with this native module and sip.js on iOS, trying to make them play nice with each other. sip.js seems to rely on PeerConnection’s localDescription value having the ICE candidates in there after the ICE process is complete, so that the SIP Invite/Answer contents already cary the candidates accurante information. This does not seem to happen with this module (as opossed to what happens on a browser environment), the localDescription value seems to not be updated with the ICE process results.

Also tried just with this module’s github simple example (without sip.js in the mix) and the same behaviour is observed. To note that if after the ICE process is complete I make another createOffer the sdp returned does have the ICE candidates in it.

Someone knows if this is normal behaviour, or care to make any relevant comment?

Thanks,
André

1 Like

Why do you expect the SDP to have ICE candidates if the ICE process is not complete?

1 Like

Hi @saghul.

Maybe I didn’t explain correctly.

I don’t expect that, what I expected was that localDescription had the ICE candidates after the process is finished, which is what doesn’t happen (and it happens in the browser). To see the candidantes in there I have to make a second createOffer + setLocalDescription run.

1 Like

Ah, I see. TBH I’m not sure what the correct behavior should be here, maybe we should confirm with the specs.

1 Like

Where do you think this can be mentioned? Searched around a bit already.

Tell me one thing if you know, this react-native module shares webrtc stack with the browsers or is a different implementation?

1 Like

@saghul I think I found something related with this here: WebRTC connectivity - Web APIs | MDN

The pendingLocalDescription contains not just the offer or answer under consideration, but any local ICE candidates which have already been gathered since the offer or answer was created. Similarly, pendingRemoteDescription includes any remote ICE candidates which have been provided by calls to RTCPeerConnection.addIceCandidate() .

1 Like

Looks related, but we don’t implement pendingLocalDescription so…

In general, waiting to create the local description until ICE is done gathering will work on all environments, so I’d just do that.

1 Like

That’s kinda of a hack but it works.

1 Like

Hi @saghul. Been trying to give this thing another go and have a question about your last response. You said to wait until ICE is done gathering to set the local description but in my tests the ice gathering is started by the call to setLocalDescription. Is there another way of triggering the ICE gathering?

Or updating the value of the local description after ice has finished without using createAnswer. I guess. Maybe I’m missing something.

Best regards.

1 Like

Some clarification on my previous post.

For outgoing calls strategy is calling createOffer() again after the ICE process is complete, the SDP returned will contain the gathered candidates (don’t really understand why though). Call setLocalDescription with that and proceed. All works ok.

For inbound calls calling createAnswer() a second time doesn’t work. Throws error complaining about the PeerConnection signaling state not being the expected one (suspect this is because we called setLocalDescription earlier to trigger the ICE process). As of now have no way of getting the candidates into the SDP, tried doing it manually but didn’t work, maybe they have a specific place to be in the sdp and I don’t know where.

Do you see any way around this or is it give up time?

1 Like

Well. I’ve done it by inserting the candidates manually in the SDP after the ICE process. Seems to work well, anyone in need reading this in the future, ask and I can share how to do it.

1 Like

@andremar @saghul
I’m beginner of webrtc. I got same problem.
・createAnswer and setLocalDescription and ICE Candidate gethering is complete.
but my peerConnection has no candidate.

so, I also try to insert candidates manually, but Invalid SDP error.

If’ OK, please tell me how you create manually SDP.

1 Like

@andremar for me it works fine, when I just wait before triggering the call after calling createOffer(). I’m still curios in seeing your code, so I would appreciate if you post it here! I did not mannage to trigger the onicecandidates though and I had to call it manually which is what caused the problem I believe that I’m having now c=IN IP4 0.0.0.0 so I can initiate a call but there is no IP address so it’s useless…

here’s what I did:

    let peerConnection = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
    peerConnection.addStream(localStream);
    peerConnection.onicecandidate = e => {
console.log('onicecandidate');
     
window.setTimeout(() => {
     
console.log('window.setTimeout');
     
        
     
peerConnection.createOffer().then(sdp => peerConnection.setLocalDescription(sdp)).then(() => {
     
let sdpVariable = peerConnection.localDescription.sdp;
     
alert(sdpVariable);

});
});
}

@h.mito did you figure it out?

@saghul could I add this to the GitHub README?

I can not edit my original reply, so I’m just gonna copy it to here because it needs editing:

https://pastebin.com/kFMUMbze

I’m still getting c=IN IP4 0.0.0.0 which is not good. But at least now onicecandidate is getting triggered.

Hi @chagai95, my issue was really due to what sip.js expected the webrtc stack to do, which is different in browsers and in react-native-webrtc module, so the solution I found was a change to their code to support the different behaviour in this module. Not sure if it’s usefull to you…

1 Like

I’d love to see it anyway, maybe it’ll help me understand more what’s going on, I don’t really get it yet fully.

just figured it out! you need to call setLocalDescription twice! the first time is to trigger the onicecandidates and the second time is to actually set the sdp.

peerConnection.createOffer().then(sdp => peerConnection.setLocalDescription(sdp)).then(() => {
  // just to get the onicecandidates to start
}).catch((error) => {
  console.log(error);
});

after the first time you call setLocalDescription onicecandidate will be triggered you need to wait a second and then call setLocalDescription again and in the call back function send the sdp.

let count = 0;
peerConnection.onicecandidate = e => {
  Clipboard.setString(JSON.stringify(e.candidate));
  console.log(JSON.stringify(e.candidate))
  console.log('onicecandidate');
  count++;

  window.setTimeout(() => {
    console.log('window.setTimeout');

    peerConnection.createOffer().then(sdp => peerConnection.setLocalDescription(sdp)).then(() => {
      sdpVariable = peerConnection.localDescription.sdp;
      let config =
      {
        "type": "offer",
        "sdp": sdpVariable
      };


      var message = JSON.stringify(config);
      if (ws.readyState === 1) {
        console.log('wsSend: ' + message);
        // making sure the offer is only sent once.
        if (sdpSent === false) {
          ws.send(message);
          // setting sdpSent to true so we don't send multiple calls to the Web Socket, we send this from here because 
          // this is where we get all the ice candidates, but we don't want to send an offer after every ice candidate that is registered and
          // we make sure all of them are registered before calling by waiting 1 second.
          sdpSent = true;
          // copying the sdp to the clipboard to analyze better from the PC
          Clipboard.setString(sdpVariable);
          console.log(sdpVariable);
          console.log(count);
        }
      } else {
        console.log('failed to send message. ws state: ' + ws.readyState);
      }
    }).catch((error) => {
      console.log(error);
    });



  }, 1000);
};

Sorry for resurrecting old thread. But I also found a solution to this problem. I was generating an answer, but I guess the same would work when generating an offer.

Here’s my flow to generate an answer that does contain ICE candidates:

  • Call peerConnection.createAnswer, but don’t send the answer
  • Wait a moment (I just used 2 seconds, but I suppose you could wait for the ice gathering state to change)
  • Get peerConnection.localDescription and send that as the answer instead.

This also seems to be the method that simple-peer uses when trickle is disabled.

Hope this helps somebody!

worked for me. thx~

but, how setTimeout in createAnswer?

Hello, does anyone have a working code example on how to set up a createAnswer()?

The setTimeout works for an offer, but not for an answer.

Also in order to make it trigger a second time, I need to add soomthing for the offer. E.g. offerToReceiveAudo true. Otherwise it will not trigger again.

// this one to startgetting ice candidates
conn.createOffer({ iceRestart: true }).then(sdp => conn.setLocalDescription(sdp));

setTimeout(async () => {
            // this one actually gets us the valid ice candidate we can use for a connection
            conn.createOffer({ offerToReceiveAudio: true }).then(sdp => conn.setLocalDescription(sdp));
}, 1000);