murat-dogan / node-datachannel

WebRTC For Node.js and Electron. libdatachannel node bindings.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Datachannels start accumulating latency when connection is poor

joecarl opened this issue · comments

I'm developing a little robot with a Raspberry Pi. It's remotedly controlled using the web browser. And I'm using this library on the raspberry side to connect via webrtc.

The robot transmits data via 3 datachannels. 1 for video, 1 for input, 1 for stats reporting.
Input is configured as ordered.
Reporting and video are unordered with maxRetransmits = 0.

Everything works fine with very low latency when the connection is good. But when when the robot reaches some places where the connection is not very good the video starts arriving with cummulative delay, as if packages were buffered and never dropped so latency starts increasing indefinetely.

I'm doing these tests with the PC and the robot connected to the same wifi.

What surprises me even more is that the input always arrives instantly even when the connection is poor. And that's why I'm opening this issue.

What surprises me even more than the previous surprise is that I also implemented (for curiosity) a "relay" video transmission through my server and it doesn't have this problem. The latency is a little bigger than the datachannel (when datachannel has good connection of course) but it can reach much further places than the datachannel approach (which seems absurd to me since datachannel is p2p).

Is there any way to force the datachannels to drop packages in this situation. Or any other suggestion? Thanks!

Everything works fine with very low latency when the connection is good. But when when the robot reaches some places where the connection is not very good the video starts arriving with cummulative delay, as if packages were buffered and never dropped so latency starts increasing indefinetely.

This is expected behavior as even if you can change their behavior regarding ordering and reliability, WebRTC data channels are still buffered and have congestion control like a TCP connection. Basically, the sender detects available network capacity and adapt what is sent accordingly. If the application sends faster than what the network can achieve, messages will be buffered.

maxRetransmits only control the retransmission strategy, so setting it to zero means packets won't be sent twice, so any message with a packet missing at reception will be discarded, but messages will still be buffered and sent once.

What surprises me even more is that the input always arrives instantly even when the connection is poor. And that's why I'm opening this issue.

If input consists in infrequent small messages, it won't build a backlog of buffered messages even if network conditions are poor, so they can be sent fast.

What surprises me even more than the previous surprise is that I also implemented (for curiosity) a "relay" video transmission through my server and it doesn't have this problem. The latency is a little bigger than the datachannel (when datachannel has good connection of course) but it can reach much further places than the datachannel approach (which seems absurd to me since datachannel is p2p).

This is surprising as it should behave more or less the same if the limiting link is the robot wifi. If you send video over a websocket for instance, it will start to buffer as soon as the application sends faster than the network capacity.

Is there any way to force the datachannels to drop packages in this situation. Or any other suggestion? Thanks!

You can prevent messages from accumulating in the data channel buffer by checking bufferedAmount() before sending. If it is non-zero, you should drop the message instead of sending it.

Messages may still be buffered at SCTP level before being sent, which may introduce delay. You can mitigate this by reducing the SCTP send buffer size with nodeDataChannel.setSctpSettings({ sendBufferSize: 262144 }). You can't reduce the buffer size lower than the maximum message size, and be aware that a buffer size too low may cap the throughput.

A better solution would be to use the dedicated WebRTC media transport to send real-time video instead of data channels which are not really designed for this. Real-time media is not buffered and packets are just dropped in case of network issues. There is a media example in node-datachannel, libdatachannel itself has more advanced examples.

Ok thankyou very much for your detailed suggestions. I will try and see.

This is surprising as it should behave more or less the same if the limiting link is the robot wifi. If you send video over a websocket for instance, it will start to buffer as soon as the application sends faster than the network capacity.

In my relay implementation I use UDP on the raspberry side to send data to the server, then the server sends via websocket to the browser. (I guess UDP doesn't care about congestion control, does it?)

A better solution would be to use the dedicated WebRTC media transport to send real-time video instead of data channels which are not really designed for this.

Yeah, I guess that would be the best approach, I will also try that method (when I find out how to deal with codecs and stuff)

I tried your suggestions and got some improvement. But it still has more latency than my relay implementation :(
I guess I will definetely have to implement the other option you suggested about using the dedicated media transport for the video transmission.

I'll see what I can do about that. Thankyou!

Sorry, I haven't mentionned it even if it should be the obvious parameter to tune for your use case: rather than setting maxRetransmits to 0, you should set maxPacketLifeTime to a given duration (in milliseconds) to drop packets once the lifetime is elapsed and therefore limit delay.