Chapter 5
HTML5 Audio and Video

No book on HTML5 would be complete without an examination of the new video and audio elements. These ground-breaking new elements have already been utilized on the Web, albeit in a limited capacity, but more and more developers and content creators are starting to incorporate them into their projects.

For The HTML5 Herald, we’re going to be placing a video element in the first column of our three-column layout. But before we explore the details of the video element and its various attributes and associated elements, let’s take a brief look at the state of video on the Web today.

For the most part, this chapter will focus on the video element, since that’s what we’re using in our sample project. However, the audio element behaves relatively identically: almost all the attributes and properties that we’ll be using for video also apply to audio. Where there are exceptions, we’ll be sure to point them out.

A Bit of History

Up until now, multimedia content on the Web has, for the most part, been placed in web pages by means of third-party plugins or applications that integrate with the web browser. Some examples of such software include QuickTime, RealPlayer, and Silverlight.

By far the most popular way to embed video and audio on web pages is by means of Adobe’s Flash Player plugin. The Flash Player plugin was originally developed by Macromedia and is now maintained by Adobe as a result of their 2005 buy-out of the company. The plugin has been available since the mid-90s, but did not really take off as a way to serve video content until well into the 2000s.

Before HTML5, there was no standard way to embed video into web pages. A plugin like Adobe’s Flash Player is controlled solely by Adobe, and is not open to community development.

The introduction of the video and audio elements in HTML5 resolves this problem and makes multimedia a seamless part of a web page, the same as the img element. With HTML5, there’s no need for the user to download third-party software to view your content, and the video or audio player is easily accessible via scripting.

The Current State of Play

Unfortunately, as sublime as HTML5 video and audio sounds in theory, it’s less simple in practice. A number of factors need to be considered before you decide to include HTML5’s new multimedia elements on your pages.

First, you’ll need to understand the state of browser support. At the time of writing, the only browsers with a significant market share that don’t support native HTML5 video and audio are Internet Explorer 8 and earlier. Unfortunately, this is still a sizable slice of most sites’ audiences.

The other major browser makers offer HTML5 video support in versions now in wide use (Chrome 3+, Safari 4+, and Firefox 3.5+). The last version of Chrome without HTML5 video support (version 2) has a nonexistent market share, and the same is true for the nonsupporting versions of Safari and Opera.

Although IE’s market share is significant, you can still use HTML5 video on your pages today. Later on, we’ll show you how the new video element has been designed with backwards compatibility in mind, so that users of nonsupporting browsers will still have access to your multimedia content.

Video Container Formats

Video on the Web is based on container formats and codecs. A container is a wrapper that stores all the necessary data that comprises the video file being accessed, much like a ZIP file wraps or contains files. Some examples of well-known video containers include Flash Video (.flv), MPEG-4 (.mp4 or .m4v), and AVI (.avi).

The video container houses data, including a video track, an audio track with markers that help synchronize the audio and video, language information, and other bits of metadata that describe the content.

The video container formats relevant to HTML5 are MPEG-4, Ogg, and WebM.

Video Codecs

A video codec defines an algorithm for encoding and decoding a multimedia data stream. A codec can encode a data stream for transmission, storage, or encryption, or it can decode it for playback or editing. For the purpose of HTML5 video, we’re concerned with the decoding and playback of a video stream. The video codecs that are most pertinent to HTML5 video are H.264, Theora, and VP8.

Audio Codecs

An audio codec in theory works the same as a video codec, except it’s concerned with the streaming of sound, rather than video frames. The audio codecs that are most pertinent to HTML5 video are AAC and Vorbis.

What combinations work in current browsers?

It would be nice if browser support allowed us to choose a single container, video codec, and audio codec to create a standard way of embedding video using the new video element in HTML5. Unfortunately, it’s not quite that simple—although things are improving.

In Table 5.1, we’ve outlined video container and codec support in the most popular browser versions. This chart only includes browser versions that offer support for the HTML5 video element.

Table 5.1. Browser support for HTML5 video

Container/Video Codec/Audio CodecFirefoxChromeIEOperaSafariiOS SafariAndroid
Ogg/Theora/Vorbis3.5+3+10.5+
MP4/H.264/AAC3–119+4+4+2.1+[a]
WebM/VP8/Vorbis4+6+9+[b] 10.6+2.3+

[a] Versions of Android prior to 2.3 require JavaScript to play the video.

[b] IE9 supports playback of WebM video with a VP8 codec when the user has installed a VP8 codec on Windows.

Opera Mini and Opera Mobile currently offer no support for HTML5 video, but Opera has announced there are plans to include support in upcoming releases.

Warning: Licensing Issues

While the new video element itself is free to use in any context, the containers and codecs are not always as simple. For example, while the Theora and VP8 (WebM) codecs are not patent-encumbered, the H.264 codec is patent-encumbered and licensing for it is provided by the MPEG-LA group.

Currently, for H.264, if your video is provided to your users for free, there’s no requirement for you to pay royalties. However, detailed licensing issues are far beyond the scope and intent of this book, so just be aware that you may need to do some research before using any particular video format when including HTML5 video in your pages.

The Markup

