best way to sync data with html5 video - javascript

I am building an application that takes data from an android app and replays it in a browser. The android app basically allows the user to record a video and while it is recording it logs data every 100ms such as gps position, speed and accelerometer readings to a database. So i want the user to be able to play the video back in their browser and have charts, google map etc show a realtime representation of the data synced to the video. I have already achieved this functionality but it's far from perfect and I can't help thinking there must be a better way. What I am doing at the moment is getting all of the data from the database ordered by datetime ascending and outputting it as a json encoded array. Here is the process I am doing in pseudo code:
Use video event listener to find the current datetime of video
do a while loop from the current item in the data array
For each iteration check whether the datetime for that row is less than the current datetime from the video
If it is then update the dials from the data
Increment array key
Here is my code:
var points = <?php echo json_encode($gps); ?>;
var start_time = <?php echo $gps[0]->milli; ?>;
var current_time = start_time;
$(document).ready(function()
{
top_speed = 240;
min_angle = -210;
max_angle = 30;
total_angle = 0 - ((0-max_angle)+min_angle);
multiplier = top_speed / total_angle;
speed_i=0;
video.addEventListener('timeupdate', function() {
current_time = start_time + parseInt((video.currentTime * 1000).toFixed(0));
while(typeof points[speed_i] !== 'undefined' && current_time > points[speed_i].milli)
{
newpos = new google.maps.LatLng(points[speed_i].latitude, points[speed_i].longitude);
marker.setPosition(newpos);
map.setCenter(newpos);
angle = min_angle + (points[speed_i].speed * multiplier);
$("#needle").rotate({
animateTo : angle,
center: [13,11],
duration: 100
});
speed_i++;
}
}
});
Here are the issues I seem to have encountered:
- Have to load thousands of rows into json array which can't be very good for permorance
- Have to do while loop on every video call back - again can't be very good for performance
- Playback is always a bit behind
Can anyone think of any ways this can be improved or a better way completely to do it?

There are a few reasons why this may be running slowly. First, the timeupdate event only runs about every 250ms. So, if you're going to refresh at that rate, dandavis is right and you don't need that much data. But if you want animation that's that smooth, I suggest using requestAnimationFrame to update every 16ms or so.
As it is, if you update every 250ms, you're cycling through 2 or 3 data points and updating the map and needle three times in a row, which is unnecessary.
I recommend looking into Popcorn.js, which is built exactly for this kind of thing and will take care of this for you. It will also handle seeking or playing backwards. You'll want to pre-process the data so each point has a start time and an end time in the video.
There are also some things you can do to make the data transfer more efficient. Take out any extra properties that you don't need on every point. You can store each data point as an array, so the property names don't have to be included in your JSON blob, and then you can clean that up with a few lines of JS code on the client side.
Finally, separate your data file from your script. Save it as a static JSON file (maybe even gzipped if your server configuration can handle it) and fetch it with XMLHttpRequest. That way, you can at least display the page sooner while waiting for the code to download. Better yet, look into using a JSON streaming tool like Oboe.js to start displaying data points even before the whole file is loaded.

Related

How to measure graphics performance while drawing in JS?

I have a relatively simple website (small browser game) that does not perform well. Its graphic part is flickering and I'd like to measure how long it takes to draw different elements.
I draw in the method triggered by
window.requestAnimationFrame(() => this.drawAll());
To measure time, I've added to my drawing functions elements that capture the current time:
let t0 = new Date().getTime();
...
let t1 = new Date().getTime();
...
let t2 = new Date().getTime();
The intent was to display the delta between those numbers in the console, but the problem is that if you do it in the real-time console just freezes.
The idea I have is to accumulate time information and display it once a second (or for every 100 refresh cycles), but I'm wondering if there is any better way to do so?
Thanks!
EDIT: Idea to accumulate information did not work well: each part I'm concerned about is rendered decently fast and new Date().getTime() only counts milliseconds. The alternative is to use 'performance.now()' - it counts fractions of ms.
I hate leaving questions without the answer.
Due to lack of better responses, I'll convert 'EDIT' part of the question to an answer:
I did end up with a few improvements to the idea:
Use 'performance.now()' instead of 'new Date().getTime()' to capture time
Don't output time snippets to console on every render event, but instead accumulate the duration of events in static variables as well as count the number of render events. When the counter reaches 100 (or any number that can help reasonably indicate performance difference) output accumulated data to console.
something like this:
static performanceEventCounter=0;
static duration1=0;
static duration2=0;
render() {
let t0=performance.now();
...
let t1=performance.now();
...
let t2=performance.now();
duration1+=t1-t0;
duration2+=t2-t1;
if(++performanceEventCounter>100) {
console.log('duration1=${duration1}, duration2=${duration2}');
performanceEventCounter=0;
duration1=0;
duration2=0;
}

Smoothie Charts real-time high speed data

I've got a high-speed data output from a C++ script, an int is produced every 800 microseconds(~1.6kHz), I've played around with this running with websocketd to stream it live to a smoothie chart on a web server on the same machine. I've found the best way to make the chart as smooth as possible is to output an array of 100 datapoints to the websocket. Inside my smoothie javascript, it splits the array and then adds the data with arbitrarily spread X values between the last array and this array.
'
conn.onmessage = function(evt) {
$("#log").text("Connected");
var arrZ = evt.data.split(',');
newTime = new Date().getTime();
var timeInterval = (newTime - oldTime)*0.01
for (i=0;i<100;i++){
timeSeries.append(oldTime, arrZ[i]);
oldTime += timeInterval;
}
oldTime = new Date().getTime();};
'
The data plot is not fantastic but works. Is there any other(faster) way - architecture wise - to get this data on to smoothiecharts?
SmoothieCharts Charting high speed data
Thanks,
The problem is that underneath, you're using TCP/IP packets. And with just a few bits of data on every update, these packets aren't immediately full. Your OS has the reasonable expectation that waiting a bit will improve bandwidth, as it can send the same amount of raw data with fewer packet headers.
However, this backfires for you. You care about latency, not data. The easiest solution is to stuff dummy data in your packets. Each value, once formatted and padded should be ~1500 bytes.

How to make fps run at the same pace across different clients

Both of the server and client have the code
setInterval(function(){
player.x++;
player.y++;
}, 20);
I have a node.js / socket.io server where the player's x and y are being updated every frame and sent to the client. The client is also having a different x and y being updated using the same logic. The client seems to be moving faster than the server though so it's making the game not in sync.. What should I be doing differently?
that's a huge and kinda complex topic, I'm not in the mood to google these topics for you; but a few hints/directions.
This ain't meant to be a full answer, but it's more explanation, a comment can take.
1st: don't increment values, neither in the back- nor in the frontend.
Define them as an animation: { startTime, startValue, endTime, endValue }. Now you can interpolate the current position at any time, independant of the interval.
2nd: you have to account for the time-offset between front- and backend.
3rd: check in the frontend for manipulations of the timer: this is a common way to cheat. Don't take the correctness of the clock for granted.
4th: you also have to account for the ping, when recieving actions from the frontend for example, you should subtract the ping to know when the action has really been made. And don't forget to check the ping over and over again: it might change over time.
And again, you can only trust your own clock, and you should check for manipulations (eg. artificially slowing down the connection). google ping-cheater, ...
finally: You're not the first one to create a real-time game. Search for these topics, they have been solved, and they have been asked by other people in the past.
Search especially in the flash-community. Flash still is a popular gaming-platform, and AS3-code should be pretty easy to understand and to port to JS.
It was actually really really easy, anyone with the same problem just read this article. https://www.viget.com/articles/time-based-animation
The simplest way to put it "move based on how much time has passed since you've last moved" So just find a deltaTime variable by subtracting time_now from time_then.
This is what my updatePosition function looks like now
self.updatePosition = function(){
var now_time = (new Date).getTime();
var delta_time = now_time - self.then_time;
self.x += delta_time * self.velocityX;
self.y += delta_time * self.velocityY;
self.then_time = now_time;
}
Just set the then_time in your initialization or whenever your character can first start moving.

Inconsistent Timestamp in JavaScript?

I'm running into some strange error with the Date.now() function in JS.
Background Info: I'm developing a webApp that sends and receives packets to/from a server. All my incoming packets are marked with a timestamp, generated by Date.now(). Among these packets is data, that is printed into a line chart. So I get a value on the y axis and the timestamp on the x axis. So far so good.
Letting the app run has shown me some strange behaviour -> My line chart sometimes draws data points back in the past, right before already present data points! While checking the timestamps of these points i saw, that my chart does everything right, but the timestamps are wrong.
So I wrote a little test script, to collect some more evidence:
var i = 0;
var ts = [Date.now()];
setInterval(function () {
ts.push(Date.now());
console.info("checking next timestamp ...");
if (ts[i] > ts[i+1]) {
console.error("old timestamp (" + ts[i] + ") is BIGGER than current timestamp (" + ts[i+1] + ")!");
}
i++;
}, 100);
Running this over a few minutes prints me one error:
Way later i get another one, and so on.
For this test i chose 100ms interval time to check many timestamps. But my App has an update interval of maybe 5s and still there arrives this error sometimes. Not as often as with 100ms, but it's there :(
Has anyone else seen this behaviour before? Am I doing something wrong here?
BTW: The packets in my App come in one by one, so it's impossible that the order of the packets is mixed up or sth.
Well, as I have taken a look onto the Performance API yesterday, I think this is the way to go. I implemented it in my application as follows:
Everytime when I receive an input packet from my server I give them a timestamp. Until now I had realised this by Date.now() but this seems to be inconsistent as the OS or the VM (or both) recalculate their current time from time to time.
So now I calculate my own timestamp. For this I have to read the timestamp from when my application started. The performance API gives me this value by
var startTS = window.performance.timing.navigationStart; //returns a timestamp in ms
So this is a value available everywhere in my app since it exists in the window namespace and does not need a global variable made by myself. Very nice.
Now, to get the current timestamp, I have to add a time value that indicates how long my application is running since startTS. To get this we just use:
var timeSinceStartup = window.performance.now(); //returns sth. like 1337.9836718 (ms with fractional part)
If needed, one can round this value as the fractional part might not be needed:
var rounded = (timeSinceStartup + 0.5) | 0; //same as but faster than Math.round(timeSinceStartup);
Well, the rest is easy. We just have to add those values (as both are milliseconds) and voila:
var currentTS = startTS + timeSinceStartup;
I have updated the little code snippet from my question to test this:
var i = 0;
var start = window.performance.timing.navigationStart;
var ts = [start + window.performance.now()];
setInterval(function () {
ts.push(start + window.performance.now());
console.info("checking next timestamp ...");
if (ts[i] > ts[i+1]) {
console.error("old timestamp (" + ts[i] + ") is BIGGER than current timestamp (" + ts[i+1] + ")!");
}
i++;
}, 100);
Chrome is running this test at the moment (in VM) and it looks like everything is just fine (17500 checks without an error). I would have wondered if sth. else would happen, as we just add a constant with a monoton rising value ;)
Only drawback of this solution seems to be the browser support. As of now every modern browser but Safari support this. As my app is developed to run only on modern browsers this is ok for me, but the lack of Safari is bad.
http://caniuse.com/#feat=high-resolution-time
I will have to take a look into crossbrowser solutions/polyfills/Frameworks or sth. Thanks everybody for your help. Anyway I'm still curious if there might be a solution for Date.now() in Chrome, as FF and IE had no problems with using it.
https://code.google.com/p/chromium/issues/detail?id=408077
EDIT
This seems to be a good polyfill for a missing Performance API:
https://gist.github.com/paulirish/5438650
Ofcourse it falls back to Date.now(), so in browsers that don't support the API I might run into the same problem as before. For this case I just keep checking my timestamps in the app and if a timing error occurs I'm going to handle it silently (e.g. don't draw it to the chart, instead wait for the next value).
So long...

What is the correct way to send position updates via WebSockets?

I need to broadcast position updates in a game-world running with node.js. My current idea is to use BinaryJS. I'm not sure what is the correct way to use it, though. I have thought in 3 options:
How I'm doing currently, using socket.io: I programmatically store a buffer of position-updates that happened in a given amount of time and then send it all at once:
setTimeout(function(){
//called a fixed amount of times per second (40)
//this variable will hold the accumulated position changes on the last 25ms
//var accumulated_position_changes = [id0,x0,y0,z0, id1,x1,y1,z1...];
client.send(accumulated_position_changes);
},25);
But if binary.js does the chunking by itself (does it?), then should I just indiscriminately send position changes every time they happen?
MyClass.prototype.set_position = function(x,y,z){
// this is called thousands times a second
this.x = x, this.y = y, this.z = z;
client.send([this.id, x, y, z]);
};
Or should I somehow create an object deriving node.js's stream and use it? Maybe with stream.write? Or some way else? What is the right way to deal with this problem?
Binary.JS seems to write a packet every time you write to the pipe, in my use cases.
Why not write the position changes to a buffer, and then use Underscore.js' throttling. You could call update() or whatever, which writes the whole buffer of changes all at once. You can call that function as often as you want, but use the throttled version so that it only actually runs at a maximum of every 50ms or so.
That delay is incredibly fast for web applications. Be sure you are considering the rate at which your clients can receive and ACK that info. Honestly, I wouldn't update the clients until the data has successfully been sent and ACKed. Otherwise, you will have way too high of an update rate for some.

Categories

Resources