While Silverlight 2 brings us great capabilities as .NET developers and opens many opportunities for creating rich clients in the browser, it still supports strong media features that have been available since the initial release of Silverlight.  The ability to deliver efficient, high quality media in the browser is an increasing need in a lot of sites producing content for their members.  Traditional ‘podcasts’ which were historically audio-only, are moving increasingly faster to richer media.  This is nothing new of course, but being able to quickly distribute the media on your sites efficiently and provide methods for your users to either embed content, or for you to deliver content to other sites means you need a predictable method for doing that.

In Silverlight 1, while it supported all the same features, one thing that I (and presumably you as well) didn’t find attractive was the deployment scenarios for delivering media content via players.  Silverlight 1 required the distribution of various scripts and initialization functions.  This wasn’t ideal for providing you with something you can just drop into a web page anywhere.  In fact, it was preventative in some places like MySpace, for example.

Silverlight 2 brings a new packaging model, the XAP (pronounced “zap”) file.  Essentially this is an archive file which contains a manifest, reference assemblies and any custom code you have created.  It can also contain resources that your application may use.  Because we have this format now, we aren’t restricted to delivering multiple files for our application…we can deliver our application in one file essentially (your mileage may vary depending on your needs).

Great, now we have a good packaging model, but what about drawing my own controls, etc.?

True, Silverlight 1 had a gap of native controls to the runtime, requiring you to be creative with XAML to deliver functionality.  In the typical media player scenario where you want a few timelines, volume bar, etc…even the trivial tasks required you to wire up some functionality.  Well, Silverlight 2 brings us a common set of controls now that we can leverage.

So let’s take a look at an implementation of a Silverlight 2 media player.  I’m going to use a base player created by Joel Neubeck who has been doing some really great posting on Silverlight lately.  In this example I’m going to try to articulate what Joel did with the base controls and demonstrate how he was able to create an effective use of styling and controls to create a re-usable player.  Additionally, I’m going to add a few new features to it for my desires.

I’m not going to go into the full details of this code, but wanted to point out the key features of what Joel accomplished as I think it was clever use of some controls you might not have considered to use in this scenario.  As well, I’ll show what I added and why.

The Controls

We’ll need a play/pause button, a track timeline, perhaps a download timeline, volume controls, and full-screen button.  We could do these in various ways.  For our buttons we could create canvas elements and add a lot of code to switch them on/off.  We could use a regular button and hide/show different ones.  I thought Joel made an excellent choice in choosing to use a ToggleButton.  The ToggleButton is still a button with all the same features, but supports more than just a normal/pressed state.  Essentially it supports a ‘checked’ state similar to a checkbox or something.  This is useful because we’re using this to basically provide play and pause capabilities.  By using a ToggleButton, we can essentially make the ‘normal’ state the Play view and the pressed (checked) state the Pause view. 

Okay, now for the tracks.  In Silverlight 1 we would have to use a few rectangles on top of each other, add our mouse capture code, etc.  In Silverlight 2, Joel saw the Slider as being an effective way of showing a linear timeline and it provides the functionality for value changing and a thumb for scrubbing, etc.  Perfect.  We’ll use the Slider for the download and volume features as well.

So once we add these to our design surface (Joel chose to create the media controls as a separate UserControl) it looks like this:

Skinning the Controls

Blah.  That looks like crap and rather button-sh.  Here is where skinning comes in.  Jesse just completed a good tutorial on skinning controls that provide a little more depth.  Using this techniques, we can define at a more granular level what we want our content views to be.  For example on the play/pause button, instead of a ‘button’, we replace the content with Path elements that are geometric shapes rather than a typical button look.  In combination now with implementing states in the styles (i.e., Normal State, Pressed State, Checked State) we can define that when the button is pressed (checked) what it shall do/look like by pre-defining the state.

We apply these same concepts to our other controls like the slider, able to skin the timeline area independent from the thumb scrubber, etc.  We put our styles in App.xaml so they are globally available to the application.  Do this with the volume control and other buttons (mute/full-screen) and now our exact same media controls (without affecting any real code that controls the logic), we now have this look:

which looks much more like what we are after. 

Now Joel’s initial example was fixed to a specific size of a media file.  I want my player to scale to whatever the container is.  Essentially I removed all specific sizes from XAML where appropriate and changed the media controls container grid so that the center column (where the timeline sliders are) will expand/contract as needed.  This gives us two features.  The first is that we have a re-usable control area regardless of the media element size.  The second is that when the user goes into full screen mode, the controls also expand to full screen appropriately.  The only thing you have to do is set the containing <div> where this media player is going to live to the desired size (i.e., 640x480) and the application will scale the internal elements to fill that space accordingly.  Full screen mode will as well adjust the controls to fill the screen.

