Launching QuickTime movies in the player using ASP.NET AJAX framework

As my dearest friends know, I once existed as a broadcast monkey prior to my life as a code monkey. It gives me great joy that it's finally at least somewhat practical to deliver short-form high definition video via the Internet, even if it is only a couple of minutes at a time. That said, the best bang for the buck in terms of compression is easily H.264, encoded by way of QuickTime. Flash can actually play these files, but it's not practical to do it in-browser when you're talking about something that's 1280x720 or 1920x1080. That's probably why Apple spawns the movies in the player itself. It's a pretty seamless experience, and since "everyone" has iTunes, "everyone" has QuickTime. (If you want to get into some stupid religious debate about this, please, do it elsewhere.)

What's not nearly as obvious is how Apple was doing it. It took awhile to find the right article on the subject, and even then, it didn't quite align with what the movie trailer site was doing. QT movies can begin with a "poster movie," and that can be anything QT can "play," including a still image. For reasons still not clear to me, you need one of these even if you're not going to show any of the markup as rendered stuff in the browser.

The design goal was simple: Make it so that clicking on something launches QuickTime Player and plays a movie. What follows is my first attempt. Keep in mind that it's not particularly pretty, and it needs some refactoring. It's obviously not as efficient as it could be. Comments follow...

/// <reference name="MicrosoftAjax.debug.js" />

Type.registerNamespace("Video");

Video.QuickTimePackager = function() {
        Video.QuickTimePackager.initializeBase(this);
        this._Id = null;
        this._TargetElement = null;
        this._FileUrl = null;
        this._PosterImage = null;
        this._holder = null;
}

Video.QuickTimePackager.prototype = {
    initialize: function() {
        Video.QuickTimePackager.callBaseMethod(this, "initialize");
        $addHandlers(this.get_TargetElement(), { "click":this._make }, this);
        this._holder = document.createElement("div");
        this._holder.setAttribute("id", this.get_Id());
        document.body.appendChild(this._holder);
    },
   
    _make : function(e) {
        var o = document.createElement("object");
        o.setAttribute("codebase", "http://www.apple.com/qtactivex/qtplugin.cab#version=7,3,0,0");
        o.setAttribute("height", "1");
        o.setAttribute("width", "1");
        o.setAttribute("id", "outobject");
        this._createParam(o, "src", this.get_PosterImage());
        var i = document.createElement("object");
        i.setAttribute("data", this.get_PosterImage());
        i.setAttribute("type", "video/quicktime");
        i.setAttribute("height", "1");
        i.setAttribute("width", "1");
        i.setAttribute("id", "inobject");
        this._createParam(i, "href", this.get_FileUrl());
        this._createParam(i, "target", "QuickTimePlayer");
        this._createParam(i, "controller", "false");
        this._createParam(i, "autohref", "true");
        this._createParam(o, "href", this.get_FileUrl());
        this._createParam(o, "target", "QuickTimePlayer");
        this._createParam(o, "controller", "false");
        this._createParam(o, "autohref", "true");
        o.setAttribute("classid", "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B");
        this._holder.appendChild(o);
        if (navigator.userAgent.indexOf("MSIE") == -1 || navigator.userAgent.indexOf("Opera") != -1)
            o.appendChild(i);
    },
   
    _createParam : function(parent, name, value) {
        var param = document.createElement("param");
        param.setAttribute("name", name);
        param.setAttribute("value", value);
        parent.appendChild(param);
    },

    dispose : function() {
        Video.QuickTimePackager.callBaseMethod(this, "dispose");
    },
   
    get_Id : function() {
        return this._Id;
    },
    set_Id : function(value) {
        this._Id = value;
    },
   
    get_TargetElement : function() {
        return this._TargetElement;
    },
    set_TargetElement : function(value) {
        this._TargetElement = value;
    },
   
    get_FileUrl : function() {
        return this._FileUrl;
    },
    set_FileUrl : function(value) {
        this._FileUrl = value;
    },
   
    get_PosterImage : function() {
        return this._PosterImage;
    },
    set_PosterImage : function(value) {
        this._PosterImage = value;
    }
}

Video.QuickTimePackager.registerClass("Video.QuickTimePackager", Sys.Component);

And then to wire it up to whatever object you want to be clickable:

Sys.Application.add_init(function() {
    $create(Video.QuickTimePackager, { "Id":"hd", "TargetElement":$get("fauxButton"), "FileUrl":"http://www.popw.com/video/kittehfight.mov", "PosterImage":"/images/blank.png" }, null, null, null);
});

The first piece of code is a Sys.Component, which you can drop in an external file and/or have it loaded by ScriptManager. I dumped it in a library as an embedded resource, but feel free to do it your way. The fragment at the end should live in script tags at the bottom of your page. Id is your name for the movie's container, where the object tags will be rendered, TargetElement is the actual element (not its ID) that you want to make clickable, FileUrl is the location of the movie and PosterImage is the poster movie you'll never see.

The init function simply adds a click handler to the DOM element you passed in with $create, and creates an empty div as the placeholder for the object tags. The _make method makes the object tags that force the sudden creation of a movie, specifiying the player as the target. This combination of parameters overall are necessary to get the movie to launch. Part of those requirements are the poster movie, which no one will ever see, but has to be there. I just pass in a blank 1x1 graphic.

The object within an object is a little strange, but this is how Apple does it on the trailer site as well. The outer object is what IE needs to work, while the inner one is what every other browser expects (including Opera, which impersonates IE sometimes, but we know better). Also of note is that, according to comments in the script library that Apple uses, the classid attribute has to be added last, or IE won't see the rest of the parameters. That seemed to be true as I experimented. The rendered stuff will launch the player with the movie, and off you go, spreading your glorious HD porn, er, nature video, across the Series of Tubes.

Again, it needs a little clean up, but it's a nice reusable piece for you to plant your HD, spawned QT movies in your ASP.NET page.

1 Comment

Comments have been disabled for this content.