Producer's WebRTC would not connect

Hello,
I am implementing a Flutter based mediasoup client. I am porting the existing JS client library to Dart and am able to complete the producer/consumer sequence mentioned here.
The server is running inside my own node app that includes custom signalling implementation.
The consumer seems to be able to connect to the server (I get RTCIceConnectionStateConnected event).
However, the producer’s WebRTC is not able to connect to the server - I only get RTCIceConnectionStateChecking event.
At the moment I use a single video-only stream with a single producer and a single consumer.
I have compared my SDPs with those of Chrome (based on the available JS mediasoup client library). The SDPs seem to be very similar - I see that same ICE candidates and pretty much the same entries.
One that that caught my attention is the following line in Producer’s (Android) WebRTC log:
I/basic_port_allocator.cc(15179): (line 1020): Discarding candidate because it doesn’t match filter.
I/basic_port_allocator.cc(15179): (line 1095): Port[c4bf6700:video:1:0:local:Net[wlan0:10.0.1.x/24:Wifi:id=3]]: Port completed gathering candidates.
Ever since that line I only see attempts to establish ICE connectivity via loopback interface (which would be incorrect since the server is not running on the localhost).

I have followed in debugger pretty much every line in my Dart mediasoup client library (ported from JS) and I don’t see and errors being generated.

Any ideas what might be wrong with this or where else should I try to look?

Thank you,
Leo

According to the error the ICE candidate is invalid.
Is the address of the ice candidate valid?
Also what is the ice candidate type?

What version of the webrtc library are you using?

Here are the candidate lines from the producer’s remote answer SDP:
a=candidate:udpcandidate 1 udp 1076558079 10.0.1.3 10060 typ host
a=candidate:tcpcandidate 1 tcp 1076302079 10.0.1.3 10056 typ host tcptype passive
Here are the candidate lines from Chrome’s SDP when using JS based mediasoup client:
a=candidate:udpcandidate 1 udp 1076558079 10.0.1.3 10032 typ host
a=candidate:tcpcandidate 1 tcp 1076302079 10.0.1.3 10028 typ host tcptype passive
Seems to be the same.
The IP of mediasoup server is 10.0.1.3 and here is webRtcTransport section from config.ts file on the server side:
webRtcTransport: {
listenIps: [
{
ip: ‘10.0.1.3’,
announcedIp: ‘’,
},
],
maxIncomingBitrate: 1500000,
initialAvailableOutgoingBitrate: 1000000,
},

Regarding WebRTC version: here is the line from build.gradle file on Android:
api ‘org.webrtc:google-webrtc:1.0.28262’
Its a precompiled package included with Flutter’s WebRTC package.

Thank you,
Leo

Actually, I may have missed a couple of important log lines. It appears the correct route was selected:
I/p2p_transport_channel.cc(15179): (line 1938): Channel[video|1|__]: New selected connection: Conn[c4bf1200:video:Net[wlan0:10.0.1.x/24:Wifi:id=3]:YtSdse9g:1:0:local:udp:10.0.1.x:39189->4eXizEB3:1:1076558079:local:udp:10.0.1.x:10038|CRWS|S|0|0|4623781745794104831|38]
However, almost immediately after that line there a couple of troubling Dtls related lines:
I/dtls_transport.cc(15179): (line 791): DtlsTransport[video|1|__]: configuring DTLS handshake timeout 76 based on ICE RTT 38
I/openssl_stream_adapter.cc(15179): (line 777): BeginSSL with peer.
I/dtls_transport.cc(15179): (line 696): DtlsTransport[video|1|__]: DtlsTransport: Started DTLS handshake
I don’t see anything Dtls related after the last line so I would assume the handshake never completes.
Any ideas on this?

Thank you,
Leo

After some more investigations, it appears mediasoup server acts as Dtls server for both, producer and consumer, whereas the client side expects server-side Producer to act as Dtls client (base on the SDPs).
After enabling Dtls server side logs I get the following for producer:
[Node] 2020-05-12T03:35:01.501Z mediasoup:Channel [pid:53518] RTC::WebRtcTransport::MayRunDtlsTransport() | transition from DTLS local role ‘auto’ to ‘server’ and running DTLS transport

[Node] 2020-05-12T03:35:01.501Z mediasoup:Channel [pid:53518] RTC::DtlsTransport::OnSslInfo() | DTLS handshake start
[Node] 2020-05-12T03:35:01.502Z mediasoup:Channel [pid:53518] RTC::DtlsTransport::OnSslInfo() | [role:server, action:‘before SSL initialization’]
[Node] 2020-05-12T03:35:01.503Z mediasoup:Channel [pid:53518] RTC::DtlsTransport::OnSslInfo() | role: server, waiting:‘before SSL initialization’]

And the following for consumer:
[Node] 2020-05-12T03:35:01.890Z mediasoup:Channel [pid:53518] RTC::WebRtcTransport::MayRunDtlsTransport() | transition from DTLS local role ‘auto’ to ‘server’ and running DTLS transport

[Node] 2020-05-12T03:35:01.890Z mediasoup:Channel [pid:53518] RTC::DtlsTransport::OnSslInfo() | DTLS handshake start

[Node] 2020-05-12T03:35:01.898Z mediasoup:Channel [pid:53518] RTC::DtlsTransport::OnSslInfo() | DTLS handshake done
So it appears Dtls client/server role expectations do NOT match for the Producer. Why does the server decide to switch from ‘auto’ to ‘server’ role in both cases?

Thank you,
Leonid

Since I also did a port into another language I know the pain of subtle things destroying everything :D. Couple of questions:

  1. Are you setting the SDP parameters the correct way? For producing, the client creates an offer and the remote answer is set afterwards. For consuming, the server creates the offer and the client sets the answer afterwards. I could imagine it only works one way because your setup is identical for both.
  2. Make sure you pass the correct DTLS parameters to the server. I once mistakenly sent back the server’s own DTLS parameters and the handshake obviously never succeeded.
  3. Regarding roles I have the following switch in my implementation for the producer:
            switch (remoteDtlsParameters.getString("role")) {
                case "client":
                    remoteMediaObj.put("setup", "active");
                    break;
                case "server":
                    remoteMediaObj.put("setup", "passive");
                    break;
                case "auto":
                    remoteMediaObj.put("setup", "active");
                    break;
            }

so the role “auto” becomes active in my case, while it sounds like it becomes passive in yours - not really sure if that is of any importance or helps though.

Just some blind guesses, the easiest way to help would be if you could provide a pair of the offer and answer and ideally also the communication you are doing with the server up until that point to compare. Also, if you are not already doing it, please activate as much logging as possible on the server-side, this usually helps a lot more than the client-side when doing your own stuff since it’s the obvious stable part of the whole thing and usually gives a lot of feedback (dtls errors etc.).

2 Likes

Other than already said in comments above, ensure you are honoring the a=ice-lite line in the remote SDP as mediasoup-client does. This is part of IceParameters object.

1 Like

Thanks, everyone.
It seems to work now - there was an issue with sending Dtls parameters to the server.

Leo

2 Likes

Can you share your ported client for flutter github repo?