Monday, January 19, 2009

Dynamic Loading Expression Media Player Template and passing parameters by code

Following my previous post on How to pass parameters to the Expression MediaPlayer component by code, here is a solution that builds upon this post to Dynamicaly load the Media Player inside your app and pass parameters to it.

You can test loading of the player here. The source code can also be downloaded here.

The loading and costumization of the parameters is done like this:

// Download the Media Player using WebClient
private void downloadVideoPlayer()
{
WebClient downloader = new WebClient();
downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(onDownloadVideoPlayerCompleted);
downloader.OpenReadAsync(new Uri("MediaPlayerTemplate.xap", UriKind.Relative));
lblLoadPlayer.Text = "Downloading Media Player";
}

// Once the Media Player is downloaded
private void onDownloadVideoPlayerCompleted(object sender, OpenReadCompletedEventArgs args)
{
try
{
string appManifest = new StreamReader(Application.GetResourceStream(new StreamResourceInfo(args.Result, null), new Uri("AppManifest.xaml",UriKind.Relative)).Stream).ReadToEnd();

XElement deploymentRoot = XDocument.Parse(appManifest).Root;
List<XElement> deploymentParts = (from assemblyParts in deploymentRoot.Elements().Elements()
select assemblyParts).ToList();

Assembly asm = null;
foreach (XElement xElement in deploymentParts)
{
string source = xElement.Attribute("Source").Value;
AssemblyPart asmPart = new AssemblyPart();
StreamResourceInfo streamInfo = Application.GetResourceStream(new StreamResourceInfo(args.Result, "application/binary"), new Uri(source, UriKind.Relative));
if (source == "MediaPlayerTemplate.dll")
{
asm = asmPart.Load(streamInfo.Stream);
}
else asmPart.Load(streamInfo.Stream);
}



MediaPlayerTemplate.Page myPlayer = asm.CreateInstance("MediaPlayerTemplate.Page") as MediaPlayerTemplate.Page;
Dictionary<string,string> dic = new Dictionary<string, string>();
dic.Add("autoplay", "true");
dic.Add("enablecaptions", "true");
dic.Add("muted", "false");
dic.Add("stretchmode", "0");
dic.Add("displaytimecode", "false");
dic.Add("playlist", "<playList><playListItems><playListItem title=\"\" description=\"\" mediaSource=\"silverlight.wmv\" adaptiveStreaming=\"False\" thumbSource=\"\" frameRate=\"23.9760431376968\" width=\"512\" height=\"284\" ><chapters><chapter position=\"11.256\" title=\"MYMARKER01\" /><chapter position=\"20.033\" thumbnailSource=\"silverlight_20.033.jpg\" title=\"Capitulo%201\" /><chapter position=\"45.585\" thumbnailSource=\"silverlight_45.585.jpg\" title=\"Chapter%202\" /><chapter position=\"58.646\" thumbnailSource=\"silverlight_58.646.jpg\" title=\"Chapter%203\" /><chapter position=\"72.199\" thumbnailSource=\"silverlight_72.199.jpg\" title=\"Chapter%204\" /></chapters></playListItem></playListItems></playList>");
myPlayer.StartUp(dic);

cnvMediaPlayer.Children.Add(myPlayer);
lblLoadPlayer.Text = "";
LayoutRoot.UpdateLayout();

}
catch (Exception e)
{
lblLoadPlayer.Text = "Download Error: " + e.Message;
}
}



If you download the source code you will need to create your own playlist that points to a a movie of your own and set the thumbnails and chapters as you wish. Check the following line of code inside VideoPlayerHoster, Page.xaml.cs



