11

I have a webrtc app, and let's say two clients( client1 and client2), is there any way to find out what ICE candidate given by client1 is used by client2 and vice versa? because, every time to find this out, I have to use wireshark on both the clients, I thought reading the sdp might help, but I was wrong, as it gives all possible candidates...

Scenario: all UDP ports of client1 are blocked( blocked my me for testing purpose).
Client1's SDP:

...
a=rtcp:49407 IN IP4 <client1's IP>
a=candidate:3864409487 1 udp 2122194687 <client1's IP> 49407 typ host generation 0 // this would never work, since the udp ports are blocked...
a=candidate:3864409487 2 udp 2122194687 <client1's IP> 49407 typ host generation 0
a=candidate:2832583039 1 tcp 1518214911 <client1's IP> 0 typ host tcptype active generation 0
a=candidate:2832583039 2 tcp 1518214911 <client1's IP> 0 typ host tcptype active generation 0
a=candidate:973648460 1 udp 25042687 <TURN server IP> 64790 typ relay raddr <Proxy IP> rport 39963 generation 0
a=ice-ufrag:YSvrOiav8TglpCWD
...
mido
  • 24,198
  • 15
  • 92
  • 117
  • 3
    Check out this thread: https://groups.google.com/d/msg/discuss-webrtc/-VReEXf9RBM/h91i7CD-oJ8J – Guy S Mar 11 '15 at 16:12

3 Answers3

10

Well, taken from my answer to another question

I wrote and tested the below piece of code, works in latest versions of both firefox and chrome, getConnectionDetails returns a promise which resolves to connection details:

function getConnectionDetails(peerConnection){


  var connectionDetails = {};   // the final result object.

  if(window.chrome){  // checking if chrome

    var reqFields = [   'googLocalAddress',
                        'googLocalCandidateType',   
                        'googRemoteAddress',
                        'googRemoteCandidateType'
                    ];
    return new Promise(function(resolve, reject){
      peerConnection.getStats(function(stats){
        var filtered = stats.result().filter(function(e){return e.id.indexOf('Conn-audio')==0 && e.stat('googActiveConnection')=='true'})[0];
        if(!filtered) return reject('Something is wrong...');
        reqFields.forEach(function(e){connectionDetails[e.replace('goog', '')] = filtered.stat(e)});
        resolve(connectionDetails);
      });
    });

  }else{  // assuming it is firefox
    return peerConnection.getStats(null).then(function(stats){
        var selectedCandidatePair = stats[Object.keys(stats).filter(function(key){return stats[key].selected})[0]]
          , localICE = stats[selectedCandidatePair.localCandidateId]
          , remoteICE = stats[selectedCandidatePair.remoteCandidateId];
        connectionDetails.LocalAddress = [localICE.ipAddress, localICE.portNumber].join(':');
        connectionDetails.RemoteAddress = [remoteICE.ipAddress, remoteICE.portNumber].join(':');
        connectionDetails.LocalCandidateType = localICE.candidateType;
        connectionDetails.RemoteCandidateType = remoteICE.candidateType;
        return connectionDetails;
    });

  }
}


//usage example:
getConnectionDetails(pc).then(console.log.bind(console));
Community
  • 1
  • 1
mido
  • 24,198
  • 15
  • 92
  • 117
  • I got ```prflx``` for ```googRemoteCandidateType``` and ```relay``` for ```googLocalCandidateType```. Do you know what ```prflx``` means?? – Anand S Jan 08 '16 at 11:58
  • @anand have to check but I think it is the stun ice candidate – mido Jan 08 '16 at 12:23
  • 1
    `prflx` means peer reflexive candidate. See this [link](http://www.jdrosen.net/uploads/1/5/0/0/15008848/ice-basic-tutorial.pptx) – ceiling cat May 08 '18 at 14:00
4

This question is very old, but here's what works reliably in 2021! - at least for me.

You can use the RTCIceTransport.getSelectedCandidatePair() method which returns an RTCIceCandidatePair object (local and remote ice-candidate pairs).

Example: To get the selected candidate pairs for sending media from a peer connection.

peerConnection.getSenders().map(sender => {
    const kindOfTrack = sender.track?.kind;
    if (sender.transport) {
        const iceTransport = sender.transport.iceTransport;
        const logSelectedCandidate = (e) => {
            const selectedCandidatePair = iceTransport.getSelectedCandidatePair();
            console.log(`SELECTED ${kindOfTrack || 'unknown'} SENDER CANDIDATE PAIR`, selectedCandidatePair);
        };
        iceTransport.onselectedcandidatepairchange = logSelectedCandidate;
        logSelectedCandidate();
    } else {
        // retry at some time later
    }
});

The above works similarly for received media on the peer connection object. Use RTCPeerConnection.getReceivers() instead, in that case

Prince Odame
  • 534
  • 7
  • 17
-1

In chrome I used this:

let pc1 = new RTCPeerConnection(cfg);
    
pc1.addEventListener('connectionstatechange', async (e) => {    
    if (pc1.connectionState === 'connected') {
        // Peers connected!
        let stats = await pc1.getStats();
        for (const value of stats.values()) {
            if(value.type=="local-candidate" || value.type=="remote-candidate")
                console.log(value);
        }
    }
})
Thịnh Phạm
  • 2,528
  • 5
  • 26
  • 39
Cesar Morillas
  • 707
  • 5
  • 11
  • 1
    Why negative vote? – Cesar Morillas May 08 '22 at 09:48
  • Good point, I think it should be mandatory to leave an explanation for negative votes - only thing I would question is why use 'const value of stats.values()'? shouldn't that be 'var' or 'let' instead of 'const' as it's in a loop? – user2677034 May 24 '22 at 17:21
  • 2
    I use const instead of let as a protection style. See https://stackoverflow.com/questions/58483101/for-of-loop-should-i-use-const-or-let – Cesar Morillas May 26 '22 at 10:41