After all that necessary business surrounding containers, codecs, browser support, and licensing issues, it’s time to examine the markup of the video element and its associated attributes.

The simplest way to include HTML5 video in a web page is as follows: <video src="example.webm"></video>

But, as you’ve probably figured out from the preceding sections, this will only work in a limited number of browsers. It is, however, the minimum code required to have HTML5 video working to some extent. In a perfect world, it would work everywhere—the same way the img element works everywhere—but that’s a little way off just yet.

Similar to the img element, the video element should also include width and height attributes:

<video src="example.webm" width="375" height="280"></video>

Even though the dimensions can be set in the markup, they’ll have no effect on the aspect ratio of the video. For example, if the video in the above example was actually 375×240 and the markup was as shown above, the video would be centered vertically inside the 280-pixel space specified in the HTML. This stops the video from stretching unnecessarily and looking distorted.

The width and height attributes accept integers only, and their values are always in pixels. Naturally, these values can be overridden via scripting or CSS.

Enabling Native Controls

No embedded video would be complete without giving the user the ability to play, pause, stop, seek through the video, or adjust the volume. HTML5’s video element includes a controls attribute that does just that:

<video src="example.webm" width="375" height="280" controls></video>

controls is a Boolean attribute, so no value is required. Its inclusion in the markup tells the browser to make the controls visible and accessible to the user.

Each browser is responsible for the look of the built-in video controls. Figure 5.1 to Figure 5.4 show how these controls differ in appearance from browser to browser.

The native video controls in Firefox 4

Figure 5.1. The native video controls in Firefox 4

… in IE9

Figure 5.2. … in IE9

… in Opera 11

Figure 5.3. … in Opera 11

… and in Chrome

Figure 5.4. … and in Chrome

The autoplay Attribute

We’d love to omit reference to this particular attribute, since its use will be undesirable for the most part. However, there are cases where it can be appropriate. The Boolean autoplay attribute does exactly what it says: it tells the web page to play the video as soon as possible.

Normally, this is a bad practice; most of us know too well how jarring it can be if a website starts playing video or audio as soon as it loads—especially if our speakers are turned up. Usability best practices dictate that sounds and movement on web pages should only be triggered when requested by the user. But this doesn’t mean that the autoplay attribute can never be used.

For example, if the page in question contains nothing but a video—that is, the user clicked on a link to a page for the sole purpose of viewing a specific video—it may be acceptable to allow it to play automatically, depending on the video’s size, any surrounding content, and the audience.

Here’s how you’d do that:

<video src="example.webm" width="375" height="280" controls 
↵autoplay></video>

Warning: Autoplaying on the iPhone

Safari on the iPhone will ignore the autoplay attribute; all video will wait for the user to press the play button before starting. This is sensible, given that mobile bandwidth is often limited.

The loop Attribute

Another attribute that you should think twice about before using is the Boolean loop attribute. Again, it’s fairly self-explanatory: according to the spec, this attribute, when present, will tell the browser to seek back to the start of the media resource upon reaching the end.

So if you created a web page whose sole intention was to annoy its visitors, it might contain code like this:

<video src="example.webm" width="375" height="280" controls 
↵autoplay loop></video>

Autoplay and an infinite loop! We just need to remove the native controls and we’d have a trifecta of worst practices.

Of course, there are some situations where loop can be useful: imagine a browser-based game, in which ambient sounds and music should play continuously as long as the page is open.

The preload Attribute

In contrast to the two previous attributes, preload could definitely come in handy in a number of cases. The preload attribute accepts one of three values:

auto

A value of auto indicates that the video and its associated metadata will start loading before the video is played. This way, the browser can start playing the video more quickly when the user requests it.

none

A value of none indicates that the video shouldn’t load in the background before the user presses play.

metadata

This works like none, except that any metadata associated with the video (for example, its dimensions, duration, and the like) can be preloaded, even though the video itself won’t be.

This particular attribute does not have a spec-defined default in cases where it’s omitted; each browser decides which of those three values should be the default state, which makes sense. It allows desktop browsers to preload the video and/or metadata automatically (having no real adverse effect) while permitting mobile browsers to default to either metadata or none, as many mobile users have restricted bandwidth and will prefer to have the choice of downloading the video or not.

The poster Attribute

When you go to view a video on the Web, normally a single frame of the video will display in order to provide a teaser of its content. The poster attribute makes it easy to choose such a teaser. This attribute, similar to src, will point to an image file on the server by means of a URL.

Here’s how our video element would look with a poster attribute defined:

<video src="example.webm" width="375" height="280" 
↵poster="teaser.jpg" controls></video>

Although the poster attribute is useful, there’s a bug in iOS3 (corrected in iOS4) that prevents playback of the video if this attribute is present. If you know that many of your visitors use iOS 3.x, you should either avoid using the poster attribute, or remove it for those devices specifically.

The audio Attribute

The audio attribute attribute controls the default state of the audio track for the video element, and currently accepts only a single possible value: muted. The spec states that other values are likely to be added in the future, for specifying the default audio track or volume, for example.

A value of muted will cause the video’s audio track to default to muted, potentially overriding any user preferences. This will only control the default state of the element—the user interacting with the controls, or JavaScript can change this.