dic.Add("playlist", "<playList>...



The Dynamic loading was implemented by looking at these 2 posts:



http://www.silverlighthack.com/post/2008/09/29/Silverlight-2-(RC0-RTM)-Dynamic-Assembly-Loading.aspx



http://silverlight.net/learn/learnvideo.aspx?video=65687



Using Expression Encoder 2 MediaPlayer Templates in your application

Microsoft Expression Encoder 2 SP1 now includes a set of Silverlight 2 Output Templates:

clip_image002

After installing SP1 you'll have a some Silverlight 2 templates in "C:\Program Files\Microsoft Expression\Encoder 2\Templates\en".

The Templates use 2 controls that implement most of the logic that we need on our media apps: MediaPlayer and ExpressionPlayer. These controls take care of all basic media handling functionalities but also Video Marker handling, Chapters and chapter thumbnails, playlists, cpations, etc.

Out-of-the-box the templates can be hosted in a html or aspx page and the parameters passed when loading the XAP. Here is the html of the page that is created when running your template:




<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="MediaPlayerTemplate.xap"/>
<param name="onerror" value="onSilverlightError" />
<param name="initparams" value='autoplay=&lt;$=TemplateParameter.AutoPlay$>,autoload=<$=TemplateParameter.AutoLoad$>,enablecaptions=<$=TemplateParameter.EnableCaptions$>,muted=<$=TemplateParameter.Muted$>,stretchmode=<$=TemplateParameter.StretchMode$>,displaytimecode=<$=TemplateParameter.DisplayTimecode$>,playlist=<$=PlayListParam(TemplateParameter.AllowedCodecs)$>' />

All customization is made via the initparams.If you want to use the MediaPlayer inside your own Silverlight application (that happens to use a media player) you need to make same changes in order to pass the parameters by code and not by the initparams of the silverlight object command in HTML as the template does.



When Silverlight loads the XAP via HTML it calls:




        public Page(object sender, StartupEventArgs e)

        {

            InitializeComponent();

            myPlayer.OnStartup(sender, e);

        }


Since you cannot create a StartupEventArgs class because it is marked as internal we need another way to pass our parameters from code.



Here are the changes:



1. In ExpressionPlayerControl.cs change the OnStartup method to receive a Dictionary<string, string> instead of StartupEventArgs:




public override void OnStartup(object sender, StartupEventArgs e)

replace by


public override void OnStartup(object sender, Dictionary<string, string> e)


2. Inside the OnStartup method replace "e.InitParams" for InitParams.



3. Since ExpressionPlayer derives from MediaPlayer and OnStartup is overrided we also need to update the OnStartup method in MediaPayer.cs:



public override void OnStartup(object sender, StartupEventArgs e)


replace by


public override void OnStartup(object sender, Dictionary<string, string> e)


4. Since you changed the method signature you also need to update the call to it on Page.xaml.cs and cast it to Dictionary<string, string>:




public Page(object sender, StartupEventArgs e)

{

    InitializeComponent();

    myPlayer.OnStartup(sender, (Dictionary<string, string>) e.InitParams);

}




5. Create a new method that will allow your code to costumize the player:



public void StartUp(Dictionary<string, string> InitParams)

{

    myPlayer.OnStartup(this, InitParams);

}


6. Now you are ready to costumize your mediaplayer from code:





   1:  MediaPlayerTemplate.Page myPlayer = asm.CreateInstance("MediaPlayerTemplate.Page") as MediaPlayerTemplate.Page;

   2:  Dictionary<string,string> dic = new Dictionary<string, string>();

   3:  dic.Add("autoplay", "true");

   4:  dic.Add("enablecaptions", "true");

   5:  dic.Add("muted", "false");

   6:  dic.Add("stretchmode", "0");

   7:  dic.Add("displaytimecode", "false");

   8:  dic.Add("playlist", "<playList><playListItems><playListItem title=\"\" description=\"\" mediaSource=\"silverlight.wmv\" adaptiveStreaming=\"False\" thumbSource=\"\" frameRate=\"23.9760431376968\" width=\"512\" height=\"284\" ><chapters><chapter  position=\"11.256\" title=\"MYMARKER01\" /><chapter  position=\"20.033\" thumbnailSource=\"silverlight_20.033.jpg\" title=\"Capitulo%201\" /><chapter  position=\"45.585\" thumbnailSource=\"silverlight_45.585.jpg\" title=\"Chapter%202\" /><chapter  position=\"58.646\" thumbnailSource=\"silverlight_58.646.jpg\" title=\"Chapter%203\" /><chapter  position=\"72.199\" thumbnailSource=\"silverlight_72.199.jpg\" title=\"Chapter%204\" /></chapters></playListItem></playListItems></playList>");

   9:   

  10:  myPlayer.StartUp(dic);





(assuming myPlayer was defined in your Xaml)