Displaying Subtitles using Timeline Markers in Windows Phone 7 media applications
Join the DZone community and get the full member experience.
Join For FreeIn this tutorial I will show my custom implementation of subtitles using Timeline Markers. Timeline Markers are usually used with SmoothStreamingMediaElement, but in my tutorial I am going to use TimelineMarker and TimelineMarkerCollection classes for my custom Media Player control.
Creating XAML
First step is to create a XAML for our custom Player user control. I have used a very simple version of it that contains only a textbox for displaying subtitle and a MediaElement.
<Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <MediaElement Grid.Row="0" Height="350" x:Name="meElement" /> <TextBlock Grid.Row="0" x:Name="tbMarker" Margin="10,0,10,35" VerticalAlignment="Bottom" TextWrapping="Wrap" /> </Grid>
Adding properties to codebehind
Next we need to add properties to hold a current timeline marker and a collection of timeline markers for a video. Each timeline marker represents a single subtitle.
private TimelineMarkerCollection Markers { get; set; } private TimelineMarker _currentMarker; public TimelineMarker CurrentMarker { get { return _currentMarker; } set { _currentMarker = value; tbMarker.Text = _currentMarker != null ? _currentMarker.Text : string.Empty; tbMarker.Visibility = _currentMarker != null ? Visibility.Visible : Visibility.Collapsed; } }
Creating Mock Subtitles (Timeline Markers)
During the next step I have created a mock TimelineMarkerCollection representing subtitles for a video content. TimelineMarker is generated every 400 milliseconds and not generated during 5000-10000 millisecond time span.
public static TimelineMarkerCollection CreateMockMarkers(TimeSpan duration) { TimelineMarkerCollection tmc = new TimelineMarkerCollection(); for (double i = 400; i < duration.TotalMilliseconds; i = i + 400) { if (i < 5000 || i > 10000) { tmc.Add(new TimelineMarker() { Text = string.Format("Some Text at {0}", i), Time = TimeSpan.FromMilliseconds(i) }); } } return tmc; }
Searching for current marker
Most important step is to find current marker. I want each of my markers to display not longer than 3000 milliseconds (MAXIMUM_MARKER_DURATION variable). Search for a current marker should be accomplished within MediaElement position change event(this event is implemented using DispatcherTimer, also check the next section “Full version of codebehind” to understand the event logic here). Bellow is a code I’ve used for searching.
private void CheckMarkers(double position) { if (Markers != null && Markers.Any()) { CurrentMarker = (from item in Markers where item.Time != null && item.Time.TotalMilliseconds <= (position + MAXIMUM_MARKER_DURATION) && item.Time.TotalMilliseconds >= position orderby item.Time descending select item).FirstOrDefault(); } }
Full version of codebehind
Bellow is a full version of codebehind I’ve used for Player control:
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; using System.Windows.Threading; using System.Windows.Data; using System.Globalization; using System.ComponentModel; namespace EugeneDotnetWPSamples.MediaPlayer { public partial class MediaPlayer : UserControl { DispatcherTimer currentPosition = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(200) }; private double MAXIMUM_MARKER_DURATION = 3000; private TimelineMarkerCollection Markers { get; set; } private TimelineMarker _currentMarker; public TimelineMarker CurrentMarker { get { return _currentMarker; } set { _currentMarker = value; tbMarker.Text = _currentMarker != null ? _currentMarker.Text : string.Empty; tbMarker.Visibility = _currentMarker != null ? Visibility.Visible : Visibility.Collapsed; } } public MediaPlayer() { InitializeComponent(); meElement.Source = new Uri(@"http://vnfiles.ign.com/nwvault.ign.com/fms/files/movies/53/CEP1080028433040trailer_low.wmv"); meElement.MediaOpened += (s, e) => { Markers = CreateMockMarkers(meElement.NaturalDuration.TimeSpan); }; currentPosition.Tick += (s, e) => { CheckMarkers(meElement.Position.TotalMilliseconds); }; meElement.CurrentStateChanged += (s, e) => { if (meElement.CurrentState == MediaElementState.Playing) currentPosition.Start(); else currentPosition.Stop(); }; } private void CheckMarkers(double position) { if (Markers != null && Markers.Any()) { CurrentMarker = (from item in Markers where item.Time != null && item.Time.TotalMilliseconds <= (position + MAXIMUM_MARKER_DURATION) && item.Time.TotalMilliseconds >= position orderby item.Time descending select item).FirstOrDefault(); } } public static TimelineMarkerCollection CreateMockMarkers(TimeSpan duration) { TimelineMarkerCollection tmc = new TimelineMarkerCollection(); for (double i = 400; i < duration.TotalMilliseconds; i = i + 400) { if (i < 5000 || i > 10000) { tmc.Add(new TimelineMarker() { Text = string.Format("Some Text at {0}", i), Time = TimeSpan.FromMilliseconds(i) }); } } return tmc; } } }
Source: http://www.eugenedotnet.com/2011/01/w15-displaying-subtitles-using-timeline-markers/
Opinions expressed by DZone contributors are their own.
Comments