Here it is added to our video element:

<video src="example.webm" width="375" height="280" 
↵poster="teaser.jpg" audio="muted"></video>

Adding Support for Multiple Video Formats

As we discussed earlier, using a single container format to serve your video is not currently an option, even though that’s really the ultimate idea behind having the video element, and one which we hope will be realized in the future. To allow inclusion of multiple video formats, the video element allows source elements to be defined so that you can allow every user agent to display the video using the format of its choice. These elements serve the same function as the src attribute on the video element; so if you’re providing source elements, there’s no need to specify a src for your video.

Taking current browser support into consideration, here’s how we might declare our source elements:

<source src="example.mp4" type="video/mp4">
<source src="example.webm" type="video/webm">
<source src="example.ogv" type="video/ogg">

The source element (oddly enough) takes a src attribute that specifies the location of the video file. It also accepts a type attribute that specifies the container format for the resource being requested. This latter attribute allows the browser to determine if it can play the file in question, thus preventing the browser from unnecessarily downloading an unsupported format.

The type attribute also allows a codec parameter to be specified, which defines the video and audio codecs for the requested file. Here’s how our source elements will look with the codecs specified:

<source src="example.mp4" 
↵type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
<source src="example.webm" type='video/webm; codecs="vp8, vorbis"'>
<source src="example.ogv" type='video/ogg; codecs="theora, vorbis"'>

You’ll notice that the syntax for the type attribute has been slightly modified to accommodate the container and codec values. The double quotes surrounding the values have been changed to single quotes, and another set of nested double quotes is included specifically for the codecs.

This can be a tad confusing at first glance, but in most cases you’ll just be copying and pasting those values once you have a set method for encoding the videos (which we’ll touch on later in this chapter). The important point is that you define the correct values for the specified file to ensure that the browser can determine which (if any) file it will be able to play.

source Order

In our example above, the MP4/H.264/AAC container/codec combination is included first. This is to ensure that the video will play on the iPad. On that device, a bug causes only the first source element to be recognized. It’s safe to assume that this bug is fixed in subsequent versions of the iPad, but for now it’s necessary to include the MP4/H.264 file first to ensure compatibility.

The first source element will be recognized by IE9, Safari, and older versions of Chrome, so that covers quite a large chunk of our HTML5-ready audience.

The next element in the list defines the WebM/VP8/Vorbis container/codec combination. This is supported by later versions of Chrome that will eventually drop support for H.264. In addition to Chrome, WebM video will also play in Firefox 4 and Opera 10.6.

Finally, the third source element declares the Ogg/Theora/Vorbis container/codec combination, which is supported by Firefox 3.5 and Opera 10.5. Although other browsers also support this combination, they’ll be using the other formats since they appear ahead of this one in the source order. The browsers that support only this combination are older versions of browsers whose current versions support other formats, so it will be possible to drop this format once those versions become sufficiently rare.

These three source elements are placed as children of the video element, so with our three file formats declared, our code will now look like this:

index.html (excerpt)
<video width="375" height="280" poster="teaser.jpg" audio="muted">
  <source src="example.mp4" type='video/mp4; 
↵codecs="avc1.42E01E, mp4a.40.2"'>
  <source src="example.webm" type='video/webm; 
↵codecs="vp8, vorbis"'>
  <source src="example.ogv" type='video/ogg; 
↵codecs="theora, vorbis"'>
</video>

You’ll notice that our code above is now without the src attribute on the video element. As well as being redundant, it would also override any video files defined in the source elements.

What about Internet Explorer 6–8?

The three source elements that we included inside our video element will cover all modern browsers. But we’re yet to ensure that our video will play for a potentially large portion of our audience. As mentioned earlier, a significant percentage of users are still using browsers without native support for HTML5 video. Most of those users are on some version of Internet Explorer prior to version 9.

In keeping with the principle of graceful degradation, the HTML5 video element has been designed so that older browsers can access the video by some alternate means. Older browsers that fail to recognize the video element will simply ignore it, along with its source children. But if the video element contains content that the browser recognizes as valid HTML, it will read and display that content instead.

What kind of content can we serve to those nonsupporting browsers? According to Adobe, 99% of users have the Flash plugin installed on their systems. Additionally, most of those instances of the Flash plugin are version 9 or later, which offer support for the MPEG-4 video container format. To allow Internet Explorer 6 to 8 (and other older browsers lacking support for HTML5 video) to play the video, we declare an embedded Flash video to use as a fallback. Here’s the completed code for the video on The HTML5 Herald, with the Flash fallback code included:

index.html (excerpt)
<video width="375" height="280" poster="teaser.jpg" audio="muted">
  <source src="example.mp4" type='video/mp4; 
↵codecs="avc1.42E01E, mp4a.40.2"'>
  <source src="example.webm" type='video/webm; 
↵codecs="vp8, vorbis"'>
  <source src="example.ogv" type='video/ogg; 
↵codecs="theora, vorbis"'>
  <!-- fallback to Flash: -->
  <object width="375" height="280" type="application/x-shockwave-
