Reputation: 376
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.
Upvotes: 5
Views: 13732
Reputation: 3303
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.
Upvotes: 8
Reputation: 25874
Impossible to say without the code, but here are a few hints:
You can try to "nicer" your process. If this improves performance, it's an OS scheduler issue.
Upvotes: 1
Reputation: 328770
[EDIT] Replace the receiver with something that has a buffer. Or maybe try to send more data than the receiver really needs - it really should have a buffer already. I doubt that there is anyone in the world who builds set-top boxes and who doesn't know that there are network lags and who isn't bankrupt already.
From your explanation, I doubt that the cause is in your code. There are many reasons why it can be slower:
Conclusion: Your problem is that the receiver has no buffer. There are really very many reasons that can cause a delay in the sending of the UDP packets and all of them are not under the control of your code.
Original Answer
UDP is not a reliable protocol. There is no guarantee that packets arrive at the receiver in time. UDP just has a smaller overhead than TCP but that comes at the cost of reliability.
The usual solution is to send more data than the client needs. So for example, you send the first 10 seconds at full throttle. This allows the client to cache some data to play over lags.
You should also change the client to send back the length of the cached stream to the server every few seconds. The server can then calculate how many packets to send at full throttle to keep the cache on the client side filled.
Upvotes: 0
Reputation: 5647
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)
Upvotes: 1