In my previous posting about the VideoWiki project I mentioned that I fell off the edge of the world when I tried to replicate the work I had done previously with the player emitted by Encoder. I’m told by the team that this is very much a work in progress, so I don’t want to get bogged down in the details, but let’s take a look at some of the issues that came up and where the code sits today.
The version of Expression Encoder that I used (version 2 service pack 1, version 2.1.1216.0) created the Template solution which in turn contained four projects
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project(“{<GUID>}”) = “Template”, “Template.csproj”, “{<GUID>}”
EndProject
Project(“{<GUID>}”) = “MediaPlayer”, “MediaPlayer\MediaPlayer.csproj”, “{<GUID>}”
EndProject
Project(“{<GUID>}”) = “AdaptiveStreaming”, “AdaptiveStreaming\AdaptiveStreaming.csproj”, “{<GUID>}”
EndProject
Project(“{<GUID>}”) = “ExpressionPlayer”, “ExpressionPlayer\ExpressionPlayer.csproj”, “{<GUID>}”
EndProject
Our working project on the other hand, is, for now, a single project solution named Viki-Start) consisting of MainPage.xaml/.cs, App.xaml/.cs and a Views directory with the default three pair of files: AboutPage.xml/.cs, ErrorWindow.xaml/.cs and HomePage.xaml/.cs as well as the usual Properties, Resources, Bin, etc. folders.
Having templated ExpressionPlayer in Blend, we copy ExpressionPlayer.dll, MediaPlayer.dll and AdaptiveStreaming.dll into the bin/debug folder of Viki-Start and add them to the references of the project and we update App.xaml in Viki-start with the template that we created in blend (that is, in Template/App.xaml) being careful about namespaces.
What You Think You Did and What You Really Did
Twenty years ago I learned a good bit of close up magic. What I really liked about it was showing a trick and then listening to the person I showed it to describe what I did to someone else. What they saw was not what I did. For the past three days, I’ve been insisting to everyone, including myself, that in all the previous videos and tutorials, it worked perfectly fine to call MediaPlayer from within my project but now it changed with Silverlight 3. Wrong! Going back to find the original code so that I could point out the differences, there are none. Looking at the tutorial and the videos closely, I always launched the project from the html file – which worked then and works now; it is just that now I no longer want to do that; I want to launch from within the program.
What changed was not the ExchangePlayer (well, actually, it did change, but not in a way that affects this discussion) but my requirements. This is a lesson I have to keep learning: synchronicity is the bane of debugging. It is the error of post-hoc ergo prompter hoc (just because it broke after I moved to Silverlight 3 doesn’t mean the failure had anything to do with moving to Silverlight 3.).
My Current Hack
Sometimes, even if you know you will eventually throw it away, it is worth getting the code to work; if only to understand it in pieces. Here is how I got my code to work. When you ask encoder to create your starter application (which you’ll remember we then templated in Blend) it emits an entire project as shown above (Template.sln). The two files we most care about are ExpressionPlayerControl.cs and MediaPlayer.cs.
ExpressionPlayerControl is the control that we are actually templating. Its job is to display our media clip and raise an event when our markers are hit and it does so by inheriting from MediaPlayer and by creating a custom media stream source for adaptive streaming. In fact, the point of ExpressionPlayerControl is to extend the MediaPlayer to fully support Adaptive Streaming.
OnStartup
The idiom for feeding the source videos to a MediaPlayer (or an ExpressionPlayer) is to call OnStartup passing in the event arguments obtained from the html page that invoked the program. Assumptions are made. For example, the initial URI is created by obtaining the URI of the html page.
Uri uriStockContent = HtmlPage.Document.DocumentUri;
That value is then modified if there is a play list (which in my case there always was) in this section
if (e.InitParams.TryGetValue("playlist", out strInitValue))
{
try
{
Playlist.Clear();
Playlist.ParseXml(HtmlPage.Document.DocumentUri, strInitValue);
}
and we’re off to the races. The supporting code for PlayList is defined in the PlayListCollection class of MediaPLayer and PlayList itself is a dependency property defined to be of type PlayListCollection.
A key assumption built into MediaPlayer is that ParseXml expects a document to serve as the source URI of the media items – that is, the physical location of the document must be the same as the physical location of the videos.
When Is A MediaPlayer Not A MediaPlayer?
If you look up ParseXml in the Silverlight documentation for MediaPlayer you won’t find it. That is because the MediaPlayer that is listed in the documentation is not a Silverlight control, it is, rather, a player to run media using Silveright in an ASP.NET Web Page and is not the MediaPlayer emitted by Encoder. Same name, different beast.
My Temporary WorkAround
To get the application up and running for now, I overloaded OnStartup in ExpressionPlayer and in MediaPlayer and then copied the modified DLLs (along with the unmodified DLL for AdaptiveStreaming) into Viki-Start.
// ExpressionPlayer
public void OnStartup()
{
base.OnStartup();
Uri whereAmI = HtmlPage.Document.DocumentUri;
string pathAsString = whereAmI.ToString();
int offset = pathAsString.IndexOf( "TestPage.html" );
string stub = pathAsString.Substring( 0, offset );
Playlist.Clear();
PlaylistItem pli = new PlaylistItem();
pli.MediaUrl = new Uri( stub + "/encoded.wmv" );
pli.IsAdaptiveStreaming = false;
pli.ThumbSource = stub + "/test.jpg";
pli.Title = "Encoded Video";
pli.Description = "Hardcoded Hypervideo";
Playlist.Add( pli );
}
// MediaPlayer
public virtual void OnStartup()
{
Playlist.Clear();
m_autoPlayCache = true;
m_autoLoadCache = true;
m_mutedCache = false;
m_stretchMode = Stretch.None;
UserBackgroundColor = new SolidColorBrush( ColorFromString( "#FF0000FF" ) );
DisplayTimeCode = false;
CaptionsVisibility = Visibility.Collapsed;
}
Not beautiful, but sufficient to keep the project moving.
Previous ViKi Version 0.0.01