Posts Tagged: video


23
Mar 11

Display additional overlay elements on top of an HTML5 video element with JavaScript

Since more and more browsers officially support the HTML5-only <video>tag it becomes convenient to build a simple system for displaying additional content-dependent information on top. This tutorial shows basic ideas and examples of the most important functions needed. Currently all major browsers’ latest version support HTML5 video. Thankfully Microsoft just rolled out version 9 of its Internet Explorer with full HTML5 support.

This is how it will look like:

The overlay system will be implemented in pure JavaScript and HTML5, making it a decent way to display content dynamically. To make things more simple free JavaScript library JQuery is used for element selection and animations. The goal is to have a predefined piece of HTML code displayed at a specific place and time for a specific duration. To keep it simple we will use an JavaScript array with 2 dimensions. The bottom layer holds all data (time_start, time_end, html, position_x, position_y) while the top layer tights all data arrays together. Example:

items[0] = [1,5,'Hello world!',80,20];

Should later create an overlay to be shown from second 1 to 5 at position 10:20 and displaying Hello World!.

Before we get started, some thoughts about the structure and execution sequence of our script: Basically we only need to do two things. First, create as many hidden <div>containers as there are overlay elements and put the corresponding html code in.  Secondly, periodically check whether an element needs to be hidden or displayed based on the current time of the video playing.

OK, so lets get started with the basic HTML body!

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>VideoOverlay Demo</title>
<link rel="stylesheet" href="styles.css" type="text/css">
 	<script src="jquery-1.5.1.min.js" type="text/javascript"></script>
	<script src="overlay.func.js" type="text/javascript"></script>
	<script type="text/javascript">
   var display_overlays = true;
   var items = [];
   items[0] = ['1','5','hello','40','40']; // format [start,end,html,x,y]
   items[1] = ['2','14','<h2>this one\'s big</h2>','320','180'];
   items[2] = ['17','24','<span style="color:red;">color is possible too</span>','200','50'];
</script>
<div id="video_box" onclick="Overlay.togglePlay();">
<div id="video_overlays"></div>
<div>
     <video id="test_video" width="640" height="360" controls="controls">       <!-- MP4 must be first for iPad! -->
<source src="movies/big_buck_bunny.mp4" type="video/mp4">       <!-- WebKit video    -->
<source src="movies/big_buck_bunny.webm" type="video/webm">       <!-- WebM/VP8/Vorbis -->
<source src="movies/big_buck_bunny.ogv" type="video/ogg">       <!-- Firefox / Opera -->     </video>
   </div>
</div>

To keep things clean most JavaScript code has been moved to an external file, overlay.func.js. The main file only holds our main data array called items[] and and a boolean display_overlays which might optionally be set false. As you can see it should be easy to insert a data array created by a server side application (e.g. a PHP script) instead of using hard coded sample data as provided above.

HTML5 is just slightly different from old HTML4. See that doctype definition at the beginning? That’s as simple as it can be. And pay attention to the <video>tag: Appending source files is all it needs to make the magic happen. Due to recent browser wars it is necessary to supply at least two differently encoded source files. All web-kit based browsers (Safari, Safari mobile on iPhone and iPad) and coming Internet Explorer only support mp4 encoded video files. On the other hand Firefox, Chrome and Opera support webm and/or ogg movie container only. You don’t have to worry about that as long as you provide differently encoded video files. Every browser just looks through that <source>list until it finds a file it is capable to play. Another interesting feature is the controls parameter which makes a browser display build in video controls. As far as I know those consist at least of play, pause and stop buttons as well as volume control and a time slider.

So grab some movie files (Big Buck Bunny is an OpenSource movie project, thus free), put together the above HTML Code and fire up your HTML5 enabled browser. That was easy, wasn’t it?

Now it’s time for JavaScript. In your external .js script insert the following code:

$(document).ready(function(){
Overlay.movie = $('#test_video');
Overlay.movie.bind('timeupdate', Overlay.schedule_check);
Overlay.schedule_setup();
});

Don’t forget to download JQuery and link in the <script> section of your HTML head to that file.

