As you’ve probably read somewhere on the interwebs, HTML5 is bringing native video support to browsers. This will enable us to embed a video file directly in our HTML, much like a SWF or image.

Background

You may have also heard that there’s currently a big controversy over what kind of video files will be supported. The defacto standard is MP4/H.264, which is used by Adobe in their Flash video format, and by huge media sites like YouTube. Mozilla, the makers of Firefox, refuse to support the MP4/H.264 standard because it isn’t open-source and free from licensing constraints.

Turns out H.264 is not public domain. Although the company that owns the H.264 patent has temporarily agreed to waive royalty fees for the next decade or so, they reserve the right to charge fees later on. Mozilla says no way, we will only support a video format that is free from licensing issues and has no patent holders (because patent holders can decide to sue some day). Mozilla supports the completely free/open-source Ogg format.

Apple and Adobe, already knee-deep in MP4/H.264 with their Quicktime and Flash video products, vow to press on with H.264.  Google also supports H.264 because YouTube relies on it, and because Google’s new Chrome browser is based on the WebKit project, which has Apple as a main code contributor. In case you haven’t noticed, Apple, Adobe and Google have pretty much cornered the internet video market the past few years, so if they’re throwing their support behind H.264, you can count on it being around for a while. Not to mention that many mobile devices, including the iPhone and most Android phones, have hardware that is designed specifically to support H.264 video, enabling smoother playback and less battery drain than non-dedicated hardware.

(For what it’s worth, Opera is in agreement with Mozilla and supports Ogg. However, not many people seem to pay attention to Opera these days, so they don’t appear to have much influence in this race. Microsoft has endorsed H.264 with it’s upcoming IE9 browser, but it won’t be available for some time.)

The problem

Firefox and Opera are essentially forcing websites to offer two versions of each video: an Ogg version and an MP4 version. In my opinion — and the opinion of many others — this simply will not do. Providing two different video files is not realistic, Ogg’s quality is inferior to H.264, and many computers and mobile devices have direct hardware support for H.264 but not Ogg. In reality, without MP4 support, HTML5 video is rendered useless for most site developers in Firefox and Opera.

The most logical workaround is to code <video> elements to work for MP4 and have a Flash Player-based fallback for older browsers and browsers that only support Ogg. Since the <video> element is designed to allow for fallback content, just like the <object> element, we can do this:

<video id="myvideo" width="480" height="360" controls>
    <source src="/video/file.m4v" type="video/mp4"></source>
    <object data="flash-video-player.swf" type="application/x-shockwave-flash" width="480" height="360">  
        <param value="flash-video-player.swf" name="movie"/>  
        <param value="file=/video/file.m4v" name="flashvars"/>
    </object>
</video>Code language: HTML, XML (xml)

This works fine in Safari, Chrome, Internet Explorer and older versions of Firefox and Opera that don’t support the <video> element. However, Firefox 3.6 and Opera 10.5 do something very irritating: even though they KNOW their <video> doesn’t support “video/MP4”, they load the <video> element anyway. Which is … like … OMG so duh … because the video can’t possibly play!

If the <video> element is loaded, Firefox and Opera will never load the fallback <object> containing the Flash-based fallback.

Because this behavior cannot be fixed with markup, we’re forced find a scripted workaround (notice that we haven’t used a single bit of JavaScript yet). Thankfully, there’s a pretty straightforward solution: Delete the <video> element in Firefox and Opera.

A Solution

Here’s a simple script that will detect whether HTML 5 video is supported, and if it is, will check to see if MP4 is supported. If HTML5 video is supported but MP4 is NOT supported, the script deletes the specified <video> element but leaves the Flash fallback in its place.

var detectVideoSupport = function (){
    var detect = document.createElement('video') || false;
    this.html5 = detect && typeof detect.canPlayType !== "undefined";
    this.mp4 = this.html5 && (detect.canPlayType("video/mp4") === "maybe" || detect.canPlayType("video/mp4") === "probably");
    this.ogg = this.html5 && (detect.canPlayType("video/ogg") === "maybe" || detect.canPlayType("video/ogg") === "probably");
    return this;
};

