В этом уроке я покажу свою собственную реализацию субтитров с использованием маркеров шкалы времени. Маркеры шкалы времени обычно используются с SmoothStreamingMediaElement , но в моем уроке я собираюсь использовать классы TimelineMarker и TimelineMarkerCollection для моего пользовательского элемента управления Media Player.
Создание XAML
Первым шагом является создание XAML для нашего пользовательского элемента управления Player. Я использовал очень простую версию, которая содержит только текстовое поле для отображения субтитров и 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>
Добавление свойств в codebehind
Затем нам нужно добавить свойства для хранения текущего маркера временной шкалы и коллекции маркеров временной шкалы для видео. Каждый маркер временной шкалы представляет один субтитр.
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; } }
Создание фиктивных субтитров (маркеров шкалы времени)
На следующем этапе я создал макет TimelineMarkerCollection, представляющий субтитры для видеоконтента. TimelineMarker генерируется каждые 400 миллисекунд и не генерируется в течение 5000-10000 миллисекунд.
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; }
Поиск текущего маркера
Самый важный шаг — найти текущий маркер. Я хочу, чтобы каждый из моих маркеров отображал не более 3000 миллисекунд (переменная MAXIMUM_MARKER_DURATION). Поиск текущего маркера должен выполняться в событии изменения положения MediaElement (это событие реализовано с использованием DispatcherTimer, также ознакомьтесь с разделом «Полная версия codebehind», чтобы понять логику события здесь). Ниже приведен код, который я использовал для поиска.
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(); } }
Полная версия codebehind
Ниже приведена полная версия кода, который я использовал для управления плеером:
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; } } }
Источник:
http://www.eugenedotnet.com/2011/01/w15-displaying-subtitles-using-timeline-markers/