1
votes

Problem: I only have a single video source (mp4) as I am trying to add custom controls to a tumblr video.

If there is only mp4 as the source video.duration is returned as NaN.

As a test when using 3 sources (mp4/webm/ogg) then it works, so video.duration must only be returned from wemb or ogg.

JSFIDDLE 1 with only a single mp4 as the source (so not working).

JSFIDDLE 2 with 3 source files which is working.

HTML

<div id="video-container">
    <!-- Video -->
    <video id="video" width="640" height="365" poster="https://68.media.tumblr.com/tumblr_oj7dx3dAJL1vf3pu7_smart1.jpg">
      <source src="https://www.tumblr.com/video_file/t:kuWOGAOEWjotDIJeZpO4yw/155341819278/tumblr_oj7dx3dAJL1vf3pu7/480" type="video/mp4">
    </video>
    <!-- Video Controls -->
    <div id="video-controls">
        <button type="button" id="play-pause" class="play">Play</button>
        <input type="range" id="seek-bar" value="0">
        <button type="button" id="mute">Mute</button>
        <input type="range" id="volume-bar" min="0" max="1" step="0.1" value="1">
        <button type="button" id="full-screen">Full-Screen</button>
    </div>
</div>
<span id="currentTime">0</span>
<span id="duration">0</span>

JS:

    // Video
    var video = document.getElementById("video");
    // Buttons
    var playButton = document.getElementById("play-pause");
    var muteButton = document.getElementById("mute");
    var fullScreenButton = document.getElementById("full-screen");
    // Sliders
    var seekBar = document.getElementById("seek-bar");
    var volumeBar = document.getElementById("volume-bar");

    // Event listener for the play/pause button
    playButton.addEventListener("click", function() {
        if (video.paused == true) {
            // Play the video
            video.play();
            // Update the button text to 'Pause'
            playButton.innerHTML = "Pause";
        } else {
            // Pause the video
            video.pause();
            // Update the button text to 'Play'
            playButton.innerHTML = "Play";
        }
    });

    ...

    // Update the seek bar as the video plays
    video.addEventListener("timeupdate", function() {
        // Calculate the slider value
        var value = (100 / video.duration) * video.currentTime;
        // Update the slider value
        seekBar.value = value;
    });

    ...

The JS is mainly taken from here: http://blog.teamtreehouse.com/building-custom-controls-for-html5-videos

What I have tried:

video.addEventListener('loadedmetadata',function(){
        console.log(video.duration); //returns NaN
    });

Some jQuery

$(document).ready(function(){
    $("#video").on("timeupdate", function(event){
        onTrackedVideoFrame(this.currentTime, this.duration);
    });
});

function onTrackedVideoFrame(currentTime, duration){
    $("#current").text(currentTime);
    $("#duration").text(duration);
}

Another attempt:

window.setInterval(function(t){
        if (video.readyState > 0) {
        var duration = $('#duration').get(0);
        var vid_duration = Math.round(video.duration);
        duration.firstChild.nodeValue = vid_duration;
        clearInterval(t);
        }
    },500);

Also

<video preload="metadata">

None of these solutions seemed to have worked.

I've also looked at this question on SO. Which has a voted answer that is untested.

1
you have to wait for the metadata for the video to have loaded in order to retrieve the duration value. if your JS is in-line then it will fire as soon as it's reached. If you wrap it on an onloaded event for the video element then you should be goodOffbeatmammal
This function video.addEventListener('loadedmetadata',function(){ is contained within an onload function (you can see this in the jsfiddle). Is that what you mean? Feel free to update/fork the fiddle if that helps. I try and avoid inline js where possible.lharby

1 Answers

3
votes

To get the duration of the video you have to wait for the metadata portion of the video source to load (this can vary significantly depending on how the video has been encoded - ideally a 2-pass encode that relocates the MOOV atom to the beginning has been used so that can be extracted quickly).

By listening to the loadedmetadata event on the video object your code will know when it is safe to query that.

In the code below you will see I register the event handler using inline javascript (or could be in the document.onload) which in turn calls the function to extract the duration when the value is available. Any code that needs to know the value should then be run.

I've also added preload="auto" just for personal preference as it helps pre-buffer content in some browsers/devices, that's not needed for the event to work in most scenarios.

<video preload="auto" id="video" width="640" height="365" poster="https://68.media.tumblr.com/tumblr_oj7dx3dAJL1vf3pu7_smart1.jpg">
  <source src="https://www.tumblr.com/video_file/t:kuWOGAOEWjotDIJeZpO4yw/155341819278/tumblr_oj7dx3dAJL1vf3pu7/480" type="video/mp4">
</video>

<script>

var vid = document.getElementById("video"
vid.addEventListener('loadedmetadata', getDuration, false);

function getDuration() {
    console.log(vid.duration)
}

</script>