var replaceVideoWithObject = function (video_id){    
    if(!video_id){ return false; }
    var video = document.getElementById(video_id);
    if(video){
        var obj = video.getElementsByTagName("object")[0];
        if(obj){
            var obj_copy = obj.cloneNode(true);
            video.parentNode.insertBefore(obj_copy, video);
            video.parentNode.removeChild(video);
        }
    }
};

window.onload = function (){
    var video = detectVideoSupport();
    //Both Opera and Firefox support OGG but lack MP4 support
    if(video.ogg && !video.mp4){
        replaceVideoWithObject("myvideo");
    }
};Code language: JavaScript (javascript)

Functioning demo.

A few notes

Tested successfully in:

  • Windows XP: Firefox 3.0, Firefox 3.5.8, Internet Explorer 6, Internet Explorer 8, Google Chrome 4.1.2
  • Windows Vista: Internet Explorer 7
  • Mac OS X (10.6): Firefox 3.6, Safari 4.01, Chrome 5 (beta), Opera 10.1, Opera 10.5b

(Note: IE6, IE7 & IE8 give an unexplained “object required” error in the demo page, but everything works fine. I will investigate as time permits.)

This demo uses the JW Media Player as the Flash fallback video player, but you can use any Flash-based video player on your page.

This demo doesn’t do any Flash Player version detection to ensure the visitor has a supported version of Flash Player. If you need to add Flash Player version detection, you can use SWFObject to embed your Flash file.

Update April 3, 2010: This post was updated to add Opera 10.5 to the list of non-behaving browsers and remove Firefox user agent sniffing.

Similar Posts