I also added a bigger “Play” indicator (and buffering indicator) that would show in the center of the MediaElement when paused or buffering.  I felt this gave more visual cueing to what was going on with the media.  This also scales as it looks at the ActualWidth/Height properties and grabs the center points based on that.  Here’s the resulting paused view in two different sizes:

640x480 view

320x240 view

Providing Startup Parameters

The next thing I wanted to do is eliminate the hard-coding of the MediaElement Source property in the base XAML file.  I just created a player with all my needs and want to re-use it without recompiling, etc.  Here’s where InitParams comes in for developers.  One thing we can do to the plugin is pass startup parameters to it.  These are surfaced using the InitParams functionality.  You can specify a comma-delimited list of name/value pairs in this property like:

firstName=Tim,lastName=Heuer,loves=Silverlight

which in turn get put into a Dictionary object in code.  These elements are retrievable in your applications Application_Startup in the StartupEventArgs.  the Application_Startup is in the App.xaml file (you can think of this as a global.asax file if you are an ASP.NET developer).  There are different ways you can implement grabbing these values out.  You could pull values (or just one value) out using a simple retrieval of the key value:

string firstName = e.InitParams[“firstName”].ToString();

You could set this to a public static variable that could then be accessed by your application.  I chose to simply iterate through the initialization parameters and add them to the resources collection of the application.  I haven’t decided yet if this is the most effective use, but for now it works and was simple:

foreach (var item in e.InitParams)
{
    if (item.Key.ToLower() == "m")
    {
        string mediaParam = e.InitParams["m"].ToString();
        Uri mediaUri;

        if (mediaParam.Contains("://"))
        {
            mediaUri = new Uri(mediaParam, UriKind.RelativeOrAbsolute);
        }
        else
        {
            mediaUri = new Uri(mediaParam, UriKind.Relative);
        }

        this.Resources.Add("m", mediaUri);
    }
    else
    {
        this.Resources.Add(item.Key, item.Value);
    }
}

You’ll see that i check for an “m” parameter and do an additional checking on that.  I’m using this parameter for passing in the source for the MediaElement and need to check if it is an absolute URI or if the media will be local to the Silverlight application.  Then any other initialization params get added to the collection as well.  This might not make sense because I’m really only using two: source and autostart and if I wanted to make this re-usable, I would really need to build up more options and put them in the code too, or else nothing is being used.  I’ll likely do that later, so let’s move on.

Now that we have these items in our Resources collection, my app continues to load and set the MediaElement source to that value:

mediaPlayer.Source = App.Current.Resources["m"] as Uri;

As a // TODO I need to add some error checking and display the thumbnail if there is a problem perhaps, but I’ll get to that later.  Anything else I might need to do with the startup params I can get them out using the same method as above now that they are in my Resources collection.

So now in my control hosted page I have this:

<div style="width:640px;height:480px">
    <asp:Silverlight ID="Xaml1" runat="server" 
    InitParameters="m=LEADER_ST2.wmv,autostart=false" 
    Source="~/ClientBin/VideoPlayer.xap" Version="2.0" 
    Width="100%" Height="100%" />
</div>

Here I was using the asp:silverlight control which exposes the InitParameters property, but I could use the object tag directly:

<object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" 
        width="640" height="480">
    <param name="source" value="ClientBin/VideoPlayer.xap"/>
    <param name="onerror" value="onSilverlightError" />
    <param name="background" value="white" />
    <param name="initParams" value="m=LEADER_ST2.wmv" />
</object>

NOTE: In the “m” parameter it will use the standard path resolution that Silverlight respects.  For more information on this, see a great post by Pete Brown on the explanation.

Providing Alternate Content

As you can see we have provided no alternate content for the object tag implementation.  The asp:silverlight control also supports providing alternate content via the PluginNotInstalledTemplate property.  This is the responsibility of the web developer implementing any active content, whether it be Silverlight, Flash or other embeddable technology.  For more information on methods of doing this, read my blog post regarding a great deployment experience.

Summary

Now I have a media player that expands to the size it needs as well as is totally re-usable for me as I can provide startup parameters to it providing the media to play.  There is some polish that probably needs to occur, but as you can see, by using the native controls we get a lot of free functionality that we can tap into and by skinning the controls you get a much better look than the default and can even totally change the visual behavior of native controls.  Thanks to Joel for his great work using native controls and state parts and skins to create a great media player.  I’ve only made a few tweaks that I think were value add, but download the code and see what you can make with it. – all you need to do is change the styles and let’s see what you come up with!

Here’s the source as it is right now with my modifications to Joel’s existing work: VideoPlayer.zip.

