Setting the Granularity of the HTML5 Audio Event 'Timeupdate'

Setting the granularity of the HTML5 audio event 'timeupdate'

I'm sorry, but that's the way it works. From the html5 specs:

Every 15 to 250ms, or whenever the MediaController's media controller position changes, whichever happens least often, the user agent must queue a task to fire a simple event named timeupdate at the MediaController.

Also,

The event thus is not to be fired faster than about 66Hz or slower than 4Hz (assuming the event handlers don't take longer than 250ms to run). User agents are encouraged to vary the frequency of the event based on the system load and the average cost of processing the event each time, so that the UI updates are not any more frequent than the user agent can comfortably handle while decoding the video.

If you read through the specification, you can get the idea that timeupdate event is something of a "best effort" kind of event. It will fire when it can and always as long as it does not to affect performance too much.

You could filter the events discarding some from time to time to smooth the arrival times, but I'm afraid it's not possible to do the opposite.

Html5 Audio timeupdate precision

So I researched my way through this problem and came out with the following findings:

1) HTML Audio is no dice for such a scenario. Its a very limited API and doesn't offer fine grain audio control. Web Audio API is the way to go for this.

2) Web Audio API is not the easiest thing to read/understand and is perhaps weakly implemented across various browsers. Its better to find a nice wrapper around it that fallsback to web audio or flash when Web Audio is not available in a browser.

3) Some wrappers exist like howler.js, sound.js and SoundManager2. Off the three SoundManager2 seems the best fit at first thought.
Demo 4G at http://www.schillmania.com/projects/soundmanager2/demo/api/ effectively solves a very similar problem as in the scenario in the question.

Update: SoundManager2 actually doesn't support the web audio api. From the technotes:

SM2 does not presently use the Webkit Audio API. It may be
experimentally or more formally added at some point in the future,
when the API is more universally-supported.

HTML5: How to get currentTime and duration of Audio Tag in milliseconds

You can use:

<audio id="track" controls>
<source src="your.mp3" type="audio/mpeg">
<source src="your.ogg" type="audio/ogg">
</audio>
<script type="text/javascript">
var audio = document.getElementById('track');
audio.addEventListener('timeupdate',function(){
var currentTimeMs = audio.currentTime*1000;
console.log(currentTimeMs);
},false);
</script>

You can read here for more information on the precision of the timeupdate event. It is dependent on the browser implementation so keep in mind you will get different results from a browser/device to another.

You should use addEventListener method rather than the ontimeupdate property - it is more maintainable.

Also if you need browser coverage it is good to use both ogg and mp3 audio files as sources.

timeupdate Event Function Firing Twice After Rebind

Looks like the problem is this chunk:

$('.goal, .choices').fadeOut(function() {
$('#audio-try_again').off('timeupdate');
setTimeout(generate_objects, 200);
});

The selector $('.goal, .choices') will return a jQuery collection containing two elements, which means the callback function is invoked two times (once per matched element).

A quick and dirty way to overcome this, for your specific application, would be:

$('.goal, .choices').fadeOut(function() {
if ( $(this).is('.goal') ) return;
$('#audio-try_again').off('timeupdate');
setTimeout(generate_objects, 200);
});

Which will, essentially, only execute the stuff inside that callback once.

HTML5 Video run event once if currentTime is larger than X

To achieve this you can use a flag to keep track of the state of the event occurring. By default the flag will be false. It will only be set to true when the currentTime of the video is less than 30 seconds from the end.

You can now use this flag in your if condition so that it will only ever fire once, like this:

var $video = $('video');

var $p = $('p');

var eventFired = false;

$video.get(0).volume = 0.2; // just for testing

function func() {

if (!eventFired && this.currentTime > this.duration - 30) {

eventFired = true;

$p.show();

}

}

$video.on('timeupdate', func);
p { display: none; }

video {

width: 300px;

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>You are less than 30 seconds from the end!</p>

<video autobuffer controls autoplay>

<source id="mp4" src="http://grochtdreis.de/fuer-jsfiddle/video/sintel_trailer-480.mp4" type="video/mp4">

</video>

Is setTimeout function accurate enough to sync with audio timing?

"Accurate enough" is subjective, but in general, no. You don't know when the audio will finish downloading and start playing, or if it will play through without buffering or being paused by the user.

Instead you could use the timeupdate event and currentTime property, to properly sync events to the timeline.

var exampleElement = document.getElementById('example');

var lastTime = 0;

exampleElement.addEventListener('timeupdate', function(e) {

var nowTime = this.currentTime;

//Check if just passed the 1.4 second time mark.

if (nowTime > 1.4 && lastTime < 1.4) {

//Add a message to the debug element.

var logElement = document.getElementById('log');

logElement.textContent += 'Do something at 1.4 seconds\n';

}

lastTime = nowTime;

});
<audio id="example" controls="controls" autoplay="autoplay">

<source src="http://media.w3.org/2010/07/bunny/04-Death_Becomes_Fur.mp4" type='audio/mp4'>

<source src="http://media.w3.org/2010/07/bunny/04-Death_Becomes_Fur.oga" type='audio/ogg; codecs=vorbis'>

</audio>

<pre id="log"></pre><!--debug element-->


Related Topics



Leave a reply



Submit