↵flash" data="mediaplayer-5.5/player.swf">
    <param name="movie" value="mediaplayer-5.5/player.swf">
    <param name="allowFullScreen" value="true">
    <param name="wmode" value="transparent">
    <param name="flashvars" value="controlbar=over&amp;image=
↵images/teaser.jpg&amp;file=example.mp4">
    <!-- fallback image -->
    <img src="teaser.jpg" width="375" height="280" alt="" 
↵title="No video playback capabilities">
  </object>
</video>

We’ll avoid going into a detailed discussion of how this newly added code works (this isn’t a Flash book, after all!), but here are a few points to note about this addition to our markup:

  • The width and height attributes on the object element should be the same as those on the video element.

  • We’re using the open source JW Player by LongTail Video to play the file; you can use whichever video player you prefer.

  • The Flash video code has a fallback of its own—an image file that displays if the code for the Flash video fails to work.

  • The fourth param element defines the file to be used (example.mp4); as mentioned, most instances of the Flash player now support video playback using the MPEG-4 container format, so there’s no need to encode another video format.

  • HTML5-enabled browsers that support HTML5 video are instructed by the spec to ignore any content inside the video element that’s not a source tag, so the fallback is safe in all browsers.

The last thing we’ll mention here is that, in addition to the Flash fallback content, you could also provide an optional “download video” link that allows the user to get a local copy of the video and view it at their leisure. This would ensure that nobody is left without a means to view the video.

MIME Types

If you find that you’ve followed our instructions closely and your videos still won’t play from your server, the issue could be related to the content-type information being sent.

Content-type, also known as the MIME type, tells the browser what kind of content they’re looking at. Is this a text file? If so, what kind? HTML? JavaScript? Is this a video file? The content-type answers these questions for the browser. Every time your browser requests a page, the server sends “headers” to your browser before sending any files. These headers tell your browser how to interpret the file that follows. Content-type is an example of one of the headers the server sends to the browser.

The MIME type for each video file that you include via the source element is the same as the value of the type attribute (minus any codec information). For the purpose of HTML5 video, we’re concerned with three MIME types. To ensure that your server is able to play all three types of video file, place the following lines of code in your .htaccess file (or the equivalent if you’re using a web server other than Apache):

AddType video/ogg  .ogv
AddType video/mp4  .mp4
AddType video/webm .webm

If this fails to fix your problem, you may have to talk to your host or server administrator to find out if your server is using the correct MIME types. To learn more about configuring other types of web servers, read the excellent article “Properly Configuring Server MIME Types” from the Mozilla Developer Network.

Note: What’s an .htaccess file?

An .htaccess file provides a way to make configuration changes on a per-directory basis when using the Apache web server. The directives in an .htaccess file apply to the directory it lives in and all subdirectories. For more on .htaccess files, see the Apache documentation.

Encoding Video Files for Use on the Web

The code we’ve presented for The HTML5 Herald is virtually bullet-proof, and will enable the video to be viewable by nearly everyone that sees the page. Because we need to encode our video in at least two formats (possibly three, if we want to), we need an easy way to encode our original video file into these HTML5-ready formats. Fortunately, there are some online resources and desktop applications that allow you to do exactly that.

Miro Video Converter is free software with a super-simple interface that offers the ability to encode your video into all the necessary formats for HTML5 video. It’s available for Mac and Windows.[7]

Simply drag a file to the window, or browse for a file in the customary way. A drop-down box offers options for encoding your video in Theora, WebM, or MPEG-4 format. There’s also an option for MP3 and a number of presets for device-specific video output.

There are a number of other options for encoding HTML5 video, but this should suffice to help you create the two (or three) files necessary for embedding video that 99% of users can view.

Creating Custom Controls

There’s another huge benefit to using HTML5 video compared to the customary method of embedding video with a third-party technology. With HTML5 video, the video element becomes a real part of the web page, rather than an inaccessible plugin. It’s as much a part of the web page as an img element or any other native HTML element would be. This means we can target the video element and its various components using JavaScript—and we can even style the video element with CSS.

As previously mentioned, each browser that supports HTML5 video embeds a native set of controls to help the user access the video content. These controls have a different appearance in each browser, which may vex those concerned with a site’s branding. No problem: by using the JavaScript API provided by the video element, we can create our own custom controls and link them up to the video’s behavior.

Custom controls are created using whichever elements you want—images, plain HTML and CSS, or even elements drawn using the Canvas API—the choice is yours. To harness this API, create your own custom controls, insert them into the page, and then use JavaScript to convert those otherwise static graphic elements into dynamic, fully functioning video controls.

Some Markup and Styling to Get Us Started

For our sample site, we’re going to build a very simple set of video controls to demonstrate the power of the new HTML5 video API. To start off, Figure 5.5 shows a screenshot of the set of controls we’ll be using to manipulate the video.

video API custom controls The simple set of video controls we’ll be building

Figure 5.5.  The simple set of video controls we’ll be building

Both of those buttons have alternate states: Figure 5.6 shows how the controls will look if the video is playing and the sound has been muted.

video API custom controls Our controls again, this time with the sound muted and the video playing

Figure 5.6.  Our controls again, this time with the sound muted and the video playing

