I need to catch the exact moment when HTML5 audio starts producing sound.
It turns out not so simple as it seems.
You might expect audio starts playing when onplay
or onplaying
event is fired? No way. At least in WebKit family, it seems to be no browser event that fires exactly at this point of time. In Chrome, Safari and Firefox onplay
and onplaying
events are just faking their behaviour by simply firing together with oncanplay
!
I've prepared a simple test to prove that fact. It demonstrates that audio actually starts playing after some reasonable time (over 100ms - 400ms) when all the events had already been fired.
You can notice this by your ears and ears if you look at console log. In the log I output currentTime
every 15ms. It seems to reflect the actual audio state correctly, and it starts changing 10-40 polls after any event has been fired. So the audio is still freezed after play
is fired.
Test code looks like this:
var audioEl = new Audio('http://www.w3schools.com/tags/horse.ogg');
audioEl.oncanplay = function () {
console.log('oncanplay');
audioEl.currentTime = 1;
console.log('ready state is: ' + audioEl.readyState);
audioEl.play();
}
audioEl.oncanplay = function () {
console.log('oncanplay again');
}
audioEl.onplay = function() {
console.log('onplay -----------------');
}
audioEl.onplaying = function() {
console.log('onplaying ----------------');
}
setInterval(function () {
console.log(audioEl.currentTime);
}, 15);
JsFiddle
I critically need to know the exact moment when the audio starts playing for precise synchronisation with visual animations.
Of course, I can find this moment roughly using quick polling. This is very bad for performance in real-time app, especially on mobile.
I'm asking if anyone knows any better solution for this. HTML audio implementation looks to be still so poor in 2014 :(
As @justin says, you can listen for the
playing
event to get the (more or less) precise moment the media starts actually playing. But yeah I've been seeing some spotty support for media events andreadyState
in general, even in latest Chrome.Whether those events work or not, I advise against using
setInterval
for animation (or just about anything else, for that matter). Instead, userequestAnimationFrame
, which will fire more often and should synchronize with the browser and video card's repaint. And you can poll for thecurrentTime
value on every frame to calculate where you should be in your animation. Performance shouldn't be a problem; your case is exactly whatrequestAnimationFrame
was designed for.While you're at it, don't set
currentTime
to 5 until afterreadyState > 0
or theloadedmetadata
event fires. If you try to setcurrentTime
before the browser has loaded enough of the video file to know the duration, it will throw an error. But you can callplay()
whenever you want; it doesn't have to wait forcanplay
.