UPDATE: Updated code using VisualStateManager and beta 2 styling method is here.

My new team does a lot of video conferencing (I think ScottHa has a picture of our last meeting somewhere on the internets).  I’m still getting used to it a bit, and network latency gives me the willies when there is such a delay in audio or choppiness.  Nonetheless I like being able to “see” people, so I haven’t written it off just yet.

My last 1:1 meeting with my boss also reminded me of geek sounds.  After talking about something he was going to fire off a message to another colleague to connect some dots.  So he paused and started typing.  I swear he uses this keyboard:

I recorded a quick snippet for my own archives…I love the sound of a solid keyboard:

It was too funny not to pass up, sorry Simon.

While you may not personally work with a lot of media solutions in your Silverlight application, it is nice to know the quality is there when you need it.  Silverlight supports the VC-1 codec for media which provides a standards implementation for high quality media.  I would imagine that most developers probably don’t know/care what all that means.  But if you are deploying a high-touch media solution (i.e., online TV, etc.) you want that high quality.

Our resident media expert, Ben Waggoner, just put up a great (and detailed) post about some ‘high-touch encoding’ techniques he uses and does some comparisons to some media outputs with FLV files as well.  There are some gory details for tweaks in the media outputs, much of which I won’t pretend to understand as an expert, but as a geek they seem to make sense :-).

One of the most compelling comparisons Ben notes is the quality from the VP6 (what FLV uses) and the VC-1 codec in a particular image…notice the shirt texture difference.  The VC-1 output in this sample is much more close to the original source.

FLV:

VC-1:

Ben admits in some areas he’s not sure why there is such a difference (i.e., the FLV is darker it appears as well).  It is an interesting article to read and he provides all the details, sample files and implementation for you to examine.  A lot of the things he shows for the tweak settings are a part of Encoder 2 which is to release soon.

For Silverlight 2, we finally have some native controls to leverage.  Most of them are to aid in input scenarios.  The text input, however, is currently scoped to be plain text input.  Some have desired a richer input control.  You knew it wouldn’t be long before someone in the community stepped up to the challenge.  Christopher Husse has done just that.

Enter: Silverlight rich text editor.

He posts a detailed description of all the capabilities on Michael Syncs blog.  The effort is also posted on Codeplex for you to peruse.

Here is what he calls the ‘incomplete feature list’:

    • Copy/Paste formatted text between RichTextBoxes and copy/paste from/to clipboard of unformatted but macro-enabled text. This means in windows clipboard even things like emoticons will be kept.
    • You may insert line breaks, unordered lists and blockquotes.
    • You may use various keyboard selection features like End/ Home/ PageUp/ PageDown/ Left/ Up/ Right/ Down, Ctrl+A/ End/ Home, Ctrl+Shift+End/ Home/ Left/ Right, Shift+End/ Home/ PageUp/ PageDown/ Left/ Up/ Right/ Down and so forth…
    • Supports direct Unicode character input using “Ctrl”+[NumPad].
    • All silverlight font formatting is supported and even some more like SUP/SUB formatting.
    • You may define macros and a proper object class that should replace matching text, like emoticons…
    • In contrast to many other rich text editors, this one is fully real-time. That means no preview is required because the editor allows editing all things directly.
    • If you only use macros and IRichTextObject to extend the control, you will automatically get support for secure content serialization of all control elements. Content serialization also supports to reload content and edit it again.
    • Secure content serialization gets rid of any potential security leak when storing user typed formatted text on a server and presenting it to visitors, because it is fully verifiable.
    • You may restrict font formatting to a well defined custom subset. This allows you to ensure that all user typed input matches your needs or website design. (this feature is currently not implemented, but only prototyped)
    • Snapshots allow convenient access to formatted content and also Find&Replace with regular expressions for example…

Way to go Christopher!

This blog runs on SubText.  I heart SubText.  I know there are others out there but for me SubText has met most of my needs.  And when it hasn’t I modify it.  Which brings me to this post.  There was a thread on an email list I belong to about Windows Live Writer (I heart Live Writer too :-)) and categories (adding new categories on the fly).  This got me to crack open the source and hunt.  Alas, there was no support for this.  I’ve been ranting about WordPress API support for SubText on the developer list and I think if I rant one more time it will probably get assigned to me.  Please keep in mind that my modifications were solely intended to make Live Writer the best tool for me…these are targeted at Live Writer functionality and may/may not add value to other areas of functionality.  So on with the show…

The Slug