Above code is executed when the body as well as all external files are loaded. You can see that this functions works with an object called Overlays which gets defined soon. The object movie is saved for later reference, pointing to our HTML <video> element. This element fires a timeupdate event when – you guessed it – the current playing position of the movie changes. While continuously playing this event is supposed to fire at least every 250ms (dependent on the browser used). Of course it will also be triggered when a user manually jumps to a different position. By binding this event to schedule_check(), we will later use it to check what overlay elements need to be shown or hidden at that specific moment. Last but not least we’ll run schedule_setup() to create and arrange all elements needed.

First part of the following JavaScript code:

var Overlay = ({
schedule_setup: function(){
var box= $("#video_overlays");    // create elements
for (var i=0; i<items.length; i++) {
var t = $("<div class="\"overlay_item\"" id="\"item_"+i+"\"">"+items[i][2]+"</div>");
t.hide();
box.prepend(t);
}
this.rearrangeItems();
},
rearrangeItems: function() {
var box= $("#video_overlays");  // getting video box offset
var off_x = Overlay.movie.offset().left;
var off_y = Overlay.movie.offset().top;
// moving every element to its designated position
for (var i=0; i<items.length; i++) {
t = $('#item_'+i);
t.css('left',parseInt(items[i][3])+off_x);
t.css('top',parseInt(items[i][4])+off_y);
}
},

This part defines an object Overlay and two of it’s functions. Function schedule_setup() fulfills what we asked for: Creating and hiding all elements. It then calls rearrange_items() which reads out the absolute position of that video element (using its offset) and moves all element boxes to their corresponding places. This is done by simply adding their given X and Y values to the video elements’ absolute position. While there is no obvious reason to use a stand alone function for that, you might think of situations where the hosting video elements’ position changes and all child elements have to be moved to a new place too.

Finally coding schedule_check():

schedule_check: function() {
// check if anything needs to be done
for (var i=0; i<items.length; i++) {
if(!display_overlays) {
$('#item_'+i).hide();
} else if(items[i][0]> Overlay.movie.attr('currentTime')) {
$('#item_'+i).hide();
} else if(items[i][0]>=Overlay.movie.attr('currentTime') && items[i][1] >= Overlay.movie.attr('currentTime')) {      if(!$('#item_'+i).is(":visible")) $('#item_'+i).fadeIn('slow');
} else {
if($('#item_'+i).is(":visible")) $('#item_'+i).fadeOut('slow');
 }
}
},

While iterating through all items this function runs a couple of if-else statements to check what needs to be done. Starting with a simple check whether or not to show those overlay elements (you remember display_overlays defined and set in our html file?) and eventually hiding them, it continues to execute one of three actions based on current time and items’ settings. To shorten things up: If it’s to early that item gets hided, while fadeOut() is called when it is too late. In between the item’s faded in.

As you can see the code above doesn’t seem to be quite efficient. Every time schedule_check() is called an action for every single overlay element is executed. It might have been an alternative to only change element properties if a special event occurs, namely if current time is equal to an elements’ start or end time. The reason I haven’t done that is user controllability. A user should be able  to jump to any time in the video which would apparently cause incorrect displayed elements if only checked for start and end. This problem is obsolete if you verify all elements’ status every time the function is called.

It might be worth a try optimizing this code to speed things up. A first attempt was made by checking an elements’ visibility before fading it in or out. I believe animations take up quite a lot computing power.

Last thing to do is setting up a CSS file (syles.css) providing style information of the overlay elements:

.overlay_item {
position:absolute;
z-index:2000;
background:#FFF;
padding:2px;
}

While the last two statements are truly optional and only make elements more readable, first two properties are very important. So position:absolute enables them to be moved in absolute coordinates to wherever you want and meanwhile not taking up any space on the page thus not destroying your design. Second property z-index defines an layer for the element. You can think of that as layers of visibilty so that elements with lower z-index might get covert by those with a higher z-index. By setting this index to 2000 we ensure no other element will be displayed on top – especially not the <video> element – so that the user has a clear view on our additional movie information.

You can find more information as well as a sample project at this post.

Source: