admin管理员组

文章数量:1406943

I have been trying to create my custom media player using HTML5 and Jquery.

I have followed different approaches and ran into some trouble based on my way of refreshing the page.

First Case

$(document).ready(function(){
    duration = Math.ceil($('audio')[0].duration);
    $('#duration').html(duration);
});

In this case, the duration returns NaN when I redirect the page to the same URL by pressing the ENTER key in the address bar. However, it works pletely fine when I refresh using the reload button or by pressing the F5 button.

Second Case

I read in some answers that loading duration after the loadedmetadataevent might help. So I tried the following:

$(document).ready(function(){
    $('audio').on('loadedmetadata', function(){
        duration = Math.ceil($('audio')[0].duration);
        $('#duration').html(duration);
    });
});

Surprisingly, in this case, the inverse of the first case happened. The duration gets displayed pletely fine in the case of a redirect, i.e., pressing ENTER while in the address bar. However, in the case of refreshing using the F5 button or the reload button, the duration doesn't get displayed at all, not even NaN which led me to believe that the code doesn't get executed at all.


Further reading suggested this might be a bug within the webkit browsers but I couldn't find anything conclusive or helpful.

What could be the cause behind this peculiar behavior? It'd be great if you could explain it along with the solution to this problem.

Edit: I am mainly looking for an explanation behind this difference in behavior. I would like to understand the mechanism behind rendering a page in the case of redirect and refresh.

I have been trying to create my custom media player using HTML5 and Jquery.

I have followed different approaches and ran into some trouble based on my way of refreshing the page.

First Case

$(document).ready(function(){
    duration = Math.ceil($('audio')[0].duration);
    $('#duration').html(duration);
});

In this case, the duration returns NaN when I redirect the page to the same URL by pressing the ENTER key in the address bar. However, it works pletely fine when I refresh using the reload button or by pressing the F5 button.

Second Case

I read in some answers that loading duration after the loadedmetadataevent might help. So I tried the following:

$(document).ready(function(){
    $('audio').on('loadedmetadata', function(){
        duration = Math.ceil($('audio')[0].duration);
        $('#duration').html(duration);
    });
});

Surprisingly, in this case, the inverse of the first case happened. The duration gets displayed pletely fine in the case of a redirect, i.e., pressing ENTER while in the address bar. However, in the case of refreshing using the F5 button or the reload button, the duration doesn't get displayed at all, not even NaN which led me to believe that the code doesn't get executed at all.


Further reading suggested this might be a bug within the webkit browsers but I couldn't find anything conclusive or helpful.

What could be the cause behind this peculiar behavior? It'd be great if you could explain it along with the solution to this problem.

Edit: I am mainly looking for an explanation behind this difference in behavior. I would like to understand the mechanism behind rendering a page in the case of redirect and refresh.

Share edited Jun 12, 2015 at 14:05 Akshay Khetrapal asked Jun 11, 2015 at 0:08 Akshay KhetrapalAkshay Khetrapal 2,6765 gold badges25 silver badges39 bronze badges 0
Add a ment  | 

4 Answers 4

Reset to default 3

It sounds like the problem is that the event handler is set too late, i.e. the audio file has loaded its metadata before the document is even ready.

Try setting the event handler as soon as possible by removing the $(document).ready call:

$('audio').on('loadedmetadata', function(){
    duration = Math.ceil($('audio')[0].duration);
    $('#duration').html(duration);
});

Note that this requires that the <script> tag be after the <audio> tag in the document.

Alternatively, you can tweak your logic slightly, so that the code that updates the duration always runs (but fails gracefully if it gets a NaN):

function updateDuration() {
    var duration = Math.ceil($('audio')[0].duration);
    if (duration)
        $('#duration').html(duration);
}

$(document).ready(function(){
    $('audio').on('loadedmetadata', updateDuration);
    updateDuration();
});

Lovely code examples and stuff from people - but the explanation is actually very simple.

If the file is already in the cache then the loadedmetadata event will not fire (nor will a number of other events - basically because they've already fired by the time you attach your listeners) and the duration will be set. If it's not in the cache then the duration will be NaN, and the event will fire.

The solution is sort of simple.

function runWhenLoaded() { /* read duration etc, this = audio element */ }

if (!audio.readyState) { // or $audio[0].readyState
    audio.addEventListener("loadedmetadata", runWhenLoaded);
    // or $audio.on("loadedmetadata", runWhenLoaded);
} else {
    runWhenLoaded.call(audio);
    // or runWhenLoaded.call($audio[0]);
}

I've included the jQuery alternatives in the code ments.

According to w3 spec this is standard behavior when duration returns NaN. So I suggest use durationchange event:

$('audio').on('durationchange', function(){
    var duration = $('audio')[0].duration;
    if(!isNaN(duration)) {
        $('#duration').html(Math.ceil(duration));
    }
});

NOTE: This code (and your too) will not work correct in case if you have more than one audio element on page. Reason is that you listen events from all audio elements on page and each element will fire own event:

$('audio').on('durationchange', function(){...});

OR

You can try:

    <script>
       function durationchange() {
          var duration = $('audio')[0].duration;
          if(!isNaN(duration)) {
              $('#duration').html(Math.ceil(duration));
          }
        }
    </script>

    <audio ondurationchange="durationchange()">
         <source src="test.mp3" type="audio/mpeg">
    </audio>

Note that behaviors will differ from one browser to another. On Chrome, you have different type of loading. When resources are not in cache, it will fetch either the plete file (for js or css for example), either a part of the file (mp3 for example). This partial file contains metadata that allows browser to determine duration and other data such as the time it'll take to download whole file at this rate, trigerring for example canplay or canplaythrough events. If you look at network usage in you dev console, you'll see that the HTTP status code will be either 200 (succesful load) or 206(partial load - for mp3 for example).

When you hit refresh, elements are checked to see if they changed. HTTP status will then be 304, meaning file hasn't been modified. If it hasn't changed and is still in browser cache, then it won't be downloaded. The call to determine if it has or not changed es from the server providing the file.

When ou simply click enter in adress bar, it's automatically taken from cache, not validating online. So it's much faster.

So depending on how you call or refresh your page (either simmple enter, refresh or plete refresh without cache), you'll have big differences on the moment you get the metadata from your mp3. Between taking the metadata from cache directly vs making a request to a server, the difference can be a few hundreds milliseconds, which is enough to change what data is available at different moment.

That being said, listening to loadedmetada should give consistent result. This event is triggered when the data with duration information is loaded, so whatever way the page is loaded, it shouldn't matter if that called is properly made. At this point you have to consider maybe some interference from other elements. What you should do is follow your audio through various events to get exactly where its at at different moments. So in you document ready you could add listeners for different event and see where the problem occurs. Like this:

$('audio')[0].addEventListener('loadstart', check_event)
$('audio')[0].addEventListener('loadeddata', check_event)
$('audio')[0].addEventListener('loadedmetadata', check_event)//at this point you should be able to call duration
$('audio')[0].addEventListener('canplay', check_event) //and so on

function check_event(e) {
   console.log(e.target, e.type)
}

You'll see that depending on the way you refresh, these events can e at different moments, maybe explaining inconsistencies in your outputs.

本文标签: javascriptAudio duration NaN on certain page request actionStack Overflow