Our controls have three components:

  • play/pause button

  • timer that counts forward from zero

  • mute/unmute button

In most cases, your custom video controls should have all the features of the default controls that various browsers natively provide. If your set of controls introduces fewer or inferior features, it’s likely you’ll end up frustrating your users.

For the purpose of introducing the API, rather than trying to mimic what the browsers natively do, we want to introduce the important parts of the video API gradually; this will allow you to get your feet wet while establishing a foundation from which to work.

We’ll be creating a very simple, yet usable, set of controls for our video. The main feature missing from our set of controls is the seek bar that lets the user “scrub” through the video to find a specific part. This means there will be no way to go back to the start of the video aside from refreshing the page. Other than that, the controls will function adequately—they’ll allow the user to play, pause, mute, or unmute the video.

Here’s the HTML we’ll be using to represent the different parts of the video controls:

index.html (excerpt)
<div id="controls" class="hidden">
  <a id="playPause">Play/Pause</a>
  <span id="timer">00:00</span>
  <a id="muteUnmute">Mute/Unmute</a>
</div>

We’ll avoid going into the CSS in great detail, but here’s a summary of what we’ve done (you can view the demo page’s source in the code archive if you want to see how it’s all put together):

  • The text in the play/pause and mute/unmute buttons is removed from view using the text-indent property.

  • A single CSS sprite image is used as a background image to represent the different button states (play, pause, mute, unmute).

  • CSS classes are being used to represent the different states; those classes will be added and removed using JavaScript.

  • The controls wrapper element is absolutely positioned and placed to overlay the bottom of the video.

  • We’ve given the controls a default opacity level of 50%, but on hover the opacity increases to 100% (we’ll be talking more about opacity in Chapter 6).

  • By default, the controls wrapper element is set to display: none using a class of hidden, which we’ll remove with JavaScript.

If you’re following along building the example, go ahead and style the three elements however you like. The appearance of the controls is really secondary to what we’re accomplishing here, so feel free to fiddle until you have a look you’re happy with.

Introducing the Media Elements API

Let’s go through the steps needed to create our custom controls, and in the process we’ll introduce you to some aspects of the video API. Afterwards, we’ll summarize some other methods and attributes from the API that we won’t be using in our controls, so you can have a good overview of what the API includes.

In order to work with our new custom controls, we’ll first cache them by placing them into JavaScript variables. Here are the first few lines of our code:

js/videoControls.js (excerpt)
var videoEl = $('video')[0],
  playPauseBtn = $('#playPause'),
  vidControls = $('#controls'),
  muteBtn = $('#muteUnmute'),
  timeHolder = $('#timer'),

Of course, caching our selections isn’t necessary, but it’s always best practice (for maintainability and performance) to work with cached objects, rather than needlessly repeating the same code to target various elements on the page.

The first line is targeting the video element itself. We’ll be using this videoEl variable quite a bit when using the API—since most API methods need to be called from the media element. The next four lines of code should be fairly familiar to you if you took note of the HTML that comprises our controls. Those are the four elements on the page that we’ll be manipulating based on user interaction.

Our first task is make sure the native controls are hidden. We could do this easily by simply removing the controls attribute from the HTML. But since our custom controls are dependent on JavaScript, visitors with JavaScript disabled would be deprived of any way of controlling the video. So we’re going to remove the controls attribute in our JavaScript, like this:

js/videoControls.js (excerpt)
videoEl.removeAttribute("controls");

The next step is to make our own custom controls visible. As mentioned earlier, we’ve used CSS to remove our controls from view by default. By using JavaScript to enable the visibility of the custom controls, we ensure that the user will never see two sets of controls.

So our next chunk of code will look like this:

js/videoControls.js (excerpt)
videoEl.addEventListener('canplaythrough', function () {
  vidControls.removeClass("hidden");
}, false);

This is the first place we’ve used a feature from the HTML5 video API. First, take note of the addEventListener method. This method does exactly what its name implies: it listens for the specified event occurring on the targeted element.

Important: But addEventListener isn’t cross-browser!

If you’re familiar with cross-browser JavaScript techniques, you probably know that the addEventListener method isn’t cross-browser. In this case, it poses no problem. The only browsers in use that lack support for addEventListener are versions of Internet Explorer prior to version 9—and those browsers have no support for HTML5 video anyway.

All we have to do is use Modernizr (or some equivalent JavaScript) to detect support for the HTML5 video API, and then only run the code for supporting browsers—all of which will support addEventListener.

In this case, we’re targeting the video element itself. The event we’re registering to be listened for is the canplaythrough event from the video API. According to the definition of this event in the spec:

The user agent estimates that if playback were to be started now, the media resource could be rendered at the current playback rate all the way to its end without having to stop for further buffering.

There are other events we can use to check if the video is ready, each of which has its own specific purpose. We’ll touch on some of those other events later in this chapter. This particular one ensures continuous playback, so it’s a good fit for us as we’d like to avoid choppy playback.

Playing and Pausing the Video

When the canplaythrough event fires, a callback function is run. In that function, we’ve put a single line of code that removes the hidden class from the controls wrapper, so now our controls are visible. Now we want to add some functionality to our controls. Let’s bind a click event handler to our play/pause button:

