I am trying to track how long a viewer has viewed a certain video and I'm a bit confused as to what some of the media events do in React JS.
When a video is skipped to a later part of the video and stops for a bit does it call onPause without actually needing to press the pause button?
Also I am confused as to what onTimeUpdate does.
Please take a look at the HTML Audio/Video Event Listeners reference here:
https://www.w3schools.com/tags/ref_av_dom.asp
You'll probably need the 'readystate' property :
Represents the ready state of the audio/video element:
0 = HAVE_NOTHING - no information whether or not the audio/video is
ready
1 = HAVE_METADATA - metadata for the audio/video is ready
2 = HAVE_CURRENT_DATA - data for the current playback position is
available, but not enough data to play next frame/millisecond
3 = HAVE_FUTURE_DATA - data for the current and at least the next
frame is available
4 = HAVE_ENOUGH_DATA - enough data available to start playing
So, I would imagine you use a counter on a setTimeout to count how long the video has been played for:
var counter = 0;
var timer = setTimeout(function() {
var vid = document.getElementById("myVideo");
var state = vid.readystate;
if( state != 0 ) counter++;
console.log( counter + " seconds its been played" );
} , 1000 );
I am experimenting with interactive audio applications in HTML, and I would like to be able to seamlessly start playing one audio file just as another audio file reaches a particular playback location; for example, if audio file A is playing, then I would like to receive an event when it reaches 16 seconds in exactly.
I have tried using audio.play(); setTimeout(myCallback, 16000); for this, but I am finding it to be incredibly unstable in some browsers (Safari especially, particularly when the window is in the background), and will sometimes fire slightly early, and other times fire very late. This remains the case even if I explicitly stop, rewind, and .load() the upcoming audio segment when scheduling the callback.
My current (simple) looper that simply toggles between two players every 16 seconds is below:
audio = []
for (var i = 0; i < 2; i++) {
audio[i] = new Audio("mew" + i + ".mp3");
}
var which = 0;
var pump = function() {
audio[which].play();
which = (which + 1) % audio.length;
window.setTimeout(pump, 16000);
}
audio[which].addEventListener('canplaythrough', function() {
pump();
});
for (i in audio) {
audio[i].load();
}
Also, note that while in this case I could use setInterval(), that will not always be the case (and anyway, setInterval() suffers from the same stability problem).
I implemented a simple countdown timer using window.setInterval. It works perfectly in my desktop browser but it does not work correctly on my smartphone (Fairphone 2) as a PhoneGap/Cordova app. According to my examinations and my research on the internet the interval/timeout is interrupted when the phone is sent to sleep/standby. That's why it does not work.
Astonishingly the interval/timeout is not interrupted when my phone is connected via usb cable to my computer. So probably it's an energy-saving feature that's causing the bad behaviour.
So, I'm lost. I don't know how to implement my simple countdown timer which of course should also work when the phone sleeps (=display turned off). Is there an alternative for window.setInterval() / window.setTimeout() ?
Here is my simple code (as stated: window.setTimeout does not work, either):
...
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/libs/jquery.js"></script>
<script>
var min = 25;
$(document).ready(function(){
intervalID = window.setInterval(function () {
--min;
if (min > 0) {
$("#countdown").text(min);
}
}, 6000);
});
</script>
...
<p id="countdown">0m</p>
Use the interval timer only for updating the display. Use the system time to decide what to display.
Then, if your interval doesn't get called when the display is not visible, this is no problem. The next time it does get called (when the display is turned on again), it will calculate the correct elapsed time from the system time and it will display correctly.
For this reason (and several others), you should never assume that setInterval() is going to keep perfect time. In Javascript, it just means you want to be called every once in a while and you get to set an approximate time to specify the frequency, but the interval may be shut off for long periods of time.
Get the time when your interval starts with Date.now() and then each time the interval fires, get the new system time and subtract from the start time to see how much time has elapsed and then calculate what you want to display.
If you want to show minutes remaining on a 25 minute timer, you could do this:
function showTimer(selector, minutes) {
var startTime = Date.now();
var interval;
function showRemaining() {
var delta = Date.now() - startTime; // milliseconds
var deltaMinutes = delta / (1000 * 60);
if (deltaMinutes < minutes) {
// display minutes remaining
$(selector).text(Math.round(minutes - deltaMinutes));
} else {
$(selector).text(0);
clearInterval(interval);
}
}
interval = setInterval(showRemaining, 15 * 1000);
showRemaining();
}
$(document).ready(function(){
showTimer("#countdown", 25);
});
Working demo: https://jsfiddle.net/jfriend00/807z860p/
I found a cordova plugin which executes timeouts/intervals even when the display is turned off:
cordova-plugin-timers (on github)
The answer of jfriend00 was also helpful.
The Problem
I need to be able to synchronize some JavaScript events to specific timing in YouTube videos as closely as possible. While I understand there are some limitations to how accurate timers can be in browsers, I think it should be possible to do better than what I'm getting from the video player. I used the following code on starting playback of the YouTube video.
startTime = new Date();
setInterval(function () {
samples.push({player: player.getCurrentTime(), jstime: new Date() - startTime});
}, 20);
This code gets the current time that the video player thinks it is at with player.getCurrentTime(), and logs it along with regular clock time against the time that playback was started. The following results give you an idea of the accuracy:
{"player":0.188,"jstime":109},
{"player":0.676,"jstime":125},
{"player":0.676,"jstime":140},
{"player":0.676,"jstime":171},
{"player":0.676,"jstime":203},
{"player":0.676,"jstime":218},
{"player":0.676,"jstime":234},
{"player":0.676,"jstime":265},
{"player":0.676,"jstime":296},
{"player":0.676,"jstime":312},
{"player":0.676,"jstime":327},
{"player":0.676,"jstime":577},
{"player":1.012,"jstime":624},
{"player":1.187,"jstime":655},
{"player":1.187,"jstime":671},
{"player":1.187,"jstime":686},
{"player":1.187,"jstime":717},
{"player":1.187,"jstime":733},
{"player":1.187,"jstime":749},
{"player":1.187,"jstime":780},
{"player":1.187,"jstime":811},
{"player":1.447,"jstime":842},
{"player":1.447,"jstime":858},
{"player":1.447,"jstime":873},
{"player":1.447,"jstime":905},
{"player":1.447,"jstime":936},
{"player":1.447,"jstime":951},
{"player":1.447,"jstime":998},
{"player":1.605,"jstime":1029},
{"player":1.605,"jstime":1061},
Some digging online reveals that the accuracy of the YouTube video timer comes directly from the underlying player (which will usually be HTML5 in my case), and should not be relied upon for anything more accurate than a few hundred milliseconds.
A Possible Solution
A few observations/assumptions (and some stating the obvious):
The time retrieved from player.getCurrentTime() will be approximating something constant.
The rate at which player time passes should be nearly the same for the clock-on-the-wall. (It may drift slightly though due to the fact that video frames are usually tied to an audio clock which always varies from machine to machine by a few Hz.)
If I observe both clocks over time, I should be able to determine the difference in rate between them (which should be close to 0).
Once the error rates are known and samples are taken over time, it should be possible to derive a timer that is close to the accuracy of the most accurate timer of the two (the clock provided to JavaScript). Is this assumption correct?
How to implement?
Given the inputs of player time and JavaScript clock-on-the-wall time, how can I derive a timer that I can call every animation frame to give me the highest accuracy possible?
How accurate could such a derived time be?
I've done this sort of thing before (admittedly using ActionScript and targeting the AVM) but the principles still stand.
Don't rely on the browser (or app) for timing info.
Think about it, a little bit of buffering on the video side, or the occasional beach-ball / spinning hour glass, and suddenly your synchronisation is wrong.
What you want to do is hook into media player events, and then react to the time of the video.
The W3 HTML5 Video demo page [ http://www.w3.org/2010/05/video/mediaevents.html ] shows a whole bunch of properties and values - the ones I think you'd be wanting to take a look at are the timeUpdate event and the currentTime property.
I think I've figured out how to accomplish this. A bit messy for now, but you get the idea.
function youTubeTimer (player) {
var startTime;
var totalTime = 0;
var lastPlayerTime;
//Array of offsets between the JS timer and reported video player time
var deltas = [];
// Amount of time to correct
var correction;
player.addEventListener('onStateChange', function (e) {
if (e.data === YT.PlayerState.PLAYING) {
startTime = new Date();
} else if (e.data === YT.PlayerState.PAUSED || e.data === YT.PlayerState.BUFFERING || e.data === YT.PlayerState.ENDED) {
totalTime = ((new Date() - startTime)/1000) + totalTime;
}
if (e.data === YT.PlayerState.ENDED) {
console.log(totalTime);
}
});
function getJsTime() {
if (player.getPlayerState() === YT.PlayerState.PLAYING) {
return ((new Date() - startTime)/1000) + totalTime;
} else {
return totalTime;
}
}
// Call this function frequently!
this.getHighResPlayerTime = function getHighResPlayerTime() {
if (!player.getCurrentTime) {
return 0;
}
var playerTime = player.getCurrentTime(); // Seconds of playback, reported by the video player
var jsTime = getJsTime(); // Seconds from the clock time when the video started
// Has the player time been updated? Adjust the correction offset.
if (playerTime !== lastPlayerTime) {
lastPlayerTime = playerTime;
// Store up to 20 samples of offsets
if (deltas.length >= 500) {
deltas.shift();
}
deltas.push(jsTime - playerTime);
// Calculate a new correction value
correction = 0;
for (var x = 0; x < deltas.length; x ++)
{
correction += deltas[x];
}
correction = correction / x;
}
return jsTime - correction;
}
}
So I am working on an interactive HTML5 video player, and currently have everything working while using popcorn.js, but I am now trying to go back and make my player work without being dependent on popcorn.js.
When working with popcorn.js, you can use code like:
popcorn.code((
start: 0,
end: 5,
onStart: function( options ) {
//put code here
}
}}
and your code will be executed when the time of your video spans from 0 through 5. Now, I am trying to have certain blocks of code executed within a certain timeframe of the video, but i can't seem to get it working right and was just wondering if someone can see where i am going about this wrong.
while (myVideo.currentTime >= 0 && myVideo.currentTime <= 5)
{
console.log(myVideo.currentTime);
}
This while loop is also running so fast that it causes the browser to slow down and freeze.
However, if i try using an if loop instead of a while loop, it (obviously) only checks it once.
You could check fewer than the while loop would.
var check = setInterval(function() {
if (myVideo.currentTime >= 5) {
clearInterval(check);
console.log("5 seconds reached");
}
}, 500);
You than can start this again when the user pauses and starts over or if he jumps to another time within the timeline.
Try using the following so your function will run only once every second.
setInterval(function(){
if(myVideo.currentTime >= 0 && myVideo.currentTime <= 5){
console.log(myVideo.currentTime);
}
},1000);
Good luck!