Amazon.com Widgets All posts tagged 'microsoft expression encoder'

WilliaBlog.Net

I dream in code

About the author

Robert Williams is an internet application developer for the Salem Web Network.
E-mail me Send mail
Code Project Associate Logo
Go Daddy Deal of the Week: 30% off your order at GoDaddy.com! Offer expires 11/6/12

Recent comments

Archive

Authors

Tags

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.


Using Expression Encoder 2 Silverlight 2 Templates in your project

Some time ago, I wrote a popular article on how to create a scrolling Silverlight 1.x Playlist using Microsoft Expression Encoder output. Well, I finally found some time to revisit that application to see how I might upgrade it to Silverlight 2. As you are probably aware, Expression Encoder 2 Service Pack 1 is now out and it ships with a handful of Player templates just for Silverlight 2. Among these new templates are two which already have built in Scrolling playlists and I thought I would test one out.

However, I already have all my videos encoded - including all the chapter point thumbnails, etc. so I didn't want to start inside Encoder, have it build my project and work from there like I did last time. This time, I created a new Silverlight 2.0 Usercontrol project in Visual Studio 2008 and worked for a week or two on the new look and feel for the site before I decided it was time to merge my project with the Expression Encoder template. I found Tim Heuer's blog entry on integrating these new templates very helpful, though I feel it is important to add that the template itself, meaning the look and feel of the player, is not stored in the dlls and it does not matter which of the template projects you open and compile, just referencing the dlls and following Tim's instructions will always give you the standard Silverlight 2 player. In order to add the look and feel you must copy (or merge) the contents of the Page.xaml file in that template into your own UserControl.

For my project I wanted the player to be a seperate UserControl, so I went to Project > New Item >  Silverlight User Control, and called it MediPlayer.xaml. Next I pasted everything from C:\Program Files\Microsoft Expression\Encoder 2\Templates\en\FrostedGallery\Source\Page.xaml into my new MediaPlayer.xaml file, then changed the x:Class at the very top to reflect my original NameSpace and Class Name. (If you forget what it was, just undo your paste, make a note of it and redo the paste).

I also wanted my Player control to be available on multiple pages, and I wanted it hidden most of the time, only to pop up in a pseudo modal 'Lightbox' format with a close button. To achieve this effect I simply used these techniques described by Scot Guthrie in his excellent tutorial on creating a Silverlight Digg application. So now I had my player, and I could show and hide it with the click of a button. Basically, my site showcases the James Bond Movies, and I have customized the Yet Another Carousel control so that you can click on the selected box art and read the synopsis, watch the Trailer and buy it on Amazon.com. My initial idea therefore, was to create a new PlayListItem programatically and add it to the player's PlayListCollection in the onLeftMouseButtonUp event of the button. What I found was that while this worked quite nicely, I ultimately ended up with a playlist slowly being generated by the user in the order in which the user clicked on the movies, making it harder to keep track of which item in the collection was which movie, but more importantly I couldn't figure out how to take full advantage of all the chapter information and thumbnails I had created for the original project. I could create chapter item objects, and add them to my own PlayListCollection object, but I could not bind that new object to my player since the Playlist Property is read only.

Reading more of Tim's blog I saw that you could add some xml to the InitParams of the control, but I have 24 videos, each with thumbnail paths for at least 4 chapter points and I didn't need to start typing that all into a single line to understand what a nightmare that would become: not only would it be hard to read and maintain but also it goes against the whole MVC seperation of code, data and presentation layers, ethic we have all grown so attached to.

More google searches led me to this solution which does allow you to move the creation of the parameters into the code behind, but seems to require tinkering with the original template code, which I am not opposed to, but I'd already thought of a cleaner solution. What I wanted was a way to create an xml file containing my entire playlist in this format:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <playList>
   3:   <playListItems>
   4:     <playListItem title="Dr No" description="Trailer" mediaSource="ClientBin/01_dr_no.wmv" adaptiveStreaming="False" thumbSource="ClientBin/01_dr_no_Thumb.jpg" >
   5:       <chapters>
   6:         <chapter  position="29.176" thumbnailSource="ClientBin/01_dr_no_MarkerThumb 00.00.29.1760677.jpg" title="1" />
   7:         <chapter  position="49.374" thumbnailSource="ClientBin/01_dr_no_MarkerThumb 00.00.49.3748838.jpg" title="2" />
   8:         <!-- etc -->
  20:       </chapters>
  21:     </playListItem>
  22:   </playListItems>
  23: </playList>

and simply pass the file to my player. And my Solution turned out to be trivially easy: I created a new class that inherits from ExpressionMediaPlayer.MediaPlayer, and added a new method that would accept my file:

 

using System;

using System.Diagnostics;

using System.Net;

using System.Windows;

using System.Windows.Browser;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.Xml.Linq;

 

 

namespace Bond_Silverlight2

{

    public class MI6MediaPlayer : ExpressionMediaPlayer.MediaPlayer

    {

        public MI6MediaPlayer(): base()

        {

        }

 

