I've developed an MPEG-ts Streamer. It reads the packets from a file and sends them at the right pace to the receiver.
Now everything works fine excluding that I have some lags quite often. I have searched for every possible error in my code. I have optimized my program quite a bit performance wise.
Now I keep a log with the time it takes the sendto()
function to send the packet and I also log the difference between when the packet should have been sent and when it is sent.
I noticed that every time the packets are a lot later than average, the time it took the sendto()
to send the previous packet is much higher than normal too.
This indicates me that it is the sendto()
that is causing those lags every time it somehow takes longer to send a packet. I am using UDP sockets.
Am I maybe doing something wrong with the sockets? Could it be possible that the socket buffer is full and that it actually takes longer to send the packet? Or am I missing something? Is there maybe a way to accelerate the socket or to make it not fill it's buffer completely before sending?
As this is meant to stream video I am quite depending on the performance, mostly for HD where the lags happen more often as the amount of packets is much higher.
It'll stay in the socket buffer until you read it or the process exits. Once the socket buffer is full, new packets arriving is dropped.
TCP reassembles the original segments and places the input in the socket receive buffer. UDP simply passes the input on to the socket receive buffer.
To begin, try a value within the range 50,000 to 150,000 packets per second. Select an initial value based on capacity of the receiving CPUs to handle NIC interrupts. To reduce average latency, try raising the value. If the number of packets overwhelms any receiver, try a lower value.
The default send buffer size for UDP sockets is 65535 bytes. The default receive buffer size for UDP sockets is 2147483647 bytes.
There are only two reasons for sendto()
to block:
Check the size of your outgoing buffer:
int buff_size;
int len = sizeof(buff_size);
err = getsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&size,&len);
Some linux systems default to absurdly small send buffers (only a few kilobytes), so you may need to set it to something larger.
If buffer is larger and sendto()
is briefly blocking anyway (how long exactly?) then that's probably just the cost of doing business. Even on a LAN, and certainly on a WAN, latency is going to vary a lot.
Update
To increase the system limit on UDP buffer sizes:
sysctl -w net.core.wmem_max=1048576
sysctl -w net.core.rmem_max=1048576
You can make this permanent by adding the following lines to /etc/sysctl.conf
:
net.core.wmem_max=1048576
net.core.rmem_max=1048576
To take advantage of this within your application, you need to use setsockopt()
:
int len, trysize, gotsize;
len = sizeof(int);
trysize = 1048576+32768;
do {
trysize -= 32768;
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&trysize,len);
err = getsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&gotsize,&len);
if (err < 0) { perror("getsockopt"); break; }
} while (gotsize < trysize);
printf("Size set to %d\n",gotsize);
Repeat the same thing for SO_RCVBUF
. The loop is important because many systems silently enforce a maximum which is less than the max you set with sysctl
and when setsockopt()
fails, it leaves the previous value untouched. So you have to try many different values until you get one that sticks. Its also important to not test for (gotsize == trysize)
because on some systems the result which is set is not actually the same as the one you requested.
Unlike TCP, UDP does not buffer data. Source code would definitively help to understand things better. How big are the packets you are sending? In UDP it would be best to keep the payload at the size of the MTU (1500 bytes)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With