js/videoControls.js (excerpt)
playPauseBtn.bind('click', function () {
  if (videoEl.paused) {
    videoEl.play();
  } else {
    videoEl.pause();
  }
});

When the button is clicked, we run an if/else block that’s using three additional features from the video API. Here’s a description of all three:

The paused attribute is being accessed to see if the video is currently in the “paused” state. This doesn’t necessarily mean the video has been paused by the user; it could equally just represent the start of the video, before it’s been played. So this attribute will return true if the video isn’t currently playing.

Since we’ve now determined that the play/pause button has been clicked, and the video is not currently playing, we can safely call the play() method on the video element. This will play the video from its last paused location.

Finally, if the paused attribute doesn’t return true, the else portion of our code will fire, and this will trigger the pause() method on the video element, stopping the video.

You may have noticed that our custom controls have no “stop” button (customarily represented by a square icon). You could add such a button if you feel it’s necessary, but many video players don’t use it since the seek bar can be used to move to the beginning of the video. The only catch is that the video API has no “stop” method; to counter this, you can cause the video to mimic the traditional “stop” behavior by pausing it and then sending it to the beginning (more on this later).

You’ll notice that something’s missing from our if/else construct. Earlier, we showed you a couple of screenshots displaying the controls in their two states. We need to use JavaScript to alter the background position of our sprite image; we want to change the button from “play me” to “pause me.”

Here’s how we’ll do that:

js/videoControls.js (excerpt)
videoEl.addEventListener('play', function () {
  playPauseBtn.addClass("playing");
}, false);
videoEl.addEventListener('pause', function () {
  playPauseBtn.removeClass("playing");
}, false);

Here we have two more uses of the addEventListener method (you’ll need to get used to it if you’re going to use the video and audio APIs!). The first block is listening for play events. So if the click handler we wrote triggers the play() method (or if something else causes the video to play, such as some other code on the page), the play event will be detected by the listener and the callback function will run.

The same thing is happening in the second block of code, except that it’s listening for the pause event (not to be confused with the paused attribute).

If the element has been played, the first block will add the class playing to our play/pause button. This class will change the background position of the sprite on the play/pause button to make the “pause me” icon appear. Similarly, the second block of code will remove the playing class, causing the state of the button to go back to the default (the “play me” state).

You’re probably thinking, “why not just add or remove the playing class in the code handling the button click?” While this would work just fine for when the button is clicked (or accessed via the keyboard), there’s another behavior we need to consider here, demonstrated in Figure 5.7.

Some video controls are accessible via the context menu

Figure 5.7. Some video controls are accessible via the context menu

The menu above appears when you bring up the video element’s context menu. As you can see, clicking the controls on the video element isn’t the only way to play/pause or mute/unmute the video.

To ensure that the button states are changed no matter how the video element’s features are accessed, we instead listen for play and pause events (and, as you’ll see in a moment, sound-related events) to change the states of the buttons.

Note: Disabling the Context Menu

You may also be concerned that the video element’s context menu has an option for Save video as…. There’s been discussion online about how easy it is to save HTML5 video, and this could affect how copyrighted videos will be distributed. Some content producers might feel like avoiding HTML5 video for this reason alone.

Whatever you choose to do, just recognize the realities associated with web video. Most users who are intent on copying and distributing copyrighted video will find ways to do it, regardless of any protection put in place. There are many web apps and software tools that can easily rip even Flash-based video. You should also be aware that even if you do disable the context menu on the video element, the user can still view the source of the page and find the location of the video file(s).

Some sites, like YouTube, have already implemented features to combat this when using HTML5 video. YouTube has a page that allows you to opt in to their HTML5 video trial. After opting in, when you view a video and open the video element’s context menu, there’s a custom context menu. The “Save Video As…” option is still present. But not so fast! If you choose this option, (as of this writing) you’ll be “rickrolled.” Sneaky!

YouTube also dynamically adds the video element to the page, so that you’re unable to find the URL to the video file by poking around in the source.

So, realize that you do have options, and that it’s possible to make it more difficult (but not impossible) for users to rip your copyrighted videos. But also recognize there are drawbacks to changing user expectations, in addition to the performance and maintainability issues associated with convoluting your scripts and markup for what could be little, if any, gain.

Muting and Unmuting the Video’s Audio Track

The next bit of functionality we want to add to our script is the mute/unmute button. This piece of code is virtually the same as what was used for the play/pause button. This time, we’ve bound the click event to the mute/unmute button, following with a similar if/else construct:

js/videoControls.js (excerpt)
muteBtn.bind('click', function () {
  if (videoEl.muted) {
    videoEl.muted = false;
  } else {
    videoEl.muted = true;
  }
});

This block of code introduces a new part of the API: the muted attribute. After the mute button is clicked, we check to see the status of this attribute. If it’s true (meaning the sound is muted), we set it to false (which unmutes the sound); if it’s false, we set its status to true.

Again, we haven’t done any button state handling here, for the same reasons mentioned earlier when discussing the play/pause buttons; the context menu allows for muting and unmuting, so we want to change the mute button’s state depending on the actual muting or unmuting of the video, rather than the clicking of the button.

