Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Silverlight 2 - Video, Markers, XML

Blogs

Mike Taulty's Blog

Elsewhere

I encountered a chap the other day who was building an application which did something like this;

  1. Play a video
  2. Load a set of times and thumbnail images from an XML file.
  3. Highlight the thumbnail images at the point where the video hits the right position.

I thought this was a great example of how easy it can be to build solutions with Silverlight 2 ( regardless of whether Expression Encoder can already do some/all of this for you automatically ).

Starting with a UI, I might have;

<UserControl x:Class="SilverlightApplication12.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls">
    <Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
      <RowDefinition
        Height="7*" />
      <RowDefinition
        Height="3*" />
    </Grid.RowDefinitions>
    <MediaElement
      x:Name="media"
      Source="{Binding VideoUri}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
      Stretch="Uniform" />
    <ListBox
      x:Name="lstPictures"
      ItemsSource="{Binding Images}"
      Grid.Row="1"
      >    
      <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
          <StackPanel
            Orientation="Horizontal"/>
        </ItemsPanelTemplate>
      </ListBox.ItemsPanel>
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Image
            MaxWidth="96"
            MaxHeight="96"
            Margin="20"
            Stretch="Fill" 
            Source="{Binding}"/>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </Grid>
</UserControl>

and then I might have an XML file for my video which looks something like;

<video url="videos/video.wmv">
  <timeline>
    <marker timeInSeconds="2"
            image="images/img1.jpg"/>
    <marker timeInSeconds="4"
            image="images/img2.jpg"/>
    <marker timeInSeconds="6"
            image="images/img3.jpg"/>
  </timeline>
</video>

I want to databind as much as possible so I write some code to produce a class that suits my needs;

public class VideoInfo
  {
    // TODO: Cache results of executing these queries. Maybe.
    public Uri VideoUri
    {
      get
      {
        string url = (string)root.Attribute("url");

        return (new Uri(url, UriKind.Relative));
      }
    }
    public List<BitmapImage> Images
    {
      get
      {
        var query =
          from i in root.DescendantsAndSelf("marker")
          select new BitmapImage() {
            UriSource = new Uri((string)i.Attribute("image"), UriKind.Relative)
          };

        return (query.ToList());
      }
    }
    public void AddMarkersToMediaElement(MediaElement element)
    {
      var query =
        from i in root.DescendantsAndSelf("marker")
        select new TimelineMarker()
        {
          Time = new TimeSpan(0, 0, (int)i.Attribute("timeInSeconds")),
          Text = string.Empty,
          Type = string.Empty
        };

      foreach (var item in query)
      {
        element.Markers.Add(item);
      }
    }
    public void LoadFromXml(Uri xmlUri)
    {
      WebClient client = new WebClient();
      client.OpenReadCompleted += OnReadCompleted;
      client.OpenReadAsync(xmlUri);
    }
    void OnReadCompleted(object sender, OpenReadCompletedEventArgs e)
    {
      root = XElement.Load(XmlReader.Create(e.Result));

      if (Loaded != null)
      {
        Loaded(this, null);
      }
    }
    public event EventHandler Loaded;
    private XElement root;
  }

This just gives me a class that will load up the XML file and fire an event when it's loaded and then offers properties to my UI so that they can be databound. With that in place, I need very little code to make the application "function" such as;

  public partial class Page : UserControl
  {
    public Page()
    {
      InitializeComponent();

      this.Loaded += OnPageLoaded;
    }

    void OnPageLoaded(object sender, RoutedEventArgs e)
    {
      VideoInfo videoInfo = new VideoInfo();
      videoInfo.Loaded += OnInfoLoaded;
      videoInfo.LoadFromXml(
        new Uri("video.xml", UriKind.Relative));
    }

    void OnInfoLoaded(object sender, EventArgs e)
    {
      VideoInfo videoInfo = (VideoInfo)sender;

      this.DataContext = videoInfo;

      media.MediaOpened += (s, a) =>
        {
          videoInfo.AddMarkersToMediaElement(media);
        };

      media.MarkerReached += (s, a) =>
      {
        lstPictures.SelectedIndex++;
      };
    }
  }

 

and we're "in business" :-)


Posted Wed, Apr 16 2008 6:57 AM by mtaulty
Filed under:

Comments

Silverlight 2 - Video, Markers, XML wrote Silverlight 2 - Video, Markers, XML
on Thu, Apr 17 2008 12:30 AM
&lt;p&gt;I encountered a chap the other day who was buil