We have the following setup for webrtc two-way video and audio streaming:
Mobile
Android application using Google WebRTC implementation java wrapper. Tested these two libs:
implementation 'org.webrtc:google-webrtc:1.0.+'
implementation 'com.github.webrtc-sdk:android:92.4515.03' // https://github.com/webrtc-sdk/android
RPi
Gstreamer with WebRTC plugin based on gst-examples with a few modifications.
Browser
Also slightly modified gst-examples Javascript implementation. It is based on native browser WebRTC support.
TURN
Coturn server working in Docker (coturn/coturn).
The problem
When calling from RPi to browser or mobile to browser everything works fine both in local network and via Internet (through TURN server). However when trying to connect RPi and Android device via Internet it gets stuck (local network works as well). The devices communicate with each other, negotiate SDPs and ICE candidates and the TURN server opens the connections properly, but there is no audio/video.
When looking at Android logs, the first difference is that there is no ICE connection change triggered:
PeerConnectionObserver: onStandardizedIceConnectionChange: CHECKING
nor
PeerConnectionObserver: onConnectionChange: CONNECTING
which happens before ICE candidates exchange. After ICE candidates have been sent there is no further communication with the other device and no status changes are being fired. Like for example these in the working case:
PeerConnectionObserver: onSelectedCandidatePairChanged: org.webrtc.CandidatePairChangeEvent@a856333
PeerConnectionObserver: onStandardizedIceConnectionChange: CONNECTED
PeerConnectionObserver: onConnectionChange: CONNECTED
PeerConnectionObserver: onIceConnectionChange CONNECTED
[Internal data channel]: onStateChange
PeerConnectionObserver: onDataChannel org.webrtc.DataChannel@b76f6f0
[External data channel]: onStateChange
The coturn logs don't show anything suspicious at all.
Would appreciate any tips or help of any kind in further investigating this issue.
EDIT:
With @RSATom suggestion we've looked more closely to ICE and SDP messages being sent and found no discrapancies there. The SDPs are identical for mobile-RPi communication through Internet and locally. The only difference are ids generated randomly. The ICEs also do not differ much. It's identical for the first sender (offerer) and slightly different for answerer because it contains the TURN details:
via Internet
video0:0:candidate:228040959 1 udp 2122260223 10.111.215.156 53556 typ host generation 0 ufrag 3SNh network-id 3 network-cost 900::UNKNOWN
video0:0:candidate:559267639 1 udp 2122202367 ::1 48566 typ host generation 0 ufrag 3SNh network-id 2::UNKNOWN
video0:0:candidate:1510613869 1 udp 2122129151 127.0.0.1 41279 typ host generation 0 ufrag 3SNh network-id 1::UNKNOWN
video0:0:candidate:1876313031 1 tcp 1518222591 ::1 46051 typ host tcptype passive generation 0 ufrag 3SNh network-id 2::UNKNOWN
video0:0:candidate:344579997 1 tcp 1518149375 127.0.0.1 60259 typ host tcptype passive generation 0 ufrag 3SNh network-id 1::UNKNOWN
video0:0:candidate:842163049 1 udp 1686052607 31.0.91.196 6742 typ srflx raddr 10.111.215.156 rport 53556 generation 0 ufrag 3SNh network-id 3 network-cost 900:stun:3.70.23.20:3478:UNKNOWN
video0:0:candidate:593469510 1 udp 41885439 172.31.0.169 14307 typ relay raddr 31.0.91.196 rport 6742 generation 0 ufrag 3SNh network-id 3 network-cost 900:turn:3.70.23.20:3478?transport=udp:UNKNOWN
via local WiFi
video0:0:candidate:2858526953 1 udp 2122260223 192.168.0.21 38123 typ host generation 0 ufrag rwx/ network-id 3 network-cost 10::UNKNOWN
video0:0:candidate:559267639 1 udp 2122202367 ::1 52058 typ host generation 0 ufrag rwx/ network-id 2::UNKNOWN
video0:0:candidate:1510613869 1 udp 2122129151 127.0.0.1 39469 typ host generation 0 ufrag rwx/ network-id 1::UNKNOWN
video0:0:candidate:842163049 1 udp 1686052607 178.235.191.135 13607 typ srflx raddr 192.168.0.21 rport 38123 generation 0 ufrag rwx/ network-id 3 network-cost 10:stun:3.70.23.20:3478:UNKNOWN
video0:0:candidate:1876313031 1 tcp 1518222591 ::1 51551 typ host tcptype passive generation 0 ufrag rwx/ network-id 2::UNKNOWN
video0:0:candidate:344579997 1 tcp 1518149375 127.0.0.1 41007 typ host tcptype passive generation 0 ufrag rwx/ network-id 1::UNKNOWN
video0:0:candidate:593469510 1 udp 41885439 172.31.0.169 13744 typ relay raddr 178.235.191.135 rport 13607 generation 0 ufrag rwx/ network-id 3 network-cost 10:turn:3.70.23.20:3478?transport=udp:UNKNOWN
The order of ICE messages seems not to be persisted.
A diff of full logs (Internet left, local right): https://www.diffchecker.com/elEA6rkJ
ICE messages diff: https://www.diffchecker.com/C1TzPcMm
EDIT 2:
I missed that some of the logs were not present. Full log diff for connectivity via Internet (working vs not-working) available here. There are a few differences:
- Invalid connection misses a lot of logs informing of connection creation and keepalive and still displays this instead:
tag: basic_ice_controller.cc
message: (line 541): Sorting 0 available connections
- For the invalid connection RTP SSRC
transport_ccis turned off - Gstreamer uses stream aliases (like
video0andaudio0) which then cause ICE candidate errors like this:
ReadyToUseRemoteCandidate: Invalid candidate. Mid 0 specified but no media section with that mid found.
- There are failed TCP requests to TURN:
LS_INFO
tag: turn_port.cc
message: (line 375): Port[31f33800:video0:1:0:relay:Net[lo:127.0.0.x/8:Loopback:id=1]]: Trying to connect to TURN server via tcp @ sicdev-turn.ddns.net:3478
LS_INFO
tag: android_network_monitor.cc
message: (line 422): Find network handle.
LS_WARNING
tag: android_network_monitor.cc
message: (line 307): BindSocketToNetwork unable to find network handle for addr: 127.0.0.x ifname: lo
LS_VERBOSE
tag: physical_socket_server.cc
message: (line 207): Binding socket to loopback address failed; result: -3
LS_ERROR
tag: basic_packet_socket_factory.cc
message: (line 186): TCP connect failed with error 22
LS_ERROR
tag: turn_port.cc
message: (line 379): Failed to create TURN client socket