Earlier, we considered general issues of using
HTML5 Audio and Video and began to dive into the details, starting with the task of determining
browser support for the desired codec . Today we will consider the task of creating your own video player on HTML5 Video.

Let me remind you that the video element itself already provides the necessary set of controls for controlling playback. To make the playback control panel visible, it is sufficient to specify the
controls attribute.
<video src="trailer_480p.mp4" width="480" height="270" poster="poster.gif" controls />
However, as I noted in the introductory article, with standard controls there is a problem, which lies precisely in the fact that they look non-standard. In other words, they look different in each browser (you can check how the controls in different browsers look, for example,
Video Format Support on
ietestdrive.com - just open it in two or three different browsers).
API to control playback
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), describing access to basic media content: control of the source of content, control of playback, change of sound level, and error handling. The main properties and methods that we need:
Network status and availabilitysrc - link (url) to the reproduced content
buffered - buffered video chunks
Reproduction and controlscurrentTime - current playback time (p.)
duration - the duration of the media content (p.)
paused - whether playback is paused
ended - whether the playback has ended
muted - on / off sound
volume - sound level [0, 1]
play () - start playing
pause () - pause
Developmentsoncanplay - you can start playing
ontimeupdate - changed playback position
onplay - the player is running.
onpause - pause pressed
onended - playback ended
Important: these are not all methods and properties exposed 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 dimensions are not known, are 0;
poster - a link to a picture that can be shown while the video is not available (usually this is one
from the first non-empty frames).
The difference between width / height and videoWidth / videoHeight is that the latter are their own characteristics of the video, in particular, taking into account the aspect ratio and other characteristics, while the container for the video can be of any size (more, less, with a different proportion ).
Play & pause
Creating our own video player, we begin with a simple task: learn how to play video for 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 include it).
First of all, we need a video element that we want to control, and an element that can be clicked to control the current state:
<div> <video id="myvideo" width="480" height="270" poster="poster.gif" > <source src="trailer_480p.mp4" type='video/mp4;codecs="avc1.42E01E, mp4a.40.2"' /> <source src="trailer_480p.webm" type='video/webm; codecs="vorbis,vp8"'/> </video> </div> <div id="controls"> <span id="playpause" class="paused" >Play</span> </div>
#controls span { display:inline-block; } #playpause { background:#eee; color:#333; padding:0 5px; font-size:12pt; text-transform:uppercase; width:50px; }
Notice the invert of the state of the button (paused) and the action (play).
Now you need to add a little js-code so that clicking on 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 desired, you can immediately add several css-styles for control buttons and their various states and ...
... it would seem that everything is already working perfectly, but it was not there! There are a few little things that we also need to take into account.
Playing first
First, we need to properly handle the end of the video playback (unless, of course, it is not looped), and at this 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. This moment must also be caught and the necessary changes made 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 do a little refactoring along the way, removing the duplicate change of the external state from the initial mode switch:
var controls = { ... togglePlayback: function() { (video.paused) ? video.play() : video.pause(); } ... }; controls.playpause.click(function(){ controls.togglePlayback(); });
Clickable video
Finally, for sure, we want the playback and pause to be switched by clicking on the video itself, so you need to add a few more lines:
controls.video.click(function() { controls.togglePlayback(); });
Current result:

Progress
Now let's go to display the progress of playing. First you need to add a few elements that will be used to display the current state and control the current position:
<span id="progress"> <span id="total"> <span id="buffered"><span id="current"></span></span> </span> </span> <span id="time"> <span id="currenttime">00:00</span> / <span id="duration">00:00</span> </span>
And corresponding 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 object controls:
var controls = { ... total: $("#total"), buffered: $("#buffered"), progress: $("#current"), duration: $("#duration"), currentTime: $("#currenttime"), hasHours: false, ... };
First of all, we need to understand how long the movie is - for this the video element has a
duration property. You can track this value, for example, when a 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 the number of hours should be displayed in the video player (by the way, generally speaking, the specification assumes that the duration of the video can vary - at this point the
ondurationchange event is
triggered , and also to be infinite - for example, during streaming radio).
We also use the special function
formatTime to translate seconds into 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 an
ontimeupdate event, which is triggered 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 gives 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 is triggered when new video portions 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 nuance regarding the
buffered property that needs to be kept in mind is that it provides not just time in seconds, but intervals in the form of a TimaRanges object. In most cases, this will be only one gap with the index 0, and starting from 0c. However, if the browser uses HTTP range server requests, for example, in response to attempts to switch to other video stream fragments, 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 site
The Noun Project ):
<span id="volume"> <svg id="dynamic" version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 95.465 95.465"> <g > <polygon points="39.323,20.517 22.705,37.134 0,37.134 0,62.865 22.705,62.865 39.323,79.486 "/> <path d="M52.287,77.218c14.751-15.316,14.751-39.116,0-54.436c-2.909-3.02-7.493,1.577-4.59,4.59 c12.285,12.757,12.285,32.498,0,45.254C44.794,75.645,49.378,80.241,52.287,77.218L52.287,77.218z"/> <path d="M62.619,89.682c21.551-22.103,21.551-57.258,0-79.36c-2.927-3.001-7.515,1.592-4.592,4.59 c19.08,19.57,19.08,50.608,0,70.179C55.104,88.089,59.692,92.683,62.619,89.682L62.619,89.682z"/> <path d="M75.48,99.025c26.646-27.192,26.646-70.855,0-98.051c-2.936-2.996-7.524,1.601-4.592,4.59 c24.174,24.674,24.174,64.2,0,88.871C67.956,97.428,72.545,102.021,75.48,99.025L75.48,99.025z"/> </g> </svg> </span>
With the appropriate styles for on and off states:
#dynamic { fill:#333; padding:0 5px; } #dynamic.off { fill:#ccc; }
To switch the status of the speaker, 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; });
(Standard jQuery methods for switching css-classes do not work with SVG elements.)
If you also want to change the volume level, then the
volume property will help you, taking values in the range [0, 1].
Final result:

What else...
In addition to the fact that you can easily customize control styles at your discretion, there are several 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 the binding of events to controls should be done
after it became clear that the video is available for playback (oncanplay):
video.addEventListener("canplay", function() { ... }, false);
Either do the appropriate checks or catch possible exceptions. Exceptions generally need to catch, for example, an
onerror event that occurs when an error loading a video stream :)
Of the additional options you might need: change the playback speed. To do this, there is the
playbackRate property and the corresponding
onratechange event.
Ready players
I think it will not be difficult for you to find ready-made solutions for using HTML5 Video with all the relying buns, up to and including the convenient customization of the appearance through CSS. Here are some useful links:
Finally,
HTML5 Video in the specification .