But unlike the play/pause button, we don’t have the ability to listen for mute and unmute events. Instead, the API offers the volumechange event:

js/videoControls.js (excerpt)
videoEl.addEventListener('volumechange', function () {
  if (videoEl.muted) {
    muteBtn.addClass("muted");
  } else {
    muteBtn.removeClass("muted");
  }
}, false);

Again, we’re using an event listener to run some code each time the specified event (in this case a change in volume) takes place. As you can probably infer from the name of this event, the volumechange event isn’t limited to detecting muting and unmuting; it can detect volume changes.

Once we have detected the change in volume, we check the status of the video element’s muted attribute, and we change the class on the mute/unmute button accordingly.

Responding When the Video Ends Playback

The code we’ve written so far will allow the user to play and pause the video, as well as mute and unmute the sound. All of this is done using our custom controls.

At this point, if you let the video play to the end, it will stop on the last frame. We think it’s best to send the video back to the first frame, ready to be played again. This gives us the opportunity to introduce two new features of the API:

js/videoControls.js (excerpt)
videoEl.addEventListener('ended', function () {
  videoEl.currentTime = 0;
  videoEl.pause();
}, false);

This block of code listens for the ended event, which tells us that the video has reached its end and stopped. Once we detect this event, we set the video’s currentTime property to zero. This property represents the current playback position, expressed in seconds (with decimal fractions).

Which brings us to the next step in our code.

Updating the Time as the Video Plays

Now for the last step: we want our timer to update the current playback time as the video plays. We’ve already introduced the currentTime property; we can use it to update the content of our #timeHolder element. Here’s how we do it:

js/videoControls.js (excerpt)
videoEl.addEventListener('timeupdate', function () {
  timeHolder[0].innerHTML = secondsToTime(videoEl.currentTime);
}, false);

In this case, we’re listening for timeupdate events. The timeupdate event fires each time the video’s time changes, which means even a fraction of a second’s change will fire this event.

This alone would suffice to create a bare-bones timer. Unfortunately, the time would be unhelpful, and ugly on the eye because you’d see the time changing every millisecond to numerous decimal places, as shown in Figure 5.8.

currentTime property Using the currentTime property directly in our HTML is less than ideal

Figure 5.8.  Using the currentTime property directly in our HTML is less than ideal

In addition, the timer in this state will not display minutes or hours, just seconds—which could end up being in the hundreds or thousands, depending on the length of the video. That’s impractical, to say the least.

To format the seconds into a more user-friendly time, we’ve written a function called secondsToTime(), and called it from our timeupdate handler above. We don’t want to show the milliseconds in this case, so our function rounds the timer to the nearest second. Here’s the start of our function:

js/videoControls.js (excerpt)
var h = Math.floor(s / (60 * 60)),
  dm = s % (60 * 60),
  m = Math.floor(dm / 60),
  ds = dm % 60,
  secs = Math.ceil(ds);

After those five lines of code, the final variable secs will hold a rounded number of seconds, calculated from the number of seconds passed into the function.

Next, we need to ensure that a single digit amount of seconds or minutes is expressed using 05 instead of just 5. The next code block will take care of this:

js/videoControls.js (excerpt)
if (secs === 60) {
  secs = 0;
  m = m + 1;
}

if (secs < 10) {
  secs = "0" + secs;
}

if (m === 60) {
  m = 0;
  h = h + 1;
}

if (m < 10) {
  m = "0" + m;
}

Finally, we return a string that represents the current time of the video in its correct format:

js/videoControls.js (excerpt)
if (h === 0) {
  fulltime = m + ":" + secs;
} else {
  fulltime = h + ":" + m + ":" + secs;
}

return fulltime;

The if/else construct is included to check if the video is one hour or longer; if so, we’ll format the time with two colons. Otherwise, the formatted time will use a single colon that divides minutes from seconds, which will be the case in most circumstances.

Remember where we’re running this function. We’ve included this inside our timeupdate event handler. The function’s returned result will become the content of the timeHolder element (which is the cached element with an id of timer):

js/videoControls.js (excerpt)
timeHolder[0].innerHTML = secondsToTime(videoEl.currentTime);

Because the timeupdate event is triggered with every fraction of a second’s change, the content of the timeHolder element will change rapidly. But because we’re rounding the value to the nearest second, the visible changes will be limited to a time update every second, even though technically the content of the timer element is changing more rapidly.

And that’s it, our custom controls are done. The buttons work as expected and the timer runs smoothly. As we mentioned at the top, this isn’t quite a fully functional set of controls. But you should at least have a good handle on the basics of interacting with HTML5 video from JavaScript, so have a tinker and see what else you can add.

Further Features of the Media Elements API

The API has much more to it than what we’ve covered here. Here’s a summary of some events and attributes that you might want to use when building your own custom controls, or when working with video and audio elements.

One point to remember is that these API methods and properties can be used anywhere in your JavaScript—they don’t need to be linked to custom controls. If you’d like to play a video when the mouse hovers over it, or use audio elements to play various sounds associated with your web application or game, all you need to do is call the appropriate methods.

Events

