Latency with Multipeer Connectivity framework - ios

So I've been dumped with a very broken and outdated code base. I'm being asked however to fix only one bug with the latency that occurs when a message is sent between two devices.
The app is for streaming audio to several devices and playing them all at once. The issue is obviously caused when the host sends a "play" message and starts playing itself. This play message is delayed by up to 3 seconds and therefore the clients all end up out of sync.
I've attempted sending a CFAbsoluteTimeGetCurrent(); value to the clients where they then work out the latency but device clocks are very unreliable and I often get negative differences in time despite obviously being positive.
Any idea on how I can combat this? And before suggestions of changing the method entirely, there's isn't much time

Have you considered sending a SYN message along with a local timestamp and then have the per return the timestamp as a ACK message? You can take the difference between the current time and the return time and half it to get a latency.
Source: I did this.

Related

Is there a way to measure latency in iOS?

The problem I'm trying to solve is I would like to have a way to measure network latency and notify users when high latency situations occur. My app allows users to stream audio or video content from online sources and while playback is possible on 3g and even EDGE speeds in my testing, spikes in latency can cause hangups in usability. When this occurs I'd like to be able to alert the user the cause of these problems.
I've gone back to looking into Apple's Reachability sample code and it still does not help. I'll refer back to an issue filed over a year ago (http://openradar.appspot.com/13982938)
Assuming I start with a strong connection, I can set a custom profile in the network link conditioner that would drop 100% of packets sent, and the reachability code in the sample app does not change the positive result. It still tells me I have a connection even though no packets get through.
Are there any reliable values I can KVO or otherwise listen to, in order to get this information?
The easiest way (and probably the only one that really works) is to store time value when you send a request and evaluate latency when you receive a response. You can check how AFNetworkActivityLogger solves this here
https://github.com/AFNetworking/AFNetworkActivityLogger/blob/master/AFNetworkActivityLogger/AFNetworkActivityLogger.m
The most important lines for you are
objc_setAssociatedObject(notification.object, AFNetworkRequestStartDate, [NSDate date], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
when request is sent and
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:objc_getAssociatedObject(notification.object, AFNetworkRequestStartDate)];
when response is received.
How to integrate this logic to your code depends on what do you use to get streamed video.
My app allows users to stream audio or video content from online sources
Some built-in ways of streaming tell you what's going on. If you're using AVPlayerItem, properties such as playbackLikelyToKeepUp and the accessLog, along with notifications such as AVPlayerItemPlaybackStalledNotification, can be helpful in keeping you abreast of any issues.

iOS gamekit/bluetooth data streaming

I have a written a program using gamekit/bluetooth to transfer low quality video using compressed jpegs from one iOS device to another. I do already realize that gamekit/bluetooth should not be used for this purpose (for small chunks of data) but it does indeed work well streaming 15 low quality compressed jpegs/second with little to no latency.
The question I have is once I increase either the quality or frame rate from the iOS device sender to the iOS receiver, a lag or delay will occur and will no longer be real time. If there is a delay, I'd like somehow for the sending iOS device to discard frames so that the receiver can catchup or for the receiver to ignore the backlog queue.
In GameKit I have set the session mode to use GKSendDataUnreliable to see if it could help, but to no avail.
If delays occur, what is the best solution and correct approach to discard the frames (jpegs) so that the iOS receiver can then catch up back to real time? Would the sender need to stop transmission for a period of time or is there something that receiving client can do to discard the accumulating queue.
I've used NSStream before as well, and while using wifi allows for greater bandwidth, the same problem will still occur in terms of delays if too much data is being transmitted.
Thank you in advance for your help.
Could you not attach a timestamp to each jpg (time since epoch perhaps) so the receiving client will ignore all images that are not within a given timeframe.
Also you could have the receiving client respond back with simple acknowledgement packets indicating that a jpg has been received. If the sending client hasn't received an acknowledge packet within a given timeframe it discards all images it was going to send and starts from scratch.
With this solution if the receiving client falls X seconds behind the sender it will stop sending acknowledgement packets and discard all incoming data until the sender throws out everything in its queue and starts sending "live" frames again.

Why do I get packet loss when using GKMatchSendDataReliable?

