ASP.NET MVC Application Building: Family Video Website #3 – Play Videos with Silverlight

In this series of blog entries, I build an entire ASP.NET MVC application from start to finish. In this entry, I integrate the Silverlight Media Player into the Family Videos MVC application so I can play videos.

Before reading this blog entry, you might want to first read the previous two entries:

Family Video Website #1 – Upload the Videos – In this entry, I create an MVC controller that accepts large file uploads.

Family Video Website #2 –Add the Database – In this entry, I added a database so that I could associate additional information, such as a title and description, with each video.

In this blog entry, I make two major changes to the Family Videos Website. First, I redesign the view that renders the uploaded files so that it can display multiple types of media. The Family Video Website not only supports playing videos; it also supports uploading pictures and audio files.

Second, I integrate the Silverlight Media Player into the application. Because we don’t want to use the Silverlight MediaPlayer control in an MVC application, I demonstrate how you can create the Silverlight Media Player using pure JavaScript in an MVC view.

Rendering Different Types of Media

I want to enable family members to upload different types of media. For example, a family member might want to upload a JPEG image, a GIF image, a RAW audio file, or a WMV video file.

Each different type of media must be rendered in a different way. The way that an image is displayed is very different than the way a video is displayed. When displaying an image, we just want to show it in an <img> tag. When displaying a video, we need to render a video player. An audio file might require a different player than a video file.

Also, I want to design the Family Video Website in such a way that it can be easily modified to support new types of media in the future. If holographic videos become hugely popular, I don’t want to have to rewrite any of the existing Family Video Website code in order to support this new media type.

When you know that code is likely to change in the future, it is a good idea to isolate that code as much as possible from the rest of the code in your application. That way, when you make changes to the isolated code, you lower the risk of introducing bugs into the existing code.

There is a software design principle for this idea named Encapsulate What Varies. The Encapsulate What Varies principle is closely related to the Single Responsibility Principle which states that a class should have only a single reason to change. If there are multiple reasons that a class might need to be changed in the future, then you should divide these different responsibilities into distinct classes.

I struggled with this issue for a while. I wanted to figure out a good way to separate out the rendering logic for different media types from the rest of my application. I also wanted an easy way to add new ways to render content.

I decided to represent each way of rendering a media type with a partial (in other words, a user control). I created a distinct partial for rendering each type of media. I created a RenderVideo.ascx partial for rendering videos and a RenderPicture.ascx partial for rendering images. If I need to render a new type of media in the future, I can just create a new partial.

For example, the RenderPicture.ascx partial is contained in Listing 1.

Listing 1 – PartialsRenderPicture.ascx

<%@ Control Language="VB" AutoEventWireup="false" CodeBehind="RenderPicture.ascx.vb" Inherits="FamilyVideos.RenderPicture" %>

<img
  src='<%= ResolveUrl(ViewData.Model.MediaPath) %>'
  width="200px" alt="" />

The partial in Listing 1 simply displays an image. The RenderVideo.ascx partial is more complicated since it renders a Silverlight Media Player. We discuss the RenderViewo.ascx partial in detail in the second part of this blog entry.

So how does the application know which partial to use for each uploaded file that it displays? In order to solve this problem, I created a RendererMap class. This class maps file extensions to renderers. For example, the class maps the extension .jpeg to the RenderPicture.ascx partial.

The RendererMap class is contained in Listing 2.

Listing 2 – ModelsRenderMap.vb

Imports System.Web.Configuration
Imports System.IO

Public Class RendererMap

    Private Shared ReadOnly _map As New Dictionary(Of String, String)

    Shared Sub New()
        Dim config As RendererConfigurationSection = WebConfigurationManager.GetSection("renderers")


        For Each item As RendererConfigurationElement In config.Renderers
            Dim extension = "." & item.Extension.ToLower()
            Dim rendererPath = item.RendererPath
            _map.Add(extension, rendererPath)
        Next
    End Sub



    Public Shared Function GetRenderer(ByVal fileName As String) As String
        Dim extension = Path.GetExtension(fileName)
        If Not _map.ContainsKey(extension) Then
            Throw New ArgumentException(extension + " is not defined in the renderers section of web.config.")
        End If
        Return _map(extension.ToLower())
    End Function


End Class

The RendererMap class exposes one method named GetRenderer() that returns the path to the partial that should be used to render a particular file. The method extracts the file extension in order to determine which renderer to use to render a particular file.

The RendererMap class loads up the associations between file extensions and renderers within its constructor. The associations between file extensions and renderers are maintained in the web configuration (web.config) file. The application root web.config file contains the configuration section in Listing 3.

Listing 3 – Renderer configuration