        public void OnStartup(string xmlPlayList)

        {

            XDocument document = XDocument.Load(xmlPlayList);

            try

            {

                Playlist.Clear();

                Playlist.ParseXml(HtmlPage.Document.DocumentUri, document.ToString());

            }

            catch (System.Xml.XmlException xe)

            {

                Debug.WriteLine("XML Parsing Error:" + xe.ToString());

            }

            catch (NullReferenceException)

            {

            }

        }

 

    }

}

 


This required some minor changes to the MediaPlayer.xaml file, inorder to make it use my version of the player:

First of all, I replaced the <expression:ExpressionPlayer> tags with <Bond_Silverlight2:MI6MediaPlayer> tags and any static resource styles that had a target type of ExpressionPlayer:ExpressionPlayer also needed to be replaced, and then everything used my new player. Obviously, there was another step - how to initialize and pass my xml playlist to my new player. First, I created my xml file in the format outlined above. It is important to note, that my video files and associated JPEG files are stored on the webserver inside the Clientbin folder, rather than inside the xap file as resources or content. In the code behind of my MediaPlayer.xaml (that is the usercontrol I pasted the page.xaml into earlier, not the MI6MediaPlayer class from the code above), I call the startup method using a link to my xml file:

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

 

namespace Bond_Silverlight2

{

    public partial class MediaPlayer : UserControl

    {

        public MediaPlayer()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MediaPlayer_Loaded);

        }

 

        void MediaPlayer_Loaded(object sender, RoutedEventArgs e)

        {

            Player1.OnStartup("Playlist.xml");

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            Player1.Stop();

            Visibility = System.Windows.Visibility.Collapsed;

        }

    }

}

 

The xml file ("Playlist.xml") is stored as Content within the xap file. This should be the default behavior if you created the xml file inside Visual Studio by using the Project > Add Item menu, but if you didn't, you can check by right clicking on the xml file, choosing Properties and checking that the Build Action is "Content", and Copy to Output Directory is set to "Do Not Copy".

Now when my Player is first loaded into memory, my full playlist is immediately available.

 


Posted by Williarob on Wednesday, April 08, 2009 1:47 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Customizing Expression Encoder Output

I finished building my first 100% pure Silverlight 1.0 application, the result of which can be viewed at http://www.thejamesbondmovies.com, and I wanted to share a few of the techniques I learned. Anyone who has played with Microsoft Expression Encoder will probably recognize the player as coming from one of the built in templates. Expanding the XAML to include my own elements was not too difficult, the Blend 2 September Preview made that quite easy. When I have more time I will describe in detail how I made the scrollable playlist as I could find no tutorials anywhere on how to do that, but for right now I just want to address the basics of Scripting the output. I am by no means a JavaScript Guru, which is perhaps why I struggled a bit to understand exactly how to interact with my new elements, and exactly where to put my code. When you build a new Silverlight application in Blend 2 or Visual Studio the JavaScript that is provided to you as a starting point appears to be quite different to that outputted by the encoder. Visual Studio gives you the following in a file called (by default) Scene.xaml.js:

if (!window.SilverlightJSApplication1)
window.SilverlightJSApplication1 = {};
SilverlightJSApplication1.Scene = function() 
{
}
SilverlightJSApplication1.Scene.prototype =
{
handleLoad: function(plugIn, userContext, rootElement) 
{
this.plugIn = plugIn;
// Sample button event hookup: Find the button and then attach event handlers
this.button = rootElement.children.getItem(0);	
this.button.addEventListener("MouseEnter", Silverlight.createDelegate(this, this.handleMouseEnter));
this.button.addEventListener("MouseLeftButtonDown", Silverlight.createDelegate(this, this.handleMouseDown));
this.button.addEventListener("MouseLeftButtonUp", Silverlight.createDelegate(this, this.handleMouseUp));
this.button.addEventListener("MouseLeave", Silverlight.createDelegate(this, this.handleMouseLeave));
},
// Sample event handlers
handleMouseEnter: function(sender, eventArgs) 
{
// The following code shows how to find an element by name and call a method on it.
var mouseEnterAnimation = sender.findName("mouseEnter");
mouseEnterAnimation.begin(); 
},
handleMouseDown: function(sender, eventArgs) 
{
var mouseDownAnimation = sender.findName("mouseDown");
mouseDownAnimation.begin(); 
},
handleMouseUp: function(sender, eventArgs) 
{
var mouseUpAnimation = sender.findName("mouseUp");
mouseUpAnimation.begin(); 
// Put clicked logic here
alert("clicked");
},
handleMouseLeave: function(sender, eventArgs) 
{
var mouseLeaveAnimation = sender.findName("mouseLeave");
mouseLeaveAnimation.begin(); 
}
} 

Blend 2 gives you an almost identical file called Page.xaml.js:

