Потоковая передача медийной информации повсеместно распространилась в Интернете. Такое впечатление, словно все — от новостных сайтов и социальных сетей до соседа — участвуют в создании онлайнового видео. Из-за такого скачка в популярности большинство сайтов стремится предоставлять своим посетителям видео высокого качества (нередко оптимизируемое под пропускную полосу соединения), причем надежным и дружественным к пользователям способом.
Ключевым элементом в онлайновой доставке медийной информации является сам проигрыватель. Именно с ним взаимодействует посетитель, и он определяет все аспекты использования. При такой концентрации внимания на проигрывателе неудивительно, что современные медийные веб-проигрыватели стали гораздо сложнее в реализации, чем это было несколько лет назад. В итоге разработчикам нужна надежная инфраструктура, на основе которой они могли бы создавать свои проигрыватели.
Silverlight Media Framework (SMF) — проект с открытым исходным кодом, выпущенный Microsoft на конференции PDC 2009 (Professional Developers Conference). Это расширяемая и высокомасштабируемая инфраструктура видео для Silverlight, образующая стабильное ядро, на основе которого разработчики и проектировщики могут создавать собственные проигрыватели. Исходный код Silverlight Media Framework был существенно переработан с учетом уроков, извлеченных из проектов веб-видео NBC Olympics и Sunday Night Football.
В этой статье поясняются основные элементы SMF, демонстрируется, как интегрировать SMF в собственные проекты проигрывателей, и пошагово разбирается простой проект, в котором создается проигрыватель с применением SMF. Я покажу, как использовать протоколирование (logging), параметры настройки и средства обработки событий в SMF. Наконец, мы создадим приложение-проигрыватель, показывающее по окончании текущего видеоролика рекомендации по просмотру видео на ту же тему.
Начинаем работу с SMF
Первым делом нужно скачать инфраструктуру с сайта Codeplex (smf.codeplex.com). Вы также должны скачать Smooth Streaming Player Development Kit (iis.net/expand/smoothplayer) и ссылаться на него в любых проектах, где используется SMF. Smooth Streaming Player Development Kit не является частью SMF — это совершенно самостоятельный компонент с закрытым исходным кодом. Однако SMF использует базовый набор его функциональности, в частности сам видеопроигрыватель. На момент написания статьи Smooth Streaming Player Development Kit находится в состоянии второй бета-версии.
SMF состоит из нескольких сборок Microsoft .NET (рис. 1), каждая из которых содержит свою функциональную часть общей инфраструктуры.
Увеличить
Рис. 1 Сборки Silverlight Media Framework
Основной сборкой является Microsoft.SilverlightMediaFramework.dll, которая содержит ряд вспомогательных классов и типов, на которые ссылаются остальные части инфраструктуры. При любом использовании SMF вы также должны ссылаться на сборку Microsoft.SilverlightMediaFramework.dll.
Пространство имен Microsoft.SilverlightMediaFramework.Data предоставляет вспомогательные классы для использования внешних к проигрывателю данных и инкапсуляции данных внутри проигрывателя. Данные могут быть самыми разными в любой форме, но в то же время они могут быть настроечной информацией для самого проигрывателя. В связи с этим существует другое пространство имен, Microsoft.SilverlightMediaFramework.Data.Settings, в котором содержатся типы, представляющие настроечные данные проигрывателя и позволяющие работать с ними.
Помимо данных, используемых для настройки, тип, с которым вам скорее всего придется взаимодействовать в пространстве имен Data — класс DataClient, способный получать данные от внешнего источника. Поэтому, если вы хотите скачивать и использовать данные, внешние по отношению к проигрывателю, вы должны сослаться на эту сборку.
SMF-проигрыватель включает отказоустойчивую инфраструктуру Microsoft.SilverlightMediaFramework.Logging, в которой применяется парадигма на основе обратных вызовов; вы регистрируете свои методы обратного вызова в системе регистрации событий (logging system), и эти методы выполняют дополнительные операции при обратном вызове, например передают информацию веб-сервису или отображают ее в текстовом поле. Ссылка на данную сборку нужна, если вы хотите задействовать встроенные механизмы регистрации событий SMF.
В сборке Microsoft.SilverlightMediaFramework.Player реализован сам проигрыватель. Она также предоставляет ряд элементов управления, используемых проигрывателем, например регулятор громкости, маркеры временной шкалы и др. Исходный SMF-проигрыватель предельно компактен и является отличной отправной точкой для любого проекта, в котором нужен проигрыватель на основе Silverlight. Однако главная особенность всех элементов управления, определенных в SMF, — концепция их создания на основе шаблонов (templating), поэтому к каждому элементу управления можно применять различные темы, используя такие инструменты, как Expression Blend или Visual Studio.
Компиляция SMF и ссылки на нее
SMF скачивается в виде ZIP-архива, в котором вы найдете файл решения, проект для каждой выходной библиотеки и тестовые проекты для запуска и проверки самого проигрывателя.
SMF опирается на Smooth Streaming Player Development Kit. Для ссылки на этот набор переместите сборку Smooth Streaming (Microsoft.Web.Media.SmoothStreaming.dll) в папку \Lib проекта SMF.
Далее откройте решение SMF в Visual Studio и скомпилируйте его, создав все сборки, необходимые для использования этой инфраструктуры. Чтобы проверить, что все работает правильно, нажмите F5 для запуска отладки. При этом решение будет скомпилировано, запустится мишень отладки Microsoft.SilverlightMediaFramework.Test.Web, и вы увидите исходный SMF-проигрыватель, который воспроизводит потоковое видео «Big Buck Bunny» (рис. 2). Заметьте, что исходный проигрыватель имеет вполне законченный вид со всеми необходимыми элементами управления.
Рис. 2 Проигрыватель SMF и видео «Big Buck Bunny»
Следующий шаг — создание собственного (отдельного) проекта Silverlight и использование из него инфраструктуры SMF. В Visual Studio выберите File | New | Project | Silverlight Application. Назовите свое решение SMFPlayerTest и щелкните кнопку OK. Появится модальное диалоговое окно, предлагающее разместить это приложение Silverlight на новом веб-сайте. Щелкните OK и вы увидите базовое решение для приложения Silverlight, состоящее из двух проектов:SMFPlayerTest и SMFPlayerTest.Web.
Наконец из только что созданного проекта нужно сослаться на сборки Smooth Streaming Player Development Kit и SMF. Скопируйте выходные сборки SMF и Smooth Streaming Player Development Kit из папки Debug решения SMF в свой проект, как показано на рис. 3. Ваше новое решение теперь включает все ссылки на сборки, необходимые для использования всех возможностей SMF.
Рис. 3 Ссылки и необходимые сборки
Отображение проигрывателя
Чтобы приступить к использованию SMF, включите пространство имен SMF-проигрывателя в свою страницу MainPage.xaml. Это обеспечивает корректное разрешение всех ссылок:
xmlns:p="clr-namespace:Microsoft.SilverlightMediaFramework.Player;assembly=Microsoft.SilverlightMediaFramework.Player"
Теперь вставьте XAML проигрывателя в элемент управления Grid с именем LayoutRoot в странице:
<Grid x:Name="LayoutRoot">
<p:Player>
</p:Player>
</Grid>
Нажав F5, вы запустите проект, и появится SMF-проигрыватель. Однако, поскольку проигрывателю пока не указано, что именно следует воспроизводить, он ничего не делает. Все, что вы увидите, — окно проигрывателя без воспроизводимого контента.
Для воспроизведения видео SMF использует SmoothStreamingMediaElement (из Smooth Streaming Player Development Kit). SMF наследует от SmoothStreamingMediaElement собственный проигрыватель CoreSmoothStreamingMediaElement. Этот объект нужен, если вы хотите, чтобы проигрыватель воспроизводил потоковый контент. Не забудьте задать в свойстве SmoothStreamingSource корректный URL:
<Grid x:Name="LayoutRoot">
<p:Player>
<p:CoreSmoothStreamingMediaElement
AutoPlay="True"
SmoothStreamingSource="replace with address to content here" />
</p:Player>
</Grid>
Microsoft предоставляет образец потокового видео под названием «Big Buck Bunny», который можно использовать для тестирования проектов на Silverlight. Чтобы задействовать этот тестовый поток, запишите в свойство SmoothStreamingSource объекта CoreSmoothStreamingMediaElement следующее значение:
http://video3.smoothhd.com.edgesuite.net/ondemand/Big%20Buck%20Bunny%20Adaptive.ism/Manifest
И вновь нажмите F5 для компиляции и запуска проекта. Браузер запустится с тем же проигрывателем, что и раньше, но на этот раз начнется потоковое воспроизведение видео «Big Buck Bunny» после полной загрузки проигрывателя. Если вашей задачей было создание базового проигрывателя на Silverlight для воспроизведения потокового контента, вы ее решили.
Однако SMF предлагает гораздо больше, чем мы видели до сих пор. Давайте добавим кое-что из базовой регистрации событий.
Регистрация событий в проигрывателе
Система регистрации в SMF очень проста:всякий раз, когда фиксируется какое-либо событие, оно вызывает событие LogReceived. Вы регистрируете обработчик для этого события, а затем получаете уведомление о каждом фиксируемом событии. Что вы делаете с этим уведомлением дальше — исключительно ваша забота; вы можете показывать его в новом окне внутри проигрывателя, фильтровать события и уведомлять веб-сервис всякий раз, когда возникает определенное событие, или что хотите еще.
Событие LogReceived статически определено в классе Logger (он содержится в Microsoft.SilverlightMediaFramework.Logging.dll), поэтому регистрироваться на фиксируемые события можно в любой части проекта. Вот пример определения и регистрации обработчика в файле MainPage.xaml проекта SMFPlayerTest:
public partial class MainPage : UserControl {
public MainPage() {
InitializeComponent();
Logger.LogReceived +=
new EventHandler<SimpleEventArgs<Log>>(
Logger_LogReceived);
}
void Logger_LogReceived(object sender,
Microsoft.SilverlightMediaFramework.SimpleEventArgs<Log> e) {
throw new NotImplementedException();
}
}
SMF генерирует довольно много событий. Чтобы увидеть их, создайте точку прерывания в методе Logger_LogReceived и вновь запустите проигрыватель в режиме Debug. Почти сразу же сработает точка прерывания, и вы сможете пошагово пройти через параметры метода и посмотреть передаваемую через них информацию.
Данные события журнала (log event data) упаковываются в специальный объект, чей тип должен наследовать от абстрактного класса Log с тремя свойствами: Sender, Message и TimeStamp. Sender ссылается на объект, который вызвал событие. Message — это объект типа System.String, содержащий текст зафиксированного события. TimeStamp просто хранит дату и время первого создания объекта. Объект SimpleEventArgs<>, передаваемый как второй параметр в ваш обработчик событий, хранит ссылку на объект Log в свойстве Result.
Чтобы сгенерировать событие журнала, нужно лишь создать экземпляр типа, который наследует от базового класса Log, а затем передать этот тип в статически определенный метод Log типа Logger. Инфраструктура предоставляет класс DebugLog, уже наследующий от базового типа Log. Особенность типа DebugLog в следующем.Если библиотеки, на которые ссылается ваш проект на Silverlight, были созданы при компиляции SMF в отладочном режиме (Debug), то передача типа DebugLog в SMF-систему регистрации приведет к генерации соответствующего события регистрации (logging event), а значит, запустит ваш обработчик. С другой стороны, SMF, скомпилированная в режиме Release, проигнорирует любой вызов метода Log, при котором передается объект DebugLog. В итоге, если у вас есть отладочные выражения, вам нужно использовать только сборки в режиме Debug и объект DebugLog в качестве аргумента события; в ином случае вам придется конструировать собственный тип, наследующий от абстрактного типа Log.
Вот пример, где событие Listening генерируется через систему событий SMF созданием экземпляра объекта DebugLog и его передачей статическому методу Log объекта Logger (не забудьте скомпилировать файлы Smooth Streaming Player Development Kit в режиме Debug):
public MainPage() {
InitializeComponent();
Logger.LogReceived +=
new EventHandler<SimpleEventArgs<Log>>(
Logger_LogReceived);
Logger.Log(new DebugLog {
Message = "Listening!", Sender = this });
}
Наследование от класса Player
Хотя регистрация событий — основная особенность проигрывателя, функции воспроизведения SMF доступны, только если вы наследуете от SMF-типа Player и самостоятельно расширяете его.
Чтобы увидеть, как это работает, нужно создать новый класс SMFPlayer, производный от типа Player.
Новый класс SMFPlayer выглядит так:
namespace SMFPlayerTest {
public class SMFPlayer : Player {
public override void OnApplyTemplate() {
base.OnApplyTemplate();
}
}
}
Каждый тип FrameworkElement (вроде Player в SMF) имеет метод OnApplyTemplate, вызываемый всякий раз, когда генерируется событие ApplyTemplate. Этот метод часто служит удобной начальной точкой при инициализации типа FrameworkElement.
В данном случае я переопределяю исходный метод OnApplyTemplate в новом классе SMFPlayer. Чтобы продемонстрировать, что вместо исходного типа Player выполняется новый тип SMFPlayer, вы можете установить точку прерывания в переопределенный метод. При отладке проигрывателя в Visual Studio эта точка прерывания сработает, когда Silverlight начнет выполнять SMFPlayer.
Теперь обновите файл MainPage.xaml, чтобы использовать новый класс проигрывателя. Прежде всего включите пространство имен проигрывателя в список пространств имен, на которые уже есть ссылки (так же, как вы делали это раньше):
xmlns:smf="clr-namespace:SMFPlayerTest"
Затем просто обновите теги Player в XAML для использования SMFPlayer вместо Player:
<Grid x:Name="LayoutRoot">
<smf:SMFPlayer>
<p:CoreSmoothStreamingMediaElement
AutoPlay="true"
SmoothStreamingSource="http://..." />
</smf:SMFPlayer>
</Grid>
Далее создайте экземпляр класса DebugLog и передайте его в метод Log, как было показано ранее. Это приведен к срабатыванию события, на которое вы зарегистрировали свой обработчик:
public override void OnApplyTemplate() {
Logger.Log(new DebugLog {
Message = "Hello from OnApplyTemplate!",
Sender = this
});
base.OnApplyTemplate();
}
Для прослушивания конкретно этого события из обработчика фильтруйте свойство Message объекта DebugLog. В данном примере ищите любое сообщение, содержащее «OnApplyTemplate»:
void Logger_LogReceived(
object sender, SimpleEventArgs<Log> e) {
if (e.Result.Message.Contains("OnApplyTemplate")) {
return;
}
}
Использование настроечных данных
Зрелая инфраструктура обработки конфигурационных параметров крайне важна в большинстве крупномасштабных проектов ПО. Код обработки таких параметров в SMF встроен в сборку Microsoft.SilverlightMediaFramework.Data.dll, которая позволяет скачивать универсальные внешние данные. Уровень настроек SMF использует эту инфраструктуру для поиска и скачивания XML-файла настроек, размещенного на веб-сервере. Как только эти данные успешно скачиваются и считываются, уровень настроек SMF инкапсулирует их в объект SettingsBase, методы которого впоследствии используются для получения нужных значений.
Класс SettingsBase, как и предполагает его имя, служит базой для более специфичного класса, который может обеспечить строго типизированный доступ к вашим настроечным данным. Вот пример класса, производного от SettingsBase. У него два свойства:одно — для получения URL источника видеопроигрывателя, а другое — для извлечение булева значения, указывающего, что должен делать видеопроигрыватель — автоматически запускать воспроизведение или ждать нажатия соответствующей кнопки:
namespace SMFPlayerTest {
public class SMFPlayerTestSettings : SettingsBase {
public Uri VideoPlayerSource {
get { return new Uri(
GetParameterValue("videoSource")); }
}
public bool? AutoStartVideo {
get { return GetParameterBoolean(
"autoStart"); }
}
}
}
Методы свойств используют функции, реализованные в классе SettingsBase, для просмотра нижележащего набора пар «имя-значение», загружаемых в тип (через механизм, о котором я расскажу чуть позже). Это обеспечивает извлечение настроечной информации безопасным с точки зрения типизации и дружественным к IntelliSense способом.
Теперь создаем в проекте SMFPlayerTest.Web новый XML-файл, присваиваем ему имя SMFPlayerSettings.xml и добавляем в него:
<?xml version="1.0" encoding="utf-8" ?>
<settings>
<Parameters>
<Parameter
Name="videoSource"
Value="http://video3.smoothhd.com.edgesuite.net/ondemand/Big%20Buck%20Bunny%20Adaptive.ism/Manifest" />
<Parameter Name="autoStart" Value="True" />
</Parameters>
</settings>
Далее создаем объект SettingsClient, в который вы будете загружать конфигурационные XML-данные. SettingsClient принимает URI, указывающий на файл настроек:
m_settingsGetter = new SettingsClient(
new Uri("http://localhost:10205/SMFPlayerSettings.xml"));
Извлечение настроечных данных осуществляется асинхронно, поэтому методу RequestCompleted объекта SettingsClient нужно назначить метод обратного вызова:
m_settingsGetter.RequestCompleted +=
new EventHandler<SimpleEventArgs<SettingsBase>>
(m_settingsGetter_RequestCompleted);
Последний шаг — запуск непараметризованного метода Fetch объекта SettingsClient. После получения данных будет вызван обработчик события settingsGetter_RequestCompleted; при этом ему будет передан объект SettingsBase:
void m_settingsGetter_RequestCompleted(
object sender, SimpleEventArgs<SettingsBase> e) {
SettingsBase settingsBase = e.Result;
return;
}
В объект SettingsBase, переданный в метод settingsGetter_RequestCompleted, из файла SMFPlayerSettings.xml загружаются пары «имя-значение», извлеченные за вас нижележащей инфраструктурой.Чтобы загрузить эти данные в ваш объект SMFPlayerTestSettings, вы просто вызываете метод Merge, который объединяет конфигурационную информацию из объектов, производных от SettingsBase:
SettingsBase settingsBase = e.Result;
m_settings.Merge(settingsBase);
this.mediaElement.SmoothStreamingSource =
m_settings.VideoPlayerSource;
this.mediaElement.AutoPlay =
(bool)m_settings.AutoStartVideo;
return;
Вам больше не нужно «зашивать» значения свойств AutoPlay и SmoothStreamingSource объекта CoreSmoothStreamingMediaElement в XAML-страницу, так как настройки проигрывателя скачиваются в методе OnApplyTemplate. Вот все, что вам нужно для XAML проигрывателя:
<Grid x:Name="LayoutRoot">
<smf:SMFPlayer>
<p:CoreSmoothStreamingMediaElement />
</smf:SMFPlayer>
</Grid>
При запуске проигрывателя будут загружены все настроечные данные, обратный вызов загрузит значения в медийный элемент (media element) проигрывателя, и начнется воспроизведение потокового видео.
Расширение SMF-проигрывателя
На многих популярных сайтах хостинга видео, когда завершается воспроизведение видео, вы видите список рекомендуемых к просмотру сходных по теме видеороликов. Чтобы проиллюстрировать, насколько легко расширить SMF-проигрыватель, давайте пошагово рассмотрим, как создать аналогичную функцию рекомендаций в проекте SMFPlayerTest.
Начнем с добавления атрибута x:Name к элементу Player в файле MainPage.xaml:
<Grid x:Name="LayoutRoot">
<smf:SMFPlayer x:Name="myPlayer">
<p:CoreSmoothStreamingMediaElement />
</smf:SMFPlayer>
</Grid>
Это позволяет ссылаться на объект SMFPlayer по имени из Visual Studio и Expression Blend.
Теперь щелкните правой кнопкой мыши файл MainPage.xaml в Solution Explorer и выберите Open в Expression Blend. Это приведет к запуску Expression Blend 3, и вы увидите интерфейс проектирования SMF-проигрывателя. В разделе Objects and Timeline найдите узел myPlayer в дереве визуальных объектов, имя которого соответствует имени, ранее присвоенному объекту SMFPlayer. Цель — создать шаблон для SMFPlayer, а затем добавить три кнопку Suggestion в этот шаблон. Используя шаблон в Expression Blend, вы сможете добавлять, редактировать или удалять элементы управления, встроенные в сам проигрыватель.
Чтобы создать шаблон, щелкните правой кнопкой мыши myPlayer в окне Objects and Timeline и выберите Edit Template | Edit a Copy. Когда появится диалоговое окно Create Style Resource, щелкните OK. Чтобы вставить три кнопки на поверхность видеопроигрывателя, дважды щелкните значок кнопки в окне Tools — и делайте так для каждой кнопки, которую вы хотите добавить.Теперь в дереве элементов управления, образующих шаблон проигрывателя, должны быть видны три кнопки (рис. 4).
Рис. 4 Элементы управления Button, добавленные к дереву элементов управления
Выберите все три кнопки в дереве, переключитесь в окно свойств для этих элементов управления и задайте горизонтальное и вертикальное выравнивание по центру (рис. 5).
Рис. 5 Установка выравнивания элемента управления Button
Кнопки имеют размер по умолчанию и лежат одна на другой. Задайте ширину каждой кнопки, равной 400, а высоту — 75. Настройте зазоры так, чтобы первая кнопка была смещена на 175 пикселей от нижней границы, вторая — на 175 пикселей от верхней границы, а у третьей вообще не было никакого смещения. В итоге кнопки должны выглядеть, как показано на рис. 6.
Рис. 6Кнопки, выровненные по центру, в Expression Blend
Чтобы убедиться в корректности размещения кнопок на поверхности проигрывателя, сохраните все открытые файлы в Expression Blend и вернитесь в Visual Studio. Visual Studio может предложить перезагрузить документы, измененные в Expression Blend. В таком случае согласитесь, щелкнув OK. В Visual Studio нажмите F5, чтобы перезапустить SMF-проигрыватель в режиме Debug. Теперь в проигрывателе должны появиться три кнопки, выровненные по центру и по нижней границе окна воспроизведения видео (рис. 7.).
Рис. 7 Кнопки, выровненные по центру, в проигрывателе SMF
Подключение обработчиков событий
С кнопками нужно сопоставить обработчики событий. Чтобы ссылаться на кнопки из кода, нужно присвоить им имена — это делается с помощью текстового поля Name на вкладке Properties. Для простоты назовем кнопки Button1, Button2 и Button3. После этого окно Objects and Timeline должно обновиться и отобразить имена кнопок рядом с их значками в дереве визуальных объектов.
На вкладке Properties для каждой кнопки вы найдете кнопку Events, с помощью которой визуальному компоненту назначаются обработчики событий. Выберите одну из кнопок, щелкните кнопку Events на вкладке Properties, а затем дважды щелкните поле Click для автоматической генерации обработчика этого события в MainPage.xaml.cs. В окне свойств для каждой кнопки теперь будет показываться обработчик, назначенный ее событию Click (рис. 8), а в файле MainPage.xaml.cs появятся обработчики события Click каждой кнопки.
Рис. 8 Установка обработчика событий
Теперь вы можете заняться отладкой проигрывателя. Щелкнув любую кнопку на экране, вы вызовете событие Click, которое обрабатывается автоматически сгенерированными методами в MainPage.xaml.cs.
Рекомендуемые видеоролики
А теперь давайте используем эти кнопки для поддержки рекомендаций по просмотру видеороликов на сходные темы. Предложения (рекомендации) будет представлять следующий XML:
<?xml version="1.0" encoding="utf-8" ?>
<Suggestions>
<Suggestion DisplayName="A suggestion" Url="" />
<Suggestion DisplayName="Another suggestion" Url="" />
<Suggestion DisplayName="My final suggestion" Url="" />
</Suggestions>
Значение атрибута Url будет указывать, какой видеоролик загрузит проигрыватель при щелчке кнопки, а атрибут DisplayName содержит текст, который выводится на кнопке. Сохраните этот файл под именем Suggestions.xml в проекте SMFPlayerTest.Web.
Тип DataClient (в пространстве имен Microsoft.SilverlightMediaFramework.Data) будет использоваться для загрузки XML-документа и представления контента со строгой типизацией. Чтобы сделать каждое чтение Suggestion из XML-файла строго типизированным, создайте класс SMFPlayerTestSuggestion в своем проекте Silverlight:
namespace SMFPlayerTest {
public class SMFPlayerTestSuggestion {
public string DisplayName;
public Uri Url;
}
}
DataClient, как и SettingsBase, рассчитан на наследование классом, который поддерживает строго типизированное представление данных из XML-контента (в нашем случае это массив объектов SMFPlayerTestSuggestion).
Создайте другой файл класса в проекте SMFPlayerTest и назовите его SMFPlayerTestDataClient:
namespace SMFPlayerTest {
public class SMFPlayerTestDataClient :
DataClient<SMFPlayerTestSuggestion[]> {
public SMFPlayerTestDataClient(Uri Url) : base(Url) { }
protected override void OnRequestCompleted(
object sender, SimpleEventArgs<string> e) {
throw new NotImplementedException();
}
}
}
SMFPlayerTestDataClient наследует от DataClient и присваивает свой аргумент шаблона массиву типов SMFPlayerTestSuggestion. Базовый класс DataClient предоставляет всю логику асинхронных сетевых операций, необходимую для подключения и скачивания внешнего XML-файла. Однако после скачивания контента базовый класс DataClient вызывает OnRequestCompleted и ожидает, что вся обработка XML-данных будет происходить там. Иными словами, базовый класс DataClient скачивает контент, но тот, кто реализует OnRequestCompleted, отвечает за все операции над этим контентом.
Вот более полная реализация OnRequestCompleted:
protected override void OnRequestCompleted(
object sender, SimpleEventArgs<string> e) {
XDocument doc = XDocument.Parse(e.Result);
List<SMFPlayerTestSuggestion> suggestions =
new List<SMFPlayerTestSuggestion>();
foreach (XElement element in doc.Descendants("Suggestion")) {
suggestions.Add(new SMFPlayerTestSuggestion {
DisplayName = element.Attribute("DisplayName").GetValue(),
Url = element.Attribute("Url").GetValueAsUri()
});
}
base.OnFetchCompleted(suggestions.ToArray());
}
Для упрощения картины, чтобы анализировать нужные элементы и атрибуты в XML, я использовал в этой реализации LINQ to XML. Как только из каждого узла Suggestion получены значения атрибутов DisplayName и Url, создается экземпляр объекта SMFPlayerTestSuggestion и выполняется присваивание значений.
Заключительный шаг — запуск OnFetchCompleted. Внешние потребители SMFPlayerTestDataClient могут регистрировать обработчики на получение уведомления о событии FetchCompleted, когда завершается скачивание данных по рекомендуемым видеороликам. Поскольку OnRequestCompleted упаковывает XML-данные с целью строгой типизации, каждый обработчик события получит удобный массив объектов SMFPlayerTestSuggestion — по одному для каждого элемента Suggestion в XML-документе, загруженном базовым классом DataClient.
Нижележащий DataClient предоставляет метод Fetch, при вызове которого начинается процесс асинхронного скачивания контента. Чтобы приступить к скачиванию данных, относящихся к рекомендациям, по окончании видеоролика, подключите обработчик mediaElement_MediaEnded к событию MediaEnded объекта MediaElement:
void mediaElement_MediaEnded(
object sender, RoutedEventArgs e) {
m_client = new SMFPlayerTestDataClient(
new Uri("http://localhost:10205/Suggestions.xml"));
m_client.FetchCompleted +=
new EventHandler<SimpleEventArgs<
SMFPlayerTestSuggestion[]>>(m_client_FetchCompleted);
m_client.Fetch();
}
Метод mediaElement_MediaEnded создает экземпляр типа SMFPlayerTestDataClient, назначает другой обработчик событию FetchCompleted, а затем вызывает Fetch, чтобы начать процесс скачивания. Обработчик FetchCompleted будет запущен вызовом OnFetchCompleted, реализованным ранее в OnRequestCompleted (который вызывается базовым классом DataClient после того, как контент скачан).
Реализация suggestion_FetchCompleted, зарегистрированная в mediaElement_MediaEnded, принимает строго типизированный массив данных Suggestion и назначает по одному Suggestion каждой кнопке:
void m_client_FetchCompleted(
object sender, SimpleEventArgs<
SMFPlayerTestSuggestion[]> e) {
for (int c = 1; c <= 3; c++) {
Button btn = (Button)GetTemplateChild(
"Button" + c.ToString());
btn.Tag = e.Result[c - 1].Url;
btn.Content =
e.Result[c - 1].DisplayName;
}
}
GetTemplateChild — метод нижележащего типа FrameworkElement — получает ссылку на каждую кнопку, определенную в MainPage XAML. Для каждой кнопки отображаемый текст присваивается свойству Content, а URI — свойству Tag. После этого обработчик события Click каждой кнопки может извлечь URI из свойства Tag и назначить URL элементу MediaElement проигрывателя для воспроизведения потокового видео:
private void Button1_Click(
object sender, System.Windows.RoutedEventArgs e) {
Uri redirectUrl = (Uri)((Button)sender).Tag;
myPlayer.MediaElement.SmoothStreamingSource =
redirectUrl;
}
Отображение кнопок
Теперь мы должны скрывать кнопки, пока воспроизводится текущее потоковое видео, а по окончании этого процесса вновь делать кнопки видимыми. Как только пользователь щелкает кнопку, все кнопки вновь скрываются.
В Visual Studio отредактируйте класс SMFPlayer, дополнив его двумя атрибутами TemplateVisualState:
[TemplateVisualState(Name = "Hide", GroupName = "SuggestionStates")]
[TemplateVisualState(Name = "Show", GroupName = "SuggestionStates")]
public class SMFPlayer : Player
TemplateVisualState — удивительно мощный атрибут, определяющий визуальные состояния, в которых может существовать объект. Когда визуальное состояние становится активным, Silverlight обновляет свойства визуальных элементов, принадлежащих классу, в соответствии с определением (например, изменяет видимость дочерних элементов управления Button).
Чтобы задать текущее визуальное состояние, используйте статический метод GoToState класса VisualStateManager (встроенный в Silverlight тип). Свойство GroupName класса TemplateVisualState группирует сходные состояния, а свойство Name того же класса указывает индивидуальное состояние.
Вернемся в Expression Blend. В шаблоне myPlayer щелкните надпись «myPlayer» прямо над окном дизайнера, а затем выберите Edit Template | Edit Current. Щелкните вкладку States и прокрутите SuggestionStates вниз, как показано на рис. 9.
Рис. 9 Визуальные состояния для SuggestionStates
Два SuggestionStates, созданные атрибутами, показываются как Hide и Show. Если щелкнуть Hide, слева появится красный кружок, указывающий, что Expression Blend записывает любые изменения свойств, внесенные в дизайнере. Expression Blend будет записывать эти изменения, пока вы повторно не щелкнете Hide, после чего красный кружок — индикатор записи исчезнет.
Пока Expression Blend активно ведет запись для визуального состояния Hide, установите для кнопок значение Collapsed. Выберите все три кнопки в окне Objects and Timeline и укажите Collapsed как значение для их Visibility на вкладке Properties. Остановите запись для визуального состояния Hide повторным щелчком кнопки Hide. Теперь щелкните Show, чтобы слева от визуального состояния Show появился красный кружок. На этот раз записывайте Visible как состояние видимости, щелкнув кнопку Advanced Property Options справа от раскрывающегося списка Visibility и выбрав Record Current Value. Сохраните все открытые документы и вновь вернитесь в Visual Studio.
Встроенный в Silverlight класс VisualStateManager предназначен для явного указания текущего активного визуального состояния. Из метода OnApplyTemplate проигрывателя установите Hide как текущее активное визуальное состояние:
VisualStateManager.GoToState(this, "Hide", true);
В suggestion_FetchCompleted задайте Show как текущее активное визуальное состояние, чтобы вновь отобразить кнопки по окончании видеопотока и завершении скачивания данных Suggestion:
VisualStateManager.GoToState(this, "Show", true);
Чтобы скрыть кнопки при щелчке любой из них (или при повторном воспроизведении исходного потока), создайте новый обработчик для события MediaOpened элемента MediaElement и установите визуальное состояние в Hide.
Запустите проигрыватель в режиме отладки в последний раз. Вы увидите, что кнопки невидимы до самого конца воспроизведения видеоролика, и что в этой точке они становятся видимыми. Щелкните кнопку для перехода проигрывателя по любому URL, указанному в соответствующем Suggestion этой кнопки.
Пространство, отведенное проекту SMF на Codeplex, обеспечивает вам доступ к кодовой базе, документации, дискуссиям и трекеру проблем. Просмотрите все эти ресурсы и, если можете, внесите свой вклад. Чем больше творчески мыслящих людей будут участвовать в этом проекте, тем лучше для каждого из нас.