I am building a web app using Vue and Socket.io.
The app is divided into two: A Host (Hosting a party) and the guests.
It's a party game and the people participating will be guidede to do stuff simultaneously so syncing between devices is crucial.
Every body (including the host) will be asked to download an audio file. Once everybody has downloaded the audio file the Host can then start the playback on all devices using socket.io.
The Host emits an event "startAllAudio" to the socket.
socket.emit("startAllAudio");
When the socket gets the event it emits a new event "startAudio" with a timestamp set 3 seconds ahead in time.
const startDelay = 3000;
var now = new Date().getTime();
io.emit("startAudio", {
timeToStart: now + parseInt(startDelay),
});
All this works like a charm on iOs devices. But on android it does not. After debugging a lot I found that the internal clock on the Android devices is out of sync with (Or at least not the same as) the iOs devices. I have two different relatively new Android phones for testing and even they are not in sync with each other. I have 10+ various iOs devices for testing and the all sync perfectly.
I made a javascript clock with milliseconds and put it on the web app for debugging. And you can clearly see that the two Android phones are different from each other and from the iOs devices which are the same (within reason). The difference in time matches the audible difference in playback.
var interval = window.setInterval(() => {
const date = new Date(Date.now());
this.time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + ":" + date.getMilliseconds();
}, 500);
So finally we get to the actual question(s):
How would you go about fixing that?
Can you force the android devices to timesync? Maybe even to a specific time server?
I thought about "probing" the responce time between the web app and the socket.
i.e. sending 3 emits and time the respons time and the take an average of that and use it with the timestamp on the Android phones to adjust the timeToStart value sendt from the socket. But it does not seem like a bulletproof method.
Any suggestions would be greatly appreciated.
Related
I am a react-native developer and I want to create an application for the Students and Motto of the application is give peaceful music (Need to play on Bluetooth) from the application, and for this audio I want to maintain the frequency (Means as of now when I play those audio files from the various device it's getting me different output but I want same output from the all the various devices)
I have tried to find the solution but didn't get anything useful, So it will be good if anyone can help me with this point
You could consider mqtt (https://github.com/SudoPlz/sp-react-native-mqtt), ntp (https://github.com/luneo7/react-native-ntp-sync) and react-native sound libraries (https://github.com/zmxv/react-native-sound#readme).
With an mqtt broker (you can get free ones with hivemq or mosquitto) and the mqtt library installed in all your devices, you can create a channel/group where all devices will be connected and listening every message in this channel/group.
You will need to set a master device.
The shared messages will be the playback instructions eg:
Instruction 1: Load in buffer the song with url ... {instruction: 'buffer', songURL: 'https://<song_url>'}
Instruction 2: When every device has loaded, send a ready status {instruction: 'ready', deviceId: '<device_id>'}
The master device will be counting all the ready instructions until 100% of devices are ready to play.
Instruction 3: The master device send the play instruction for all devices with the start date using ntp {instruction: 'play', startDate: <timestamp>}. Every device on the channel receives the instruction and calculates the delta between the star date and the local date from ntp and you can do this diff with moment. With this delta in seconds and the second's length of the song (you can get it with react-native-sound library API) set the time of play with setCurrentTime to the track and play()
You can send periodical instructions with the getCurrentTime of every device to make more delta comparisons and set the corresponding time in every period.
Hope it helps and glad to hear from you if it worked.
It looks like there's a small difference (milliseconds) when getting the current time in iOS and Android.
Swift: Date().toMillis()
Kotlin: System.currentTimeMillis()
Check the following example (left is iOS, right is Android):
The current timestamp in iOS (first message sent) is 1595158104237, while the current timestamp in Android (second message sent) is 1595158098388, with the Android one being 5849ms earlier than the iOS one even if it was sent later.
Is that something that can only happen in emulators, or also in real devices?
Is there a way to get the "same" current timestamp on both systems?
Because it local device timestamp, if you change system date in the device it will change the timestamp as well
If you want, you can get timestamp from a server so it will be same for all device and OS
I'm creating an android application that interfaces with the texas instruments sensortag. One of the things the app needs to do is be able to change the frequency in which the temperature is reported to the app. I am able to change it through the official TI app which is great, but I cannot seem to get it working in my app.
When viewing the official app (iOS, can't run the android one?), it shows the temperature GATT service, which contains 3 characteristics. When I inspect the characteristics discovered by my app however, it only seems to find two - the data, and the notifications. Not the interval. I have attempted to construct this characteristic myself and write it however it doesn't do anything - no error, no success, just nothing.
The steps I've taken are essentially:
bluetoothGatt.discoverServices();
...
services = bluetoothGatt.getServices();
...
BluetoothGattService service = bluetoothGatt.getService(serviceUUID);
System.out.println("Characteristic = " + service.getCharacteristic(SensorTagGatt.UUID_IRT_PERI));
The output yields null. Is there something obvious I'm missing or that I should be doing that I might not be?
EDIT:
I've installed another app onto the phone written by another developer, and using this to inspect the services and characteristics available shows that it too is unable to find it, so I'm assuming there is something wrong with the android service discovery? The official iOS app is working as expected, and showing all characteristics. Unfortunately, the official android app seems to be incompatible with the version 1.5 firmware and crashes when trying to connect but I assume it too will fail to find the characteristic.
Has anyone else run into this issue and if so been able to get around it?
I have 2 android phones phones, both connected to the same wifi, both with bluetooth.
I want some method that syncs somehow the phones and starts a function on the same time on both phones.
For example playing a song at the same time.
I already tried with bluetooth but its with lag, sometimes 0.5 secs. I want something in +- 0.01sec if possible.
Someone suggesting playing it in the future with 2-3 seconds, sending the time-stamp, but how do you sync the internal clocks of the devices then ?
Before calling that particular method, try to measure the latency between the two devices:
1.First device says Hi(store the current time)
2.Second device receives the Hi.
3.Second device says back Hi !!
4.First device receives the Hi.((storedTime - currentTime) / 2 )
Now you have the latency, send your request to second device to start your particular method and start it on first one after the latency.
Try to measure the latency 5 to 10 times to be more accurate.
you have a way to transfer data between the devices right ?
if so you can send a time-stamp which is in the future,
ex: if the present time stamp is 1421242326 you send 1421242329 or something and start the function at that time on both devices.
Basically use #Dula's suggestion (device 1 sends command to device 2 and gives a "start time" which lies in the future). Both devices then start the action at the same time (in the future).
To make sure that the devices are synchronized, you can use a server-based time sync (assuming that both devices have Internet access). To do this, each device contacts the same server (using NTP, or HTTP-based NTP, or contacts a known HTTP server, like www.google.com and uses the value in the "Date" header of the HTTP response). The "server-date" is compared to the system clock on the device, and the difference is the "time-offset from server-time". The time-offsets can be used to synchronize on the "server-time", which is then used as the time base for the actual action (playing the media, etc.).
If your WiFi router allows clients to talk to each other (many public hotspots disable this), you could implement a simple socket listener on one (or each) device and have the initiating device broadcast a message.
For more complicated things and network flexibility, I've had good success with connected sessions using AllJoin. There is a bit of a learning curve to do interesting things, but the simple stuff is pretty easy once you understand the architecture.
Use a server to provide a synchronous event to just the two clients who have decclared their mutual affinity (random as a parm and pair serializer Partner-1 or Partner-2 which they share prior to their respectve calls for the sync event).
Assume both clients on same subnet (packets from 2 events serialized on the server , arrive across the network at the 2 clients simultaneously client-side) This provides synchronous PLays by 2 , bound clients.
The event delivered by server is either a confirm to play queued selected track OR a broadcast( decoupled, more formal)
The only tricky thing is the server side algorythm implementing this:
Queue a pair of requests or error
Part1, part2 with same Random value constitute valid pair if both received before either times out.
On a valid pair schedule both to the same future event in their respective , committed responses.
OnSchedule do the actual IO for 2 paired requests. Respective packets will arrive back at respective clients at same time, each response having been subject to equal network latency
Ng if two diff carrier 4G or lte networks involved. (Oops)
This thing is possible via socket, you will send a event via socket then the other device receive that event. For learn socket io chat
maybe it's not the answer you are looking for but i think that due to the high precision you are wanting , you should look for a push technology, i advice you to take look at SignalR. It's real time technology which gives you abstraction of sending methods , it have a built-in methods like Clients.All.Broadcast that fit your needs.
You can try to use some MQTT framework to send message between two device, or into a set with more number of devices.
I'm trying to implement an HTML5 app that will work on desktop, android, and iOS. The app's main function is to wirelessly send commands to the server about what scripts to run and to receive regular messages pushed from that server about the status of those scripts.
The server is running nodejs and I decided to use Server Sent Events to do the push notifications. This requires that I use Firefox on Android since the native Android browser doesn't support SSE.
My implementation all works fine: the node server is publishing the events as it should and my client-side HTML5/javascript is picking it up fine on desktop chrome/firefox/safari, on my iPod iOS6, and my Android 2.2 phone.
However, there are 4 common situations that I need to handle:
the device loses its wi-fi signal
nodejs server crash (hopefully this isn't common!)
put browser into the background on iPod/Android while browsing another app, etc.
lock screen on iPod/Android while browser is running
Chrome/Safari behave perfectly on both desktop and iPod, as follows: if the server crashes, the site automatically reconnects and gets the pushed messages as soon as the server is up again, and if the browser app goes into the background for whatever reason, it is still getting those messages while in the background, or at the very least automatically reconnects as soon as it comes back into the foreground.
Firefox, however, both on desktop and on Android, is all too eager to close down that EventSource connection permanently. As soon as the browser loses connection to the server, either from server crash, from putting the firefox app into the background or from locking the screen, the EventSource connection is killed and does not ever try to reconnect. Of course, you can just reload the page when you come back to it, but this is annoying, especially in the case where you need to lock the screen because you need to put the phone in your pocket (this app needs to be used in some trekking situations).
Can anyone recommend any solution for this aside from just having to reload the page in Android Firefox all the time? Below is my dummy implementation, just sending random numbers every 5 seconds.
Server at /main/src
src : function(req, res) {
req.socket.setTimeout(Infinity);
// send headers for event-stream connection
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
res.write('\n');
var messageCount = 0;
var process;
function printEvent() {
messageCount++;
rand = Math.floor((Math.random()*10000)+1);
res.write('id: ' + messageCount + '\n');
res.write("data: " + rand + '\n\n');
process = setTimeout(printEvent, 5000);
}
printEvent();
res.socket.on('close', function () {
res.end();
clearTimeout(process);
});
}
Client
var source = new EventSource('/main/src');
source.onopen = function(e){
$("#test").html("Connected to server. Waiting for data...");
}
source.onmessage = function(e){
$("#test").html("MSG: " + e.data);
}
source.onerror = function(e){
var txt;
switch(e.target.readyState){
case EventSource.CONNECTING:
txt = 'Reconnecting...';
break;
case EventSource.CLOSED:
txt = 'Connection failed. Will not retry.';
break;
}
$("#test").html("Error: " + txt);
}
Thanks!!
i know only one solution, that is already used in many libs:
"heartbeat" messages - on the client side you may check, if the "random number" is not received from the server in 10 seconds (5000 seconds + some lag) - the connection is seems to be broken and you should do the reconnection
(with native EventSource you may use "close" method, than create new EventSource OR you can try https://github.com/Yaffle/EventSource )