Автор: Kristoffer Vinther
Перевод: Александра Подсекина
Оригинал статьи
Managed DirectShow
Цель проекта Managed DirectShow - обеспечить Вас всем необходимым для разработки медиа приложения и серверного программного обеспечения, используя .NET. Замысел проекта заключается не столько в предоставлении полностью функционального медиа сервера и системы приложений, сколько в предоставлении базовых строительных блоков, которые позволят реализовать подобную систему как можно более простыми способами.
С чего все началось
У меня была мечта. Мечта о медиа сервере с набором основных функций. Я хотел, чтобы он мог показывать телепередачи, останавливать и перематывать передачи в прямом эфире, записывать телепередачи, предоставлять программу передач, планировать запись по программе передач (и чтобы он был способен начать запланированную запись, даже если сервер остановлен), и я хотел смотреть телепередачи и записи эфира через устройство, которое бы соединялось по беспроводной связи с сервером. И мне совсем не хотелось тратить на это целое состояние. Я купил "железо" и загрузил демо версии программного обеспечения различных медиа центров. По началу они произвели на меня положительное впечатление, тем не менее, быстро стало ясно, что всем коммерческим продуктам недостает, как минимум одной из базовых функций и почти у всех было довольно отвратительное лицензирование. SnapStreamBeyondTV обладает отличным пользовательским интерфейсом, но у него нет поддержки сети, я не мог использовать его, чтобы запустить сервер для записи, функция рекомпрессии чаще практически не работала, не говоря о целом букете незначительных недочетов. SageTV поддерживала подключение к сети, но при это обладала плохим пользовательским интерфейсом и отсутствием целого ряда других характеристик, кроме того она основана на Java! ;-( Я не разу не пробовал работать с Microsoft Windows Media Center Edition (не было демо версии, она была доступна, фактически, только изготовителям комплектного оборудования). Ни один из этих продуктов не является бесплатным (некоторые - совсем недорогие, но лицензирование требует, например, раскошеливаться на новое устройство каждый раз, как Вы собираетесь посмотреть TV) и ни один не предоставляет программу передач (я живу не в Северной Америке).
Кое-как, тропинка - теперь уже затерянная в песках времени, привела меня к DirectShow API. Великолепный приложение в графическом редакторе в комплекте программ для разработчика SDK, позволил мне вскоре понять, что DirectShow - виртуальный фонтан функциональности (хотя и слегка глючит и только для Windows), плюс он поддерживает любое медиа "железо", сменные кодеры и декодеры, паузы, перемотку, и т.д. прямой эфир, и примеры того, как транслировать эфир в сети; практически всё, чего я хотел.
Однако, оказалось, что программный интерфейс DirectShow ужасно беспорядочный и мудреный. Чтобы сделась простейшие вещи, необходимо написать сотни строк кода. И, честно говоря, большая часть образцового кода DirectShow (оба, и тот, что представлен в SDK, и тот,что лежит в основе документации), а от модели "обработки" ошибок HRESULT hres = ...; COM мне стало как-то не по себе. Да и сам DirectShow не намного лучше. Возьмем, например, SetKey(). Даже несмотря на то, что это COM, объекное ориентирование очень плохое в API (серьезно, там 30 строк кода, когда на самом деле достаточно было бы и одной: sink.Directory = @"C:\MyDirectory";).
Если бы мне пришлось разрабатывать хоть сколечки серьезное медиа приложение с этой библиотекой, мне бы пришлось добавить "парочку" слоев к исходному программному инерфейсу DirectShow, предоставляя простой в использовании объектно-ориентированный доступ к DirectShow. Результатом попыток создания подобных слоев и стала библиотека Managed DirectShow.
Альтернативы
Я знаю, что альтернативные проекты открытого доступа, которые позволяют вам пользоваться DirectShow через управляемый код уже существуют (такие, как DirectShow.NET и directshow. net library); несмотря на это, даже не затрагивая тот факт, что они ненадежны в своей работе, не поддерживаются, они предоставляют собой лишь мишуру к интерфейсу DirectShow COM, демонстрирующую на самом деле исходный программный COM интерфейс DirectShow, что приводит к тому, что интерфейс не похож .NETовский, и ничем не упрощает неуклюжий программный интерфейс. Некоторые даже выдают HRESULT как исходный. Тьфу!
Обзор характеристик
Довольно разглагольствований. Библиотека предоставляет:
- подход основанный на использовании компонентов для построения граф-фильтров
- сохранение граф-фильтров (загрузить/сохранить графы из файла) и издание (просмотр и модификация граф-фильтров в текущее время, используя редактор графов, входящий в DirectShow SDK), а также диалоговые окна свойств графов
- компоненты, контролирующие мощность системы, включая приостановку работы системы, предупреждение приостановки работы системы, планирование таймерного включения.
- перечисление устройств
- отобранные полезные элементы пользовательского интерфейса: форма медиа, строка медиа плеера и управление ТВ навигатором.
MediaForm предоставляет удобную подборку базовых форм медиа проигрывателей, включая возможности полноэкранной работы, а так же манипуляции пуск/пауза/увеличить громкость звука/уменьшить громкость звука и т.п., которые можно выполнять при помощи современных клавиатур и пультов дистанционного управления.
Лицензия
Авторскими правами на содержание сайта http://kristoffer.vinther.name обладает Kristoffer Vinther, Copyright c 2005-2006. Все права защищены. Исходный код приводится здесь по лицензии GNU General Public License. Двоичный код приводится как есть. Нет никакой гарантии и никто не несет ответственности за какой-либо вред, который они могут повлечь за собой, прямо или косвенно. Ни какая часть двоичного кода, приводимого здесь не может быть использована в коммерческих целях без моего (Kristoffer Vinther) на то согласия.
Исходники
В последнее время, исходный код библиотеки Managed DirectShow недоступен. Он может измениться в будущем. Хотя исходники какого-либо другого специфического полезного медиа приложения также недоступны. И это в будущем, вряд ли изменится.
Что доступно, так это "сборки" Managed DirectShow. Загрузите их и прочитайте руководство, чтобы понять, как их использовать.
Ограничения
Вернемся в реальность. Конечно же, есть и определенные ограничения.
Согласно некоторым источникам, код был опробован только на карте TV- тюнера Hauppauge PVR250, на машине работающей на Windows XP SP2 и установленными .NET Framework v. 2.0 и WinDVD. Вполне возможно, что он будет работать с аппаратным ипрограмным обеспечением этой конфигурации. Я слышал, что он работает еще и с WinTV USB2. пожалуйста, сообщите мне, если Вам удалось запустить его и другими системами. Не стесняйтесь, пишите мне, если возникнут какие-либо предложения.
Нужно сделать
- поддержка DVB/ATSC
- поддержка выбора моникера устройств для Visual Studio Designer
- рекомпрессия (например, из MPEG2 в WMV9 или DivX)
- рендеринг в (Managed) Direct3D поверхности
- запись и передача по сети (не только в формате MPEG2)
- телетекст
Руководство
Установка
Чтобы установить библиотеки, просто распакуйте их туда, где затем сможете открыть и зарегистрировать файл dsunicast.ax, используюя утилиту regsvr32.exe.
Руководство по программированию
Руководство по программированию находится здесь. Начните с KvTv.DirectShow.Components пространства имен. Руководство по программированию составлено при помощи последней версии NDoc 2005 (для которой мне пришлось даже создать патч). Это делает документацию ограниченной, по крайней мере с двух сторон: во-первых - библиотеки, во-вторых - генератор патча для документа. Тем не менее, этого вполне достаточно, чтобы Вы смогли начать изучать возможности. Следующий раздел представляет собой краткое введение, что, я надеюсь, окажется для вас не менее полезным.
Быстрый старт
Как уже было ск5казано в разделе Исходники, доступных исходных кодов нет. Тем не менее, этот раздел содержит 5 кратких обучающих руководств, которых вам будет вполне достаточно, чтобы начать создавать простые медиа-приложения. Предполагается, что у вас установлен, хотя бы, Visual C# 2005 Express.
В каждом из руководств, начните с создания нового Windows приложения и вставьте ссылку на библиотеки KvTv.DirectShow и KvTv.DirectShow.Components. если у вас возникнут сомнения, какое устройство управления видеоокном использовать, попробуйте VideoWindowSink, которое выполняет функции как контрола, так и приемника потока.
Медиа Плеер (C#)
Из инструментария добавить к форме файл: FileSource, VideoWindowPullSink, и VideoMixingWindowless. Установите свойства Источника(Source) на VideoWindowPullSink как FileSource, а свойства Окна(Window) - как VideoMixingWindowless. Установите свойства DockStyle в VideoMixingWindowless как Fill.
Чтобы выбрать файл для просмотра, вставьте следующее в форму:
///<summary>
/// Показ <see cref="OpenFileDialog"/> и
/// начало отображения выбранного файла.
///</summary>
///<param name="e">Аргументы события.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Пожалуйста, выберите файл для воспроизведения";
if (dialog.ShowDialog(this) != DialogResult.Cancel)
{
this.fileSource1.FileName = dialog.FileName;
try
{
this.videoWindowPullSink1.Render();
this.videoWindowPullSink1.Run();
}
catch (KvTv.DirectShow.Components.GraphRenderException)
{
MessageBox.Show(this,
"Выбранный файл не может быть отображен." +
"Пожалуйста, убедитесь, установлены ли у Вас все кодеки "+
"и есть ли доступ на чтение к файлу.",
"Ошибка отображения файла",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
Close();
return;
}
}
else
{
Close();
}
}
Чтобы управлять воспроизведением медиа, определите свойства KeyPreview в форме как true (настоящие) и вставьте следующий код в класс форм:
privatebool playing = true;
///<summary>
/// Контролы воспроизведения, паузы, перемотки основаны на нажатии клавишь.
///</summary>
///<param name="e">Аргументы события.</param>
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
switch (e.KeyCode)
{
case Keys.OemMinus:
this.fileSource1.Rewind();
break;
case Keys.Oemplus:
this.fileSource1.FastForward();
break;
case Keys.Space:
if (playing)
{
this.fileSource1.Pause();
}
else
{
this.fileSource1.Play();
}
playing = !playing;
break;
}
}
Смотрите также: MediaForm, PlayerStrip, Duration, and Position.
Телевидение(C#)
Из панели инструментов добавьте следующее в Форму: TVTunerSource, VideoWindowPushSink (или WindowPullSink,если Ваша карта тюнера не поддерживает кодировку MPEG2) и BasicVideoWindow. Определите свойства Источеика(Source) в VideoWindowPushSink как TVTunerSource, а свойства Окна(Window) - как BasicVideoWindow. Установите свойства DockStyle в BasicVideoWindow как Fill.
Добавьте к классу формы:
///<summary>Начало отображения и настройка на 67 канал.</summary>
///<param name="e">Аргументы события.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
try
{
this.videoWindowPushSink1.Render();
this.videoWindowPushSink1.Run();
}
catch (KvTv.DirectShow.Components.GraphRenderException ex)
{
MessageBox.Show(this, "Не могу отобразить ТВ-поток: " +
ex.Message, "Ошибка отображения",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Настройка на любимый канал
this.tvTunerSource1.Tuner.AutoTune(67);
}
Для TVTunerSource необходимы устройства Захвата (Capture), Crossbar, TV Audio, and TV Video . их можно определить при помощи свойств компонента. Свойства по умолчанию предполагают null (нет) ссылок, что заставляет компонент использовать первое и лучшее устройство, которое он способен найти в каждой из категорий. Это означает, что если в Вашей системе есть лишь одно устройство каждого из видов, свойства по умолчанию будут работать хорошо. Тем не менее, если у вас много устройств, вам придется самостоятельно определять те из них, что следует использовать. Например,у меня в системе есть звуковая карта с устройством захвата, которая сбивает с толку TVTunerSource при выборе устройства захвата. Чтобы разрешить этот вопрос, я определяю свойства Захвата (Capture) для первого устройства захвата в системе методом OnLoad, как этот, например (а еще мне нравится 67-ой канал) :
///<summary>
/// Выбор устройства захвата,
/// начало отображения и настройка канала.
///</summary>
///<param name="e">Аргументя события.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Выбор первого устройства захвата
foreach (KvTv.DirectShow.Moniker moniker in
KvTv.DirectShow.Components.TVTunerSource.CaptureDevices)
{
this.tvTunerSource1.Capture = moniker;
break;
}
try
{
this.videoWindowPushSink1.Render();
this.videoWindowPushSink1.Run();
}
catch (KvTv.DirectShow.Components.GraphRenderException ex)
{
MessageBox.Show(this, "Не могу отобразить ТВ-поток: " +
ex.Message, "Ошибка отображения",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Настройка на любимый канал
this.tvTunerSource1.Tuner.AutoTune(67);
}
Смотрите также: MediaForm, PlayerStrip, Duration, and Position.
Телевидение с функцией паузы(C#)
Из панели инструментов Toolbox добавьте к форме: TVTunerSource, VideoWindowPullSink, StreamBuffer, и BasicVideoWindow. Определите свойства Source для VideoWindowPullSink как StreamBuffer, свойства Source для StreamBuffer как TVTunerSource, свойства Window как BasicVideoWindow. Определите свойства DockStyle для BasicVideoWindow как Fill.
Добавьте в форму тот же код OnLoad() как и в руководстве к Television.чтобы приостановить (pause) приостановить, вставьте следующий код в класс формы:
privatebool playing = true;
///<summary>
/// Контролы воспроизведения, паузы, перемотки основаны на нажатии клавишь.
///</summary>
///<param name="e">Аргументы события.</param>
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
switch (e.KeyCode)
{
case Keys.Space:
if (this.playing)
{
this.streamBuffer1.Pause();
}
else
{
this.streamBuffer1.Play();
}
this.playing = !this.playing;
break;
}
}
Обратите внимание на то, что Вы также сможете перематывать и ускорять буфер потока.
See also: MediaForm, PlayerStrip, Duration, and Position.
Камкодер (С#)
Из панели инструментов в форму добавьте: CameraSource, AsfRecorder, и VideoWindowSink. Определите свойства Source для VideoWindowSink как AsfRecorder, свойства Source для AsfRecorder как CameraSource. Определите свойства DockStyle в VideoWindowSink как Fill. Вставьте код, чтобы начать передачу:
///<summary>Renders and runs the graph.</summary>
///<param name="e">The event arguments.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.videoWindowSink1.Render();
this.videoWindowSink1.Run();
}
Добавление клавиши записи, необходимо установить KeyPreview в true у формы:
privatebool recording;
///<summary>
/// Управление записью клавишами.
///</summary>
///<param name="e">Аргументы события.</param>
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
switch (e.KeyCode)
{
case Keys.Space:
if (recording)
{
SaveFileDialog fileDialog = new SaveFileDialog();
if (fileDialog.ShowDialog(this) == DialogResult.OK)
{
this.asfRecorder1.Record();
recording = true;
}
}
else
{
this.asfRecorder1.StopRecording();
recording = false;
}
break;
}
}
ТВ сервер (C#)
Прежде, чем осуществить передачу media, пожалуйста, обязательно прочитайте этот замечательный блог команды Windows Network Development. Всё, что в нем освещено имеет отношение к программному обеспечению любого медиа-центра.
Чтобы создать сервер, лучше используйте Console Application, нежели Windows Application. Если вы пользуетесь Visual Studio Standard edition (или вышее), я настоятельно рекомендую начать с Windows Service Application; таким образом Вы сможете создать сервер, используя дизайнер, как в предыдущих руководствах. Ко мне это не относится, поэтому давайте сделаем это вручную. Начните с добавления ссылок и применение правильных директивов:
using System.Net;
using KvTv.DirectShow.Components;
программа запускается в три этапа:
private static void Main(string[] args)
{
try
{
Build();
Run();
Serve();
Console.Out.WriteLine("Клиент присоединен.");
}
catch (Exception ex)
{
Console.Error.WriteLine("Фатальная ошибка сервера:");
Console.Error.WriteLine(ex);
}
Console.Error.WriteLine("Нажмите Enter, чтобы выйти...");
Console.ReadLine();
}
Теперь построим граф. Нам понадобится TVTunerSource и UnicastSink:
private static TVTunerSource tv;
private static UnicastSink unicast;
private static void Build()
{
tv = new TVTunerSource();
unicast = new UnicastSink();
unicast.Source = tv;
}
Его запуск что-то напоминает. Не забудьте выбрать устройства для ТВ, если это необходимо:
private static void Run()
{
unicast.Render();
unicast.Run();
}
И наконец, сервинг потока так же прост:
privatestaticvoid Serve()
{
unicast.EndPoint = new IPEndPoint(IPAddress.Any, 40100);
unicast.Accept();
}
Смотрите также: MultiwayUnicastSink.
ТВ клиент (C#)
Для работы с клиентским приложением мы снова работаем в WindowsApplication: из панели инструментов Toolbox, добавьте UnicastSource и VideoWindowSink. Определите Source для VideoWindowSink как UnicastSource , а DockStyle как Fill. Если клиентское приложение не запусается с того же хостинга, что и сервер, определите конечную точку сервера, используя свойства ProgramStream в UnicastSource. получается, что порт по умолчанию - UnicastSource - 40100, тот порт, что мы определили на сервере. Если Вы хотите использовать другой порт, измените свойства сервера ProgramStream в UnicastSource. Теперь запустите граф, когда запустится программа и всё! Вы это сделали!:
///<summary>Отображение и запуск графа.</summary>
///<param name="e">Аргументы события.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.videoWindowSink1.Render();
this.videoWindowSink1.Run();
}
Запись TV
ТВД (функционально, так что наслаждайтесь изучением)
Рекомпрессия Медиа
ТВД
Вот и всё, что относится к разделу руководства. Надеюсь он Вам пригодится. Вот скриншот того, что можно получить, если приложить немного больше усилий (основан на местной ТВ программе):
Удачи!
[1] честно говоря, эти наблюдения были сделаны в начале 2005 года. Мир "немного" изменился с тех пор. Тем не менее, мне всё еще приходится искать новые решения вновь и вновь.

