Asynchronous loading of TimelineMarkers for Silverlight Video Player
As part of the ongoing evolution of our Open Source Silverlight Video Player on CodePlex, I added the ability to asynchronously load a set of TimeLineMarkers from an XML document. The goal was to allow users a choice between encoding there video with markers or defining those markers in an external xml file. What I thought would be a pretty trivial task, proved to be an interesting challenge. Here is a snapshot of the Xml format.
The first step in implementing this functionality was to download the marker Xml. In the event that this marker file was quite large in size I decided to make the download asynchronous. Downloading a file asynchronously required that the path to the file is absolute. Since I have no real control over my initParams I decided to support both an absolute or relative Uri. In the event that the path to the Xml document is relative, I make a reasonable assumption that it is located in the same location as the XAP. As a result ,I can define my Uri with “Host.Source” as the baseUri of the Xml document. To Download the file, I simply make a WebRequest and define an AsyncCallback to be fired when the file has completed downloading.
Threading Considerations
In Silverlight 2 Beta 2 there was a significant change in the concurrency model used for asynchronous communications. In Beta 1 these type of requests returned on the UI thread. In Beta 2, when you choose to use the BeginGetResponse of the WebRequest you are telling Silverlight to use a worker thread that comes from a thread pool. As a result, you can NOT update any user interface elements on the UI thread. Initially I thought I would be able to use “Application.Current.RootVisual.Dispatcher” as a way to execute a delegate on the UIThread. Unfortunately this throws a Cross Thread exception. After explored a few options I realized that I could simply call Dispatcher.BeginInvoke(MyMethod) from within my AsyncCallback. here is what that looked like.
void OpenStreamCompleted(IAsyncResult ar)
{
HttpWebRequest request = ar.AsyncState as HttpWebRequest
WebResponse response = request.EndGetResponse(ar)
// Read the response into a Stream object.
Stream responseStream = response.GetResponseStream()
// Create a new StreamReader instance using the specified Stream object.
using (StreamReader streamreader = new StreamReader(responseStream))
{
XElement document = XElement.Load(streamreader)
_externalMarkerData = (from el in document.Elements() select GetMarkerData(el)).ToList()
}
Dispatcher.BeginInvoke(ProcessParsedMarkerCollection)
} As you can see above, once I have the Xml downloaded I parse it from Xml into a collection. For this I thought it would be cool to use Linq for XML. I am always amazed at how few a lines of code I need to write to parse an Xml document with Linq. The end product of the parse is a List<> containing a very simple custom object called “MarkerData”. If I had chosen to move this parsing back to the UIThread I could have just created a collection of TimelineMarkers, but I thought there was no harm in having my own object to expand over time.
To get the latest source visit the project at http://www.codeplex.com/sl2videoplayer