We’ve already seen the canplaythrough, play, pause, volumechange, ended, and timeupdate events. Here are some of the other events available to you when working with HTML5 video and audio:

canplay

This is similar to canplaythrough, but will fire as soon as the video is playable, even if it’s just a few frames. (This contrasts with canplaythrough, as you’ll remember, which only fires if the browser thinks it can play the video all the way to the end without rebuffering.)

error

This event is sent when an error has occurred; there’s also an error attribute.

loadeddata

The first frame of the media has loaded.

loadedmetadata

This event is sent when the media’s metadata has finished loading. This would include dimensions, duration, and any text tracks (for captions).

playing

This indicates that the media has begun to play. The difference between playing and play is that play will not be sent if the video loops and begins playing again, whereas playing will.

seeking

This is sent when a seek operation begins. It might occur when a user starts to move the seek bar to choose a new part of the video or audio.

seeked

This event fires when a seek operation is completed.

Attributes

In addition to the attributes we’ve already seen, here’s a number of useful ones available to you:

playbackRate

The default playback rate is 1. This can be changed to speed up or slow down playback. This is naturally of practical use if you’re creating a fast-forward or rewind button, or a slow-motion or slow-rewind button.

src

As its name implies, this attribute returns the URL that points to the video being played. This only works if you’re using the src attribute on the video element.

currentSrc

This will return the value of the URL pointing to the video file being played, whether it’s from the video element’s src attribute or one of the source elements.

readyState

This attribute returns a numeric value from 0 to 4, with each state representing the readiness level of the media element. For example, a value of “1” indicates that the media’s metadata is available. A value of “4” is virtually the same as the condition for firing the canplaythrough event, meaning the video is ready to play, and won’t be interrupted by buffering or loading.

duration

This returns the length of the video in seconds.

buffered

This represents the time ranges of the video that have buffered and are available for the browser to play.

videoWidth, videoHeight

These values return the intrinsic dimensions of the video, the actual width and height as the video was encoded—not what’s declared in the HTML or CSS. Those values can be accessed through the customary width and height attributes.

You can also access attributes that are able to be declared directly in the HTML, such as preload, controls, autoplay, loop, and poster.

What about audio?

Much of what we’ve discussed in relation to HTML5 video and its API also apply to the audio element, with the obvious exceptions being those related to visuals.

Similar to the video element, the preload, autoplay, loop, and controls attributes can be used (or not used!) on the audio element.

The audio element won’t display anything unless controls are present, but even if the element’s controls are absent, the element is still accessible via scripting. This is useful if you want your site to use sounds that aren’t tied to controls presented to the user. The audio element nests source tags, similar to video, and it will also treat any child element that’s not a source tag as fallback content for nonsupporting browsers.

As for codec/format support, Firefox, Opera, and Chrome all support Ogg/Vorbis; Safari, Chrome, and IE9 support MP3; and every supporting browser supports WAV. Safari also supports AIFF. At present, MP3 and Ogg/Vorbis will be enough to cover you for all supporting browsers.

Accessible Media

In addition to their status as first-class citizens of the page, making them intrinsically more keyboard accessible (using tabindex, for example), the HTML5 media elements also give you access to the track element to display captions or a transcript of the media file being played. Like source elements, track elements should be placed as children of the video or audio element.

The track element is still in flux, but if included as a child of the video element, it would look like the example shown here (similar to an example given in the spec):

<video src="brave.webm">
  <track kind="subtitles" src="brave.en.vtt" srclang="en" 
↵label="English">
  <track kind="captions" src="brave.en.vtt" srclang="en" 
↵label="English for the Hard of Hearing">
  <track kind="subtitles" src="brave.fr.vtt" srclang="fr" 
↵label="Français">
  <track kind="subtitles" src="brave.de.vtt" srclang="de" 
↵label="Deutsch">
</video>

The code here has four track elements, each referencing a text track for captions in a different language (or, in the case of the second one, alternate content in the same language).

The kind attribute can take one of five values: subtitles, captions, descriptions, chapters, and metadata. The src attribute is required, and points to an external file that holds the track information. The srclang attribute specifies the language. Finally, the label attribute gives a user-readable title for the track.

As of this writing, the track element is yet to be supported by any browser. For more info on this new element, see the W3C spec.

It’s Showtime

Video and audio on the Web have long been the stronghold of Flash, but, as we’ve seen, HTML5 is set to change that. While the codec and format landscape is presently fragmented, the promises of fully scriptable multimedia content, along with the performance benefits of running audio and video natively in the browser instead of in a plugin wrapper, are hugely appealing to web designers, developers, and content providers.

Because we have access to nearly foolproof fallback techniques, there’s no reason not to start experimenting with these elements now. At the very least, we’ll be better prepared when support is more universal.

We’ve now covered just about everything on HTML5 “proper” (that is, the bits that are in the HTML5 spec). In the next few chapters, we’ll turn our attention to CSS3, and start to make The HTML5 Herald look downright fancy. After that, we’ll finish by looking at the new JavaScript APIs that are frequently bundled with the term “HTML5.”



[7] Linux users can use FFmpeg, the command-line utility on which Miro Video Converter is based.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset