How to make your own HTML5 Video video player

  • Tutorial
Earlier, we already looked at the general issues of using HTML5 Audio and Video and began to dive into the details, starting with the task of determining whether the browser supports the desired codec . Today we will consider the task of creating our own video player using HTML5 Video.


Let me remind you that the video element by itself already provides the necessary set of controls for controlling playback. For the playback control panel to be visible, just specify the controls attribute .


However, as I noted in the introductory article, there is a problem with standard controls, which lies in the fact that they look non-standard. In other words, they look different in each browser (you can check how the controls look in different browsers using the Video Format Support example on ietestdrive.com - just open it in two or three different browsers).

Playback Management API


The HTML5 standard for working with video introduces a new interface into the DOM - HTMLVideoElement, which in turn inherits the HTMLMediaElement interface.

HTMLMediaElement Interface


This is a common interface for both media elements (audio and video) that describes access to the basic capabilities of working with media content: control of the content source, playback control, changing the sound level and error handling. Basic properties and methods that we need:

Network status and willingness to work
the src - link (url) to playback content
buffered - buffered pieces of video

playback and control of
currentTime - currently playing (p.)
Duration - the duration of the media content (p. )
paused - whether playback is paused
ended - whether
muted playback has ended - sound on / off
volume - sound level [0, 1]
play () - start playing
pause () - pause

events
oncanplay - you can start playing
ontimeupdate -
changing the playing position onplay - running
onpause - paused
onended - playback has ended

Important: this is far from all methods and properties set via the HTMLMediaElement interface.

HTMLVideoElement Interface


Video differs from audio in several additional properties:
width and height - the width and height of the container for playing video;
videoWidth and videoHeight - the internal value of the width and height of the video, if the sizes are not known, equal to 0;
poster - a link to a picture that can be displayed while the video is inaccessible (usually this is one
of the first non-empty frames).

The difference between width / height and videoWidth / videoHeight is that the latter are the video’s own characteristics, in particular, taking into account the aspect ratio and other characteristics, while the video container can be of any size (larger, smaller, with a different proportion )

Play & pause


We will start the creation of our own video player with a simple task: we will learn how to start the video on playback and stop playback. To do this, we need the play () and pause () methods and several properties that describe the current state of the video stream (we will also use the jQuery library, do not forget to connect it).

First of all, we need a video element that we want to control, and an element that we can click on to control the current state:
Play


#controls span {
    display:inline-block;
}
#playpause {
    background:#eee;
    color:#333;
    padding:0 5px;
    font-size:12pt;
    text-transform:uppercase;
    width:50px;
}


Pay attention to inverting the state of the button (paused) and action (play).

Now we need to add a little js-code so that pressing the play button switches its state and, accordingly, starts the video clip or pauses it:
$(document).ready(function(){
    var controls = {
        video: $("#myvideo"),
        playpause: $("#playpause")                 
    };
    var video = controls.video[0];
    controls.playpause.click(function(){
        if (video.paused) {
            video.play();
            $(this).text("Pause");    
        } else {
            video.pause();
            $(this).text("Play");
        }
        $(this).toggleClass("paused"); 
    });
}); 


If you wish, you can immediately add a few css-styles for the control buttons and their various states and ...

... it would seem that everything already works great, but it wasn’t there! There are a few little things that we also need to consider.

Play first


Firstly, we need to correctly process the end of the video clip (unless, of course, it is looped), and at that moment we need to switch the control buttons so that instead of the “pause” state there is a “play” state:

video.addEventListener("ended", function() {
    video.pause();
    controls.playpause.text("Play");
    controls.playpause.toggleClass("paused");
});


Context menu


Secondly, browsers usually add the ability to control playback through the context menu. This means that the user, generally speaking, can change something bypassing our controls. You should also catch this moment and make the necessary changes to the appearance of the controls. To do this, just subscribe to the onplay and onpause events .

video.addEventListener("play", function() {
    controls.playpause.text("Pause");
    controls.playpause.toggleClass("paused");
});
video.addEventListener("pause", function() {
    controls.playpause.text("Play");
    controls.playpause.toggleClass("paused");
});


Since we have a lot of places where the appearance changes, it's time to make a little refactoring along the way, removing from the initial switching of modes the now duplicate change in the external state:

var controls = {
    ...  
    togglePlayback: function() {
        (video.paused) ? video.play() : video.pause();
    }
    ...
};
controls.playpause.click(function(){
    controls.togglePlayback();
});


Clickable video


Finally, for sure, we will want the playback and pause to switch by clicking on the video itself, so we need to add a few more lines:

controls.video.click(function() {
    controls.togglePlayback();
});


Current result:


Progress


Now let's move on to displaying the progress of playback. First you need to add a few elements that will be used to display the current state and control the current position:

00:00 / 
    00:00


And related styles:

#progress {
    width:290px;
}
#total {
    width:100%;                
    background:#999;
}
#buffered {
    background:#ccc;
}
#current {
    background:#eee;
    line-height:0;
    height:10px;
}
#time {
    color:#999;
    font-size:12pt;
}

And a few links to the corresponding elements for quick access to the controls object:
var controls = {
    ...
    total: $("#total"),
    buffered: $("#buffered"),
    progress: $("#current"),
    duration: $("#duration"),
    currentTime: $("#currenttime"),
    hasHours: false,
    ...
};


First of all, we need to understand what the duration of the video is - for this, the video element has a duration property . You can track this value, for example, when the video is ready to play - by the oncanplay event :

video.addEventListener("canplay", function() {
    controls.hasHours = (video.duration / 3600) >= 1.0;                    
    controls.duration.text(formatTime(video.duration, controls.hasHours));
    controls.currentTime.text(formatTime(0),controls.hasHours);
}, false);


In this case, we simultaneously determine whether to display the number of hours in the video player (by the way, generally speaking, the specification assumes that the duration of the clip can change - the ondurationchange event fires at that moment , and also be infinite - for example, when streaming radio).


We also use the special function formatTime to convert seconds to the format HH: mm: ss or mm: ss:

function formatTime(time, hours) {
    if (hours) {
        var h = Math.floor(time / 3600);
        time = time - h * 3600;
        var m = Math.floor(time / 60);
        var s = Math.floor(time % 60);
        return h.lead0(2)  + ":" + m.lead0(2) + ":" + s.lead0(2);
    } else {
        var m = Math.floor(time / 60);
        var s = Math.floor(time % 60);
        return m.lead0(2) + ":" + s.lead0(2);
    }
}
Number.prototype.lead0 = function(n) {
    var nz = "" + this;
    while (nz.length < n) {
        nz = "0" + nz;
    }
    return nz;
};


To display the playback process, we need the ontimeupdate event that fires when the current moment changes:

video.addEventListener("timeupdate", function() {
    controls.currentTime.text(formatTime(video.currentTime, controls.hasHours));
    var progress = Math.floor(video.currentTime) / Math.floor(video.duration);
    controls.progress[0].style.width = Math.floor(progress * controls.total.width()) + "px";
}, false);


The currentTime property returns the current time in seconds. It can also be used to change the playing time:

controls.total.click(function(e) {
    var x = (e.pageX - this.offsetLeft)/$(this).width();
    video.currentTime = x * video.duration;
});


It will also be useful to show video buffering, for this you can build on the onprogress event that fires when new portions of the video are loaded:

video.addEventListener("progress", function() {
    var buffered = Math.floor(video.buffered.end(0)) / Math.floor(video.duration);
    controls.buffered[0].style.width =  Math.floor(buffered * controls.total.width()) + "px";
}, false);


An important caveat regarding the buffered property to keep in mind is that it provides not just time in seconds, but time intervals in the form of a TimaRanges object. In most cases, it will be only one interval with an index of 0, and starting at 0c. However, if the browser uses HTTP range requests to the server, for example, in response to attempts to move to other parts of the video stream, there may be several gaps. It should also be borne in mind that, depending on the implementation, the browser may delete already lost pieces of video from the memory buffer.

Intermediate result:


Sound


Finally, let's add another little touch to our video player - the ability to turn the sound on and off. To do this, add a small control with a speaker (SVG icon taken from The Noun Project website ):



With appropriate styles for on and off states:
#dynamic {
    fill:#333;
    padding:0 5px;
}
#dynamic.off {
    fill:#ccc;
}

To switch the speaker state, we need the mute property :

controls.dynamic.click(function() {
    var classes = this.getAttribute("class");
    if (new RegExp('\\boff\\b').test(classes)) {
        classes = classes.replace(" off", "");
    } else {
        classes = classes + " off";
    }
    this.setAttribute("class", classes);
    video.muted = !video.muted;
});

(The standard jQuery methods for switching css classes do not work with SVG elements.)
If you also want to change the volume level, the volume property , which takes values ​​in the range [0, 1] , will help you .

The final result:


What else...


Besides the fact that you can easily customize the styles of controls at your discretion, there are several other important points that remain outside the scope of this article, but which are useful to remember in a real project:

Also, do not forget that you need to bind events to controls after it has become clear that the video is available for playback (oncanplay):

video.addEventListener("canplay", function() {
    ...
}, false);


Either you need to do the appropriate checks or catch possible exceptions. Exceptions generally need to be caught, for example, the onerror event that occurs when a video stream is loaded incorrectly :)

Of the additional options that you may need: changing the playback speed. To do this, there is a playbackRate property and the corresponding onratechange event .


Ready players


I think it’s not difficult for you to find ready-made solutions for using HTML5 Video with all the relying benefits, right up to convenient customization of the look through CSS. Here are some useful links:

Finally, HTML5 Video in the specification .

Also popular now: