В этом уроке я покажу свою собственную реализацию субтитров с использованием маркеров шкалы времени. Маркеры шкалы времени обычно используются с 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/