Статьи

Отображение субтитров с помощью маркеров шкалы времени в медиа-приложениях Windows Phone 7

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