if (!window.MyProjName)
window.MyProjName = {};
MyProjName.Page = function() 
{
}
MyProjName.Page.prototype =
{
handleLoad: function(control, userContext, rootElement) 
{
this.control = control;
// Sample event hookup:	
rootElement.addEventListener("MouseLeftButtonDown", Silverlight.createDelegate(this, this.handleMouseDown));
},
// Sample event handler
handleMouseDown: function(sender, eventArgs) 
{
// The following line of code shows how to find an element by name and call a method on it.
// this.control.content.findName("Timeline1").Begin();
}
} 

Both of these files make it very easy to pick up your own named elements, and add event handlers to them because the handleLoad function passes the plugIn (visual Studio) or control (Blend 2) argument that can be used to easily find a reference to your own XAML element:

	control.content.findName("MyXamlElement"); //or plugIn.content.findName("MyXamlElement");

But having played with that model a few times and become comfortable with it, you then navigate to your Expression encoder's output location and find as many as six JavaScript files. Obviously, Silverlight.js and MicrosoftAjax.js aren't what you're looking for, PlayerStrings.js is pretty empty, and BasePlayer.js clearly wasn't designed for easy editing as it has been compressed. So that just leaves player.js and StartPlayer.js. I'll come back to player.js in a moment, for it is StartPlayer.js that is the Expression Encoder's equivalent to Scene.xaml.js. 

function get_mediainfo(mediainfoIndex) {
switch (mediainfoIndex) {        
case 0:
return  { "mediaUrl": "MyVideo.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
default:
throw Error.invalidOperation("No such mediainfo");
}
}
function StartWithParent(parentId, appId) {
new StartPlayer_0(parentId);
}
function StartPlayer_0(parentId) {
this._hostname = EePlayer.Player._getUniqueName("xamlHost");
Silverlight.createObjectEx( {   source: 'player.xaml', 
parentElement: $get(parentId ||"divPlayer_0"), 
id:this._hostname, 
properties:{ width:'100%', height:'100%', version:'1.0', background:document.body.style.backgroundColor, isWindowless:'false' }, 
events:{ onLoad:Function.createDelegate(this, this._handleLoad) } } );
this._currentMediainfo = 0;      
}
StartPlayer_0.prototype= {
_handleLoad: function() {
this._player = $create(   ExtendedPlayer.Player, 
{ // properties
autoPlay    : true, 
volume      : 1.0,
muted       : false
}, 
{ // event handlers
mediaEnded: Function.createDelegate(this, this._onMediaEnded),
mediaFailed: Function.createDelegate(this, this._onMediaFailed)
},
null, $get(this._hostname)  ); 
this._playNextVideo();     
},    
_onMediaEnded: function(sender, eventArgs) {
window.setTimeout( Function.createDelegate(this, this._playNextVideo), 1000);
},
_onMediaFailed: function(sender, eventArgs) {
alert(String.format( Ee.UI.Xaml.Media.Res.mediaFailed, this._player.get_mediaUrl() ) );
},
_playNextVideo: function() {
var cVideos = 1;
if (this._currentMediainfo<cVideos)
this._player.set_mediainfo( get_mediainfo( this._currentMediainfo++ ) );    
}        
}
 

The first thing I noticed was that unlike the Visual Studio output, the _handleLoad function from Expression Encoder does not pass in a control or PlugIn reference, so how do you get one? Well the way I did it (and I could find no documentation anywhere on how to do this) was simply to add my own (new code is bold):

 StartPlayer_0.prototype= {
_handleLoad: function(control) {
this._player = $create(   ExtendedPlayer.Player, 
{ // properties
autoPlay    : true, 
volume      : 1.0,
muted       : false
}, 
{ // event handlers
mediaEnded: Function.createDelegate(this, this._onMediaEnded),
mediaFailed: Function.createDelegate(this, this._onMediaFailed)
},
null, $get(this._hostname)  ); 
control.Content.findName("MyXamlElement"); 
this._playNextVideo();     
},

 

That's all there is to it. Now go ahead and add your events as before. But wait, so what is player.js for then? Glad you asked. Player.js allows you to override the code in the BasePlayer.js file that so clearly was not meant for editing. For example, suppose you had created an animation in your XAML that made the video screen appear, perhaps from behind theatre style curtains that parted, or from behind some other element. You could override the play() function in BasePlayer.js to play your animation before the video:

Type.registerNamespace('ExtendedPlayer');
ExtendedPlayer.Player = function(domElement) {
ExtendedPlayer.Player.initializeBase(this, [domElement]);  
}
ExtendedPlayer.Player.prototype =  {
play: function() {    
this.get_element().content.findName('OpenCurtains').begin();
ExtendedPlayer.Player.callBaseMethod(this, 'play');
},
stop: function() {    
this.get_element().content.findName('CloseCurtains').begin();
ExtendedPlayer.Player.callBaseMethod(this, 'stop');
},
 	pause: function() {
 		alert('You clicked Pause');
ExtendedPlayer.Player.callBaseMethod(this, 'pause');
} 
}
ExtendedPlayer.Player.registerClass('ExtendedPlayer.Player', EePlayer.Player);

Posted by Williarob on Monday, November 19, 2007 11:21 AM
Permalink | Comments (0) | Post RSSRSS comment feed