21 Comments

  1. Useful code, but doesn’t this take us back to the bad old days of browser detection to get around flaws in a particular browser (not naming any names IE6)?

    I guess its inevitable whilst a standard emerges, just makes me feel that if workarounds like this prevail then

    a) Firefox won’t be under any pressure to offer H.264
    and
    b) Adobe still wins as everyone will use Flash as a fall back… inevitabley meaning that lazy people will just Flash to encapsulate their video as it means all their problems are solved in one.

  2. I completely understand your point, but we’re already doing browser sniffing on a grand scale via frameworks like jQuery, YUI, Dojo and MooTools. The difference with the frameworks is that the sniffing is done behind a curtain, so you never have to think about it.

    Personally, I believe Mozilla is feeling a ton of pressure to support H.264 and will cave. With almost everyone skipping OGG as a video format, Firefox becomes less relevant for people’s needs. Mozilla may be non-profit, but they don’t want to become irrelevant, esp. with IE9 on its way and Chrome stealing its user base.

    RE: Adobe and lazy people, the solution presented in this post (which will be revised soon, stay tuned!) enables savvy developers to use HTML5 video as a first option and Flash as a fallback. That means the videos work on mobile devices (iPhone, Android etc) without requiring Flash. I think that’s a strong enough incentive to get developers to choose HTML5 first — and why Apple refuses to put Flash Player on iPhones.

    RE: the “bad old days,” Browser Wars II has been afoot for quite some time now; the difference this time is that we’re making efforts to make it less painful for end users by providing useful alternatives or scripted workarounds.

    For example, HTML5 features are not evenly implemented in the various browsers, so there is a lot of detection going on at the moment for features such as local storage, HTML5 form elements, CSS3 support and so on. This pipwerks blog uses the famous ‘HTML5 shim’ to enable me to use HTML5 page elements such as article and section in older browsers.

  3. As of jQuery 1.3, the library contains no more browser sniffing (preferring feature detection). See http://docs.jquery.com/Release:jQuery_1.3

    This script does a nice bit of feature detection (good), but then falls back on browser sniffing (bad). Of course finding a way to detect this “feature” is the challenge …

    So far, this is the only solution I’ve found – thanks!

    1. Believe me, I hate browser sniffing as much as the next person, but it’s sometimes required. On a side note, MooTools has recently shifted positions and will use browser sniffing instead of feature detection in its next release. This is due to the difficulties they’ve had with feature detection across versions of browsers, esp. Firefox (3.6 broke their existing feature detection).

  4. Firefox does what the spec requires. Opera, Safari and Chrome do as well. The reason you saw the fallback in Opera is because you tested Opera 10.10 which doesn’t support video. Try Opera 10.50.

    Why are you sniffing for Firefox? It’s not needed. If you just remove the sniffing, it works everywhere.

    BTW, type is not a valid attribute on video. You might want the source element.

    1. Opera 10.5 behaves identically to Firefox 3.6 — it fails to show the fallback content if the video element is present.

      As for the ‘type’ attribute, I was unaware it wasn’t official (oops) but further testing proves that simply using the source element as you recommend does NOT make a difference. Here are test pages to prove it:

      All three of these test pages fail in Firefox 3.6 and Opera 10.5b (both Mac OS X). If I remove the script fix, the Flash fallback does not display.

      On a side note, I have already created a revised version of the script that takes Opera into account. I haven’t had time to post it yet, but will try and post it this weekend. The edits have been incorporated into this post and accompanying demo files.

      Update: The demo page that left out the ‘type’ attribute in the source element failed when I tested in Safari on the iPhone. So it appears the safest route is to use the video element with a source child element, and be sure the source element includes the ‘type’ attribute.

  5. This is great. The only problem is that it does not display on the iPad. Not sure why. Trying to figure out a solution.

    1. Unfortunately, I am not privileged enough to test on an iPad… yet. 😉 It works on my iPhone and iPod Touch, though it displays a link to the Apple Quicktime video player rather than displaying the video in the HTML page itself.

      Thanks for the info, and please let me know if you make any discoveries!

  6. Thank you help! 🙂

    I modified a line and it’s works:

    //document.body.removeChild(video);
    video.parentNode.removeChild(video);

  7. I tested this on the iPhone Simulator in iPad mode, and it worked, so I think you’re safe!

    I was using this method for a client today, and since I was already using jQuery on their site I came up with a little jQuery snippet to remove the video element (hopefully the comment system doesn’t mash this code):

    $(function(){
    vid = $(‘#myvideo’);
    if(vid.length && !vid.get(0).currentSrc)
    {
    vid.children(‘object’).unwrap()
    }
    })

    It turns out you can check for the ‘currentSrc’ attribute on the actual video element (once the DOM is loaded). If it has no currentSrc, that means it couldn’t find a supported format to play, so it gets “unwrapped”.

    It seems like this method of detection would be the most broad solution to the problem – you can include whatever video types you like, and if the browser doesn’t support them, it will fall back to Flash.

  8. I’m loathe to post again, but I can’t delete my past pending comments and I’m still fighting with this code.

    Right now what happens in firefox is that it displays the flash fallback and then underneath, displays a broken ogg-less html5 player window. Has anyone else had this issue? It could be a quirk of the css that I use, but I don’t have any styling on the video tag itself.

  9. Interesting, I made the:
    //document.body.removeChild(video);
    video.parentNode.removeChild(video);
    modification someone above recommended and it fixed it.

    My page is here: http://wileywiggins.com/catalog/

    with document.body.removeChild(video); in there it would inject the flash fallback, but the broken video element would still stay in the dom.

    I have a second video on the page that I would like to do the same thing to, what’s the correct way to add a second id to your javascript (sorry, I am a novice javascripter).

  10. yes, the video.parentNode.removeChild(video); edit is a good one, I keep forgetting to update my script.

    if you have multiple videos, just invoke replaceVideoWithObject for each one.

    window.onload = function (){
        var video = detectVideoSupport();
        if(video.ogg && !video.mp4){
            replaceVideoWithObject("myFirstVideoID");
            replaceVideoWithObject("mySecondVideoID");
            replaceVideoWithObject("myThirdVideoID");
        }
    };
    
    1. @j-flex the point of the post is that supporting Ogg (or any system that requires maintaining two versions of a video) is unrealistic for most people.

Comments are closed.