<renderers>
    <add extension="jpg" rendererPath="~/Partials/RenderPicture.ascx" />
    <add extension="jpeg" rendererPath="~/Partials/RenderPicture.ascx" />
    <add extension="gif" rendererPath="~/Partials/RenderPicture.ascx" />
    <add extension="png" rendererPath="~/Partials/RenderPicture.ascx" />
    <add extension="wmv" rendererPath="~/Partials/RenderVideo.ascx" />
</renderers>

The nice thing about storing the associations between file extensions and renderers in the web.config file is that it makes it easy to add new renderers for new media types in the future. If you need to support a new type of media then you simply need to add a new entry into the web.config file and create a new partial (user control). You don’t even need to recompile your application!

In order to create a custom section in the web.config file, I needed to create a set of custom configuration classes. These classes are contained in the Configuration folder in the project that you can download at the end of this blog entry.

I modified the Index view returned by the Home controller so that it takes advantage of the RendererMap. The Index view loops through all of the uploaded files and renders each file with the appropriate renderer with the help of the RendererMap class. The updated Index view is contained in Listing 4.

Listing 4 – ViewsHomeIndex.aspx

<%@ Page Language="VB" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="FamilyVideos.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Family Videos Home</title>
    <style type="text/css">

    h1
    {
        font: bold 14px Verdana;
    }

    div.media
    {
        border-bottom: 1px solid black;
        padding: 10px;
    }

    </style>
    <script type="text/javascript" src="../../Content/MicrosoftAjax.debug.js"></script>
    <script type="text/javascript" src="../../Content/Silverlight/SilverlightControl.js"></script>
    <script type="text/javascript" src="../../Content/Silverlight/SilverlightMedia.js"></script>
</head>
<body>
    <div>

    <% For Each m In ViewData.Model%>

    <div class="media">
        <h1><%=Html.Encode(m.Title)%></h1>
        <p>
        <%=Html.Encode(m.Description)%>
        </p>

        <% Html.RenderPartial(FamilyVideos.RendererMap.GetRenderer(m.FileName), m)%>
    </div>

    <% Next%>

    <br /><br />

    <%=Html.ActionLink("Add New Media", "Create", "Media")%>

    </div>
</body>
</html>

Integrating the Silverlight MediaPlayer in an MVC View

The whole point of the Family Video Website is to play videos. Therefore, I really needed to add support for playing video files to the application.

I wanted to take advantage of Silverlight because Silverlight enables you to play high quality videos without demanding a huge amount of bandwidth (or giant video files). Also, I really like the fact that the Silverlight Media Player works on both Internet Explorer and Firefox. My grandma happens to be a loyal user of Internet Explorer. But, who knows? Feeling rebellious, she might decide to switch to Firefox.

I also really like Microsoft Expression Encoder. You can import just about any type of video file into the encoder and you get a WMV file that is ready to be played by the Silverlight Media Player (see Figure 1). The Expression Encoder has built-in profiles for different bandwidths (for example, 256K DSL versus 512K DSL).

Figure 1 – Expression Encoder 2

image

After you encode a video, you can play the video with the Silverlight Media Player. You can download the ASP.NET Silverlight Media Player control as part of the Microsoft Silverlight Tools for Visual Studio 2008. I downloaded the Beta 2 version from the following address:

http://www.microsoft.com/downloads/details.aspx?FamilyId=50A9EC01-267B-4521-B7D7-C0DBA8866434&displaylang=en

After you install the Microsoft Silverlight Tools, the Media Player control appears in the toolbox (see Figure 2). You can use this control in an ASP.NET Web Forms page by dragging it onto the page and setting the control’s MediaSource property to the path of a video file.

Figure 2 – The ASP.NET Media Player Control

image

Unfortunately, however, you cannot use the ASP.NET Media Player control in an ASP.NET MVC View. The problem is that the Media Player control requires you to add the ASP.NET ScriptManager control to the view. Because the ScriptManager control requires a server-side form control, you can’t use the Media Player control in an ASP.NET view.

Don’t despair! There is an MVC friendly way to use the Silverlight Media Player. Because, the ASP.NET Media Player control uses the AJAX MediaPlayer class in the background, you can bypass the ASP.NET control and work with the JavaScript directly. I’ve taken this approach in the RenderVideo.ascx partial in Listing 5.

Listing 5 – PartialsRenderVideo.ascx

<%@ Control Language="VB" AutoEventWireup="false" CodeBehind="RenderVideo.ascx.vb" Inherits="FamilyVideos.RenderVideo" %>

<span id="mediaPlayer<%= ViewData.Model.Id %>"></span>

<script type="text/javascript">
    var mediaSource = '<%= ResolveUrl(ViewData.Model.MediaPath) %>';
    var width = '480px';
    var height = '400px';
    var scaleMode = 1;

    Sys.UI.Silverlight.Control.createObject('mediaPlayer<%= ViewData.Model.Id %>', '<object width="' + width + '" height="' + height + '" type="application/x-silverlight" data="data:application/x-silverlight," style="border:1px solid black">rnt<a href="http://go2.microsoft.com/fwlink/?LinkID=114576&amp;v=1.0"><img src="http://go2.microsoft.com/fwlink/?LinkID=108181" alt="Get Microsoft Silverlight" style="border-width:0;" /></a>rn</object>');

    Sys.Application.add_init(initPlayer);
    function initPlayer() {
        $create(Sys.UI.Silverlight.MediaPlayer, { "mediaSource": mediaSource, "scaleMode": scaleMode, "source": "/WebResource.axd?d=Iix4XqbYErLfcZZJDM-UZ3_Lx6-jqZZ0-IZSvJHJoXScC2jqSYJokAvkdZrY8z4Q0&t=633481889860000000" }, null, null, $get("mediaPlayer<%=ViewData.Model.Id %>"));
    }

</script>

The bulk of the code in Listing 5 consists of JavaScript. The JavaScript code instantiates a Silverlight Media Player by calling the Microsoft AJAX Library $create() method. The Media Player is associated with the <span> tag in the page.

The Media Player displayed by the RenderVideo.ascx partial works with both Internet Explorer and Firefox. After you upload a video file to the Family Video Website, you can play the video with the Media Player (see Figure 3).

Figure 3 – Displaying Videos with the Silverlight Media Player

image

A very tempting option is to create a HTML Helper to render the JavaScript code in Listing 5. That way, you could easily set different options for the Media Player such as its height, width, and player template.

In order to render the Silverlight Media Player using JavaScript, you need to add the following JavaScript files to your project:

<script type="text/javascript" src="../../Content/MicrosoftAjax.debug.js"></script>
<script type="text/javascript" src="../../Content/Silverlight/SilverlightControl.js"></script>
<script type="text/javascript" src="../../Content/Silverlight/SilverlightMedia.js"></script>

The Media Player requires these JavaScript files. I include these JavaScript file includes in the Index view.

Summary

I still have more work to do on the Family Video Website. We can now upload different types of media and display the media. We also can enter a title and description for each file upload. I feel like we have made some real progress, but the application is still very rough.

In the next entry, I want to figure out how to display a progress bar while uploading huge video files.

Download the Code

Discussion

  1. http:// says:

    thanx

  2. Nullable says:

    Nicely done! I saw this post a couple of days ago, and have been meaning to read it through… and I’m glad I did :)

    What do you think about, instead of using the web.config approach, using a cusotm ActionSelection attribute?

    Example:

    [ActionName(“RenderMedia”), FileType(“jpg,png,gif”)]
    public ActionResult RenderMedia_Jpg(string fileName);

    [ActionName(“RenderMedia”), FileType(“mp3,wav”)]
    public ActionResult RenderMedia_Music(string fileName);

    [ActionName(“RenderMedia”), FileType(“wmv”)]
    public ActionResult RenderMedia_Movie(string fileName);

    Thanks,
    -Timothy Khouri

  3. http:// says:

    @Timothy — Really cool idea! — I like how you are thinking about Preview 5 features :)
    I still like the web.config approach because it enables me to support new media types without needing to recompile the application.

  4. geek50 says:

    Error 1 Name ‘__o’ is not declared. C:MVC FrameworkFamilyVideos3FamilyVideosFamilyVideosViewsHomeIndex.aspx 42 5 FamilyVideos

  5. http:// says:

    @geek50 — I get that error message all of the time when working in a view. It goes away after I do a Build (sometimes I need to select Build, Clean to clean everything out before I do a build). Is the error preventing the application from running?

  6. kahanu says:

    Good stuff. Have you used HttpHandlers in ASP.NET MVC yet? I’m trying to get one to work now and I’m having problems. It’s a simple custom image handler to display thumbnails, etc., but I’m not getting an image, or an error.

    Can you point me to any articles that shows how to use a custom HttpHandler in MVC?

    Thanks.

  7. almny says:

    hi do you know how i can controller in sound to up or down it ?

  8. MOV to DVD says:

    because i dont have the cd can email for me the answer josue.dpr@gmail.com

  9. On my blog: Better examined some potential pitfalls

  10. Great Tips! Really these tips are very useful for me.

  11. 4 Thank you for sharing the this code.

  12. @Timothy — Really cool idea!

  13. u
    I believe it is a promising (currently version 4.0). So I would stick with it.. thanks

  14. Nathanial says:

    @Timothy — Really cool idea! — I like how you are thinking about Preview 5 features :)
    Great Tips! Really these tips are very useful for me.

    toronto swingers club |toronto web design |toronto web hosting