One of the features in SubText is the ability to auto generate URLs based on the title of the blog post.  Well, I didn’t like that.  I usually want my URLs to be a slight derivative of the URL.  My workflow, then, has been to post as draft, go to the web interface, change and then enable the syndication.  I am sick of doing that.  So let’s start with that first.  You see in blog terms the end part of your URL in called a “slug.”  SubText calls this a ‘friendly url’ and other engines may call it something else.  Here I’m going to call it a slug.  My colleague Jason Mauer has a custom blog engine he uses where he’s implemented almost every blogging API in his set.  I’ve been geekly jealous of his API for some time…now it is time to change that.

I’m not going to go into the philosophical reasons why I want a different URL slug and why I want different ones.  We can debate that over a Mt. Dew some time if you’d like.

SubText uses the MetaWeblog API by default.  The developers chose to implement the spec of MetaWeblog (as they should have) and thus the slug is not a part of the newPost spec and the Post struct used to identify the structure of a post.  So my modification was a few steps.  If you are familiar with the latest source (1.9.5b) of SubText, I’ll be referring to line numbers in there.  First, I had to modify that Post struct for the MetaWeblogAPI implementation.  Now some may shirk that this is a no-no…and I might agree…so if modifying something that isn’t going to conform to a spec that really isn’t full anymore, then move along.  I modified about line 63 and added the following:

public string wp_slug;

I actually could have used wp_slug or mt_basename, both of which mean the same thing and both of which are sent to the API by Live Writer…so I just picked one.  Now my struct has the information when it passes it along for creation/edit of the post via Live Writer.

The next step was to modify the implementation of the post.  In MetaWeblog.cs at about line 234 I added:

if (!string.IsNullOrEmpty(post.wp_slug))
{
    entry.EntryName = post.wp_slug;
}

I also added this to the editPost method to ensure compatibility on edit.

The final step was to modify my wlwmanifest.xml file to announce to Live Writer that I now support this feature.  This is done by adding to the <options> node of this manifest:

<supportsSlug>Yes</supportsSlug>

Then do a refresh of the account settings in Live Writer.  When you do that, in a new post click the little ‘up’ arrow just underneath the editing area and you should now see a Slug field:

Now I don’t have to post a draft and login to change!

New Categories

The thread actually started with wanting to create new categories during a post.  SubText is one of the engines that doesn’t expose this API directly just yet, so some altering had to be done.  Here’s what I did.  I chose to mirror the WordPress newCategory method to do this. 

First I added IWordPressApi.cs to Subtext.Framework.XmlRpc.  The complete code within it is:

using System;
using CookComputing.XmlRpc;

namespace Subtext.Framework.XmlRpc
{

    public struct WordpressCategory
    {
        public string name;
    }

    public interface IWordPressApi
    {
        [XmlRpcMethod("wp.newCategory", 
            Description = "Adds a new category to the blog engine.")]
        int newCategory(
          string blogid,
          string username,
          string password,
          WordpressCategory category);
    }
}

I then went into MetaWeblog.cs and implemented that interface with:

public int newCategory(string blogid, string username, string password, WordpressCategory category)
{
    LinkCategory newCategory = new LinkCategory();
    newCategory.CategoryType = CategoryType.PostCollection;
    newCategory.Title = category.name;
    newCategory.IsActive = true;
    newCategory.Description = category.name;

    newCategory.Id = Links.CreateLinkCategory(newCategory);

    return newCategory.Id;
}

I chose to ignore the slug/description fields (again, thus ignoring the spec which isn’t ideal) at this time, partly because I was getting errors and partly because I decided that I didn’t need them anyway.  I don’t use the description field in categories in SubText, so I just set the description to also be the title.  I also had to modify the wlwmanifest.xml file with:

<supportsNewCategories>Yes</supportsNewCategories>

and refresh my Live Writer account profile to pick up the changes.  The result is now my category options in Live Writer include an “add” feature:

Done with both of those.

Future Posting

This is something I started to look at and added the information to the MetaWeblog API, but it seems that SubText doesn’t filter out future posts in the UI – or at least my quick scan didn’t reveal it did.  I’ve moved on away from this one since I don’t future post right now, but I’ll come back to it in a while.  What I did do, however, to prevent me from thinking SubText supported this was modify my wlwmanifest.xml file to include this definition in the options:

<futurePublishDateWarning>Yes</futurePublishDateWarning>

This way at least if the idiot in me *thinks* I can do it, Live Writer will warn me.

So that’s it!  These little adjustments make my Live Writer + SubText experience AWESOME.  Live Writer truly is one of the best tools Microsoft puts out (aside from Silverlight of course).  I’m going to submit these modifications to the SubText team and see what sticks.  I assume none will since they are admittedly partial.  But I’ve been suggesting on the dev list that SubText expose a WordPress API and I have a feeling I’ll need to start working on that for the team.

Hope this helps some of you!