I have a multiplayer iOS game, and I am sending data using GKMatchSendDataReliable. However, occasionally, the data packet is lost. I've checked on the sending end and I am not getting an error. I'm just not receiving it on the receiving in. It is intermittent, and I have NSLogs right at the beginning of my receive method, so I always know when I get a message.
Is GKMatchSendDataReliable 100% reliable? It seems like a waste to have to set up my own reliable data sending routines.
It seems that this only happens when one device is on Verizon's LTE network. I havn't tried any other cellular network. When using Wi-Fi only, not necessarily the same wi-fi, it works fine.
This happens to me too. It appears that while GKMatchSendDataReliable is oodles more reliable than GKMatchSendDataUnreliable (which loses about 2% of packets in my tests), GKMatchSendDataReliable seems to occassionally lose the first packet I send (immediately after connecting).
My users also complain that some data may be accidentally lost during the game. I wrote a test app and figured out that GKMatchSendDataReliable is not really reliable. On weak internet connection (e.g. EDGE) some packets are regularly lost without any error from the Game Center API.
So the only option is to add an extra transport layer for truly reliable delivery.
I wrote a simple lib for this purpose: RoUTP. It saves all sent messages until acknowledgement for each received, resends lost and buffers received messages in case of broken sequence.
In my tests combination "RoUTP + GKMatchSendDataUnreliable" works even beter than "RoUTP + GKMatchSendDataReliable" (and of course better than pure GKMatchSendDataReliable which is not really reliable).
Apple stated that this was a bug and fixed in iOS7

How to get precise clock synchronisation when doing peer to peer with GKSession?

I made a simple game which connects to other peers using GKSession from GameKit. It was easy to set up but I discovered some problems:
Latency varies a lot. Sometimes message arrives instantly on other devices. Sometimes latency is > 1 second for sending data to other peers. Data is only a string with 10 chars.
My game depends on precise clock synchronisation. The game has music playing and it sounds odd when it doesn't start playing the same time on all connected devices.
I couldn't find anything in the documentation about how to snychroize timing. The problem is that the "master" pier which starts the game immediately begins playing the music and then all other piers receive the message a little bit later and thus start playing after delay.
Then I tried to delay playing the game music after sending the start message to all piers, but sometimes latency is lower or higher and I just can't get a good reliable sync.
Is there a open source framework which makes peer to peer and clock sync more reliable and easier to use on iOS?
Unfortunately, you're in for a world of hurt here. It is very difficult to do precise clock synchronization over a network interface. You would need your devices to sync to within 20ms here.
I would recommend doing the following: send a bunch of ping packets at 50ms intervals, make the client respond immediately. Take the average of this to give you average round trip time (RTT) and then halve that to estimate the one-way latency. Then send your 'start clock' message and spin in a loop until you've waited for your one-way latency and begin playing the music on your end. The client should play its music immediately upon receiving 'start clock'.
There is a more robust long-term sync solution outlined here: http://en.wikipedia.org/wiki/Precision_Time_Protocol

Gamekit Latency

I have the following problem with GKSessions:
Sometimes there is a huge delay when sending data (3-4 seconds sometimes up to 10 seconds) in a client/server application.
If the server sends let's say 10 packets during the delay, those 10 packets are received all at once on the client. The weird thing is that the server still receives packets from the clients during the delay.
This delay seems to be worse in WiFi networks but also happens in bluetooth networks.
Did anyone else encounter such delay spikes when using gamekit?
What could be the source of this issue?
I've been working a lot with gamekit and gksession. I always use the Peer2Peer mode even though some people discourage it. I never experience any kind of delay like the one you describe.
Do you send with GKSendDataReliable or GKSendDataUnreliable? Try switching to the other and see if it changes anything. If you're using GKSendDataReliable then the sending device will wait for a "Received" message from the receiver before sending the next message. This might be the problem.
On your testing devices: Is anything running which might flood the network?
I was testing a multiplayer gamekit based game, and if I had several active devices then suddenly some would stall completely, and I needed to hard reboot (shut down completely and restart) them to make them work again.
If you keep getting in trouble you might want to try a much more low level api: dns-sd https://developer.apple.com/library/mac/#documentation/Networking/Conceptual/dns_discovery_api/Introduction.html
I hope you make it work, good luck!

Resources