Автор: Валентин Вовк
DSPack
Единственной известной мне дельфи-оберткой над DirectShow есть DSPack. Скачать ее можно со страницы http://www.progdigy.com или последнюю версию с этого сайта. Об оберточных классах дает представление следующая иллюстрация, взятая из файла справки по DSPack:

Рис. 13. Структура классов пакета DSPack
Конечно, без хорошего знания DirectShow API трудно оценить качество и полезность классовой обертки. Но мы все же попробуем разобраться с этими классами.
TFilterGraph
Этот компонент - обертка над менеджером графа фильтров DirectShow и занимает исключительное место как в самом DirectShow, так и в DSPack'е. Любое приложение, имеющее дело с DirectShow, обязательно будет иметь дело и менеджером графа фильтров. Он используется для построения и управления графом, а также для управления синхронизацией, уведомлениями о событиях и других аспектах контроля графа.
При использовании API DirectShow для создания менеджера графа фильтров нужно пользоваться вызовом функции CoCreateInstance, а в качестве CLSID передавать либо CLSID_FilterGraph, либо CLSID_FilterGraphNoThread. CLSID_FilterGraph отвечает за создание менеджера графа фильтров (МГФ) на совместно используемом рабочем потоке (видимо, нужно все же дать представление об использовании потоков в DirectShow ), а CLSID_FilterGraphNoThread - за создание МГФ на потоке приложения.
Обычно приложения используют CLSID_FilterGraph. Но оба CLSID_ используются для создания того же объекта, но с использованием разных потоковых моделей:
- CLSID_FilterGraph служит для создания МГФ на рабочем потоке, который совместно используется всеми экземплярами CLSID_FilterGraph в одном процессе. Поток диспетчерезирует сообщения, которые посылают фильтры и управляет жизненным циклом любых окон, созданных фильтрами.
- CLSID_FilterGraphNoThread служит для создания МГФ на потоке приложения. Если вы используете этот CLSID, то поток, вызывающий CoCreateInstance, должен иметь цикл обработки сообщений. В противном случае могут происходить разного рода блокировки (deadlock). Перед завершением работы этого потока нужно также освободить (release) МГФ и все объекты графа (такие, как фильтры, контакты, ссылочные часы и т.д.).
Исходя из вышеизложеного, вроде бы нет особых причин использовать при создании МГФ CLSID_FilterGraphNoThread. Впрочем, это можно и делать. ПРИМЕР.
Впрочем, компонент TFilterGraph не использует CLSID_FilterGraphNoThread, поэтому больше не будем о нем упоминать.
МГФ предоставляет следующие, кратко описанные, интерфейсы. Несмотря на то, что их достаточно много, я не поленюсь их все выписать, а вам советую не полениться и прочитать эту табличку. После этого вам станут намного понятнее права и обязаности менеджера графа фильтров.
| №№ | Интерфейс | Описание |
|---|---|---|
| 1 | IAMGraphStreams | Управление графом фильтров (ГФ), работающим с живым источником |
| 2 | IAMStats | Позволяет приложению возвращать исполнимые данные от МГФ (???). Фильтры могут использовать этот интерфейс для записи исполнимых данных.Это, словом, для статистики - ничего не понял |
| 3 | IBasicAudio | Позволяет приложению управлять громкостью и балансом аудиопотока. |
| 4 | IBasicVideo | Позволяет приложению устанавливать видеосвойства, такие, как прямоугольники назначения и источника. |
| 5 | IBasicVideo2 | Происходит от IBasicVideo и предоставляет дополнительный метод GetPrefferedAspectRatio. |
| 6 | IFilterChain | Предоставляет методы для старта, остановки или удаления цепочки фильтров в графе фильтров. |
| 7 | IFilterGraph | Предоставляет методы для построения графа фильтров. Приложения используют его для добавления фильтра в граф, соединения и рассоединения фильтров, удаления фильтров и других базисных операций. Однако, интерфейс IGraphBuilder происходит от этого интерфейса и предоставляет дополнительные методы, которые есть более sophistical. Таким образом, приложению удобнее пользоваться интерфейсом IGraphBuilder, а не IFilterGraph. |
| 8 | IFilterGraph2 | Расширяет интерфейсы IFilterGraph и IGraphBuilder. |
| 9 | IFilterMapper2 | Регистрирует и дерегистрирует фильтры, а также локализирует их в регистре. Реализует этот интерфейс вспомогательный объект Filter Mapper. |
| 10 | IGraphBuilder | Этот интерфейс предоставляет методы, пзволяющие приложению строить граф фильтров. Этот интерфейс реализует МГФ.Этот интерфейс наследуется от IFilterGraph (который предоставляет базовые операции - такие, как добавление фильтра в граф или соединение двух контактов) , и добавляет методы, позволяющие создавать граф по частичной информации. |
| 11 | IGraphConfig | Используется МГФ для поддержки динамического построения графа. Этот интерфейс позволяет приложениям и фильтрам переконфигурировать граф, находящийся в запущенном состоянии, без его остановки, и без потери данных. |
| 12 | IGraphVersion | Для получения версии МГФ. |
| 13 | IMediaControl | Предоставляет методы для управления потоком данных, проходящих через граф фильтров. Включает методы для запуска, приостановки и остановки графа. |
| 14 | IMediaEvent | Содержит методы для получения уведомлений о событиях и переопределения заданных по умолчанию обработчиков событий МГФ. |
| 15 | IMediaEventEx | Наследует и расширяет интерфейс IMediaEvent, позволяя окну приложения получать уведомления о происходящих событиях в МГФ. |
| 16 | IMediaEventSink | Уведомляет менеджер графа фильтров о событиях, происходящих на графе. Фильтры используют этот интерфейс для отчета о событиях. Приложения не используют его. |
| 17 | IMediaFilter | Управляет потоковым состоянием фильтра. Все фильтры DirectShow реализуют этот интерфейс. Он предоставляет методы переключения состояний (остановка, пауза, запуск), для получения текущего состояния и для установки ссылочных часов. Приложения не вызывают методов IMediaFilter. МГФ также предоставляет этот интерфейс. Приложения могут вызывать методы этого интерфейса SetSyncSource и GetSyncSource для установки и получения ссылочных часов. Приложения не должны вызывать других методов этого интерфейса, а вместо этого использовать методы IMediaControl.Сам IMediaFilter наследует от IPersist, а интерфейс IBaseFilter наследует, в свою очередь, от IMediaFilter. |
| 18 | IMediaPosition | Содержит методы для поиска позиции в потоке. Интерфейс IMediaSeeking основывается на этом интерфейсе. Приложения, написанные на C/C++, могут использовать интеерфейс IMediaSeeking вместо IMediaPosition. Но IMediaSeeking несовместим с автоматизацией, поэтому приложения, написанные, например, на Visual Basic'е, должны использовать IMediaPosition.Этот интерфейс предоставляется как МГФ, так и отдельными фильтрами. Приложения должны получать указатель на интерфейс IMediaPosition от МГФ, а не от фильтров. МГФ распределяет метод путем вызова всех фильтров рендеринга. Фльтры рендеринга распространяют вызов вверх по потоку к фильтрам источников. Такая последовательность событий гарантирует синхронизацию всех потоков.Если один из распеделенных вызовов вернет ошибку, МГФ вернет первую полученную им ошибку. Некоторые из распределенных вызовов могут быть и успешными. Если, впрочем, хоть один з распределеных вызовов вернет не E_NOTIMPL, то и МГФ не вернет E_NOTIMPL. Только в том случае, если все распределенные вызовы вернут E_NOTIMPL, МГФ вернет E_NOTIMPL.Замечание для разработчиков фильтров. Не нужно реализовывать этот интерфейс. Вместо этого нужно реализовывать IMediaSeeking. Если ваш фильтр поддерживает IMediaSeeking, МГФ автоматически будет управлять интерфейсом IMediaPosition. |
| 19 | IQueueCommand | Ставит команду в очередь для ее обработки в определенное время. Приложение может использовать его для продвижения вперед управляющих команд для графа(?).Методы этого интерфейса моделируют метод IDispatch::InvokeAt. Приложение указывает интерфейс, метод интерфейса, параметры метода и ссылочное время. МГФ ставит эту информацию в очередь и, затем, вызывает этот метод в указанное время. Требуемй интерфейс должен наследовать IDispatch и должен предоставляться МГФ. Примерами таких интерфейсов есть IMediaControl, IMediaEventEx и IMediaPosition.После постановки команды в очередь МГФ возвращает указатель на интерфейс IDefferedCommand. Приложение может использовать этот интерфейс для отмены или модифкации команды. |
| 20 | IRegisterServiceProvider | Регистрирует объект как сервис. |
| 21 | IResourceManager | Этот интерфейс отвечает за разрешение конкуренции за системные ресурсы.Фильтры могут использовать этот интерфейс для запроса ресурсов, которые, возможно, используют другие объекты. Например, аудио-рендереры (фильтры, воспроизводящие аудио) использую этот интерфейс для разрешения конфликтов за устройство аудио-вывода.Приложения, обычно, не используют этот интерфейс. |
| 22 | IServiceProvider | Этот интерфейс в справке по DirectShow не описан. Прочитать о нем можно в MSDN в разделе () следующее: |
| 23 | IVideoFrameStep | Этот интерфейс делает шаги через видеопоток. Этот интерфейс позволяет приложениям, использующим DirectShow, включая проигрыватели DVD, делать шаги по видеопотоку. МГФ управляет этим шаговым процессом в связке с фильтром оверлейного микширования или фильтром видеоотображения. Прокрутка назад не поддерживается. |
| 24 | IVideoWindow | Устанавливает свойства окна видеотображения. Приложения могут использовать этот интерфейс для установки владельца, позиции и размера окна и других его свойств. |
Как видите, интерфейсов достаточно много. По этой причине более подробно мы их рассматривать будем только в случае необходимости.
Нам нужны будут еще два интерфейса: IAMGraphBuilderCallback и IAMFilterGraphCallback. Первый из них предоставляет механизм callback'а при построении графа. Для его использования нужно реализовать его методы в приложении или клиентском объекте. Запрашивайте у МГФ интерфейс IObjectWithSize и вызывайте метод IObjectWithSize::SetSize, передавая указатель на реализацию этого интерфейса (ЧТО ЗА ИНТЕРФЕЙС ТАКОЙ? - НЕ НАШЕЛ ОПИСАНИЯ). МГФ вызывает методы этого интерфейса при построении графа, который (интерфейс) дает клиент в целях модификации процесса построения графа. Главное использование этого интерфейса - конфигурирование фильтра VMR перед его соединением. Его можно использовать также для отказа использования неких фильтров (например, декодеров). Специфичекие методы этого интерфейса - SelectedFilter (вызывается, когда МГФ находит фильтр-кандидат, но перед созданием фильтра) и CreatedFilter (вызывается после того, как МГФ создает фильтр, но до попытки его соединить). Второй интерфейс - IAFilterGraphCallback, - тоже предоставляет callback-механизм при построении графа. Если при построении графа МГФ получает ошибку при попытке рендеринга некоторого контакта, он вызывает единственный метод этого интерфейса UnableToRender.
Так вот, класс TFilterGraph есть наследником TComponent, а также интерфейсов IAMGraphBuilderCallback, IAMFilterGraphCallback и IServiceProvider (об этом интерфейсе тоже нужно что-то сказать):
TFilterGraph = class(TComponent, IAMGraphBuilderCallback, IAMFilterGraphCallback, IServiceProvider)
МГФ может работать в трех режимах - gmNormal, gmCapture и gmDVD. При использовании режима gmNormal создается COM-объект IGraphBuilder (FFilterGraph), при использовании gmCapture - создаются COM-объекты ICaptureGraphBuilder2 (FCaptureGraph) и IGraphBuilder (FFilterGraph), при использовании gmDVD - COM-объект IDvdGraphBuilder (FDvdGraph). Давайте сначала рассмотрим эти интерфейсы, а затем продолжим исследование класса TFilterGraph.
Как уже было сказано (см. таблицу интерфейсов, предоставляемых МГФ), IGraphBuilder наследуется от IFilterGraph (который предоставляет базовые операции - такие, как добавление фильтра в граф или соединение двух контактов) , и добавляет методы, позволяющие создавать граф по частичной информации (единственный метод - Render - позволяет автоматически достроить граф для указанного исходящего контакта).
COM-объект "Построитель графа захвата" (Capture Graph Builder) реализует единственный интерфейс - ICaptureGraphBuilder2 (для меня так и осталось невыясненным происхождение этого названия - возможно, он заменил и расширил используемый ранее для тех же целей интерфейс ICaptureGraphBuilder), который предоставляет методы для построения графа захвата и других пользовательских графов фильтров. В общем, использование этого вспомагательного объекта имеет свои особенности, но я не считаю, что на них нужно останавливаться (неплохо бы разобраться в том, как это helper-object работает и можно ли обойтись и без него).
Наконец, IDvdGraphBuilder используется для работы с, очевидно, dvd, но это тема отдельного разговора, и я постараюсь в дальнейшем о dvd не упоминать вообще.
Методы класса TFilterGraph позволяют обращаться к методам перечисленных в таблице интерфейсов. Причем эти методы в классе предоставляются очень избирательно, вроде SetVolume (для IBasicAudio), SetState, Play, Run и т.п. (для IMediaControl) или SetRate (для IMediaSeeking). Само собой, внутри этих методов идет обращение к соответствующим методам соответствующих интерфейсов. Поэтому возникает вопрос, стоило ли вообще выписывать эти методы в противовес невыписанным, которых подавляющее большинство. Как по мне, так не стоило. Ну да ладно. По крайней мере у нас есть повод рассматривать не DirectShow API, а дельфийскую библиотеку. TFilterGraph содержит также методы для добавления фильтров в граф и очистки его от фильтров. Но не предоставляет методов для их, фильтров, соединения (IGraphBuilder::Connect) и пр. и пр. Что, во-первых, странно (поскольку требовало от разработчиков минимальных усилий), а, во-вторых, настолько сбивает с толку начинающих (потому как даже не намекает им о том, что такое вообще возможно), что на многих форумах я неоднократно пересекался с людьми, которые достаточно долго (по несколько месяцев) имели дело с программами, работающими с видео (и писали их сами!), но не подозревали даже о фильтрах и о хоть каких бы то ни было принципах работы с ними. Что же, кроме справки по дельфи, нужно иногда читать и MSDN.
Подавляющее большинство прочих методов и свойств класса TFilterGraph имеют отношение к обработке сообщений, получаемых графом фильтров. Рассмотрим подробнее, как это происходит (хотя бы для того, чтобы уметь самим это делать в случае использования не DSPack, а непосредственно DirectShow API). Итак, в private-секции класса TFilterGraph можно найти интерфейс IMediaEventEx (член FMediaEventEx). При активизации менеджера графа фильтров (в реализации метода TFilterGraph.SetActive) происходит вызов метода QueryInterface для получения интерфейса IMediaEventEx. Сейчас самое место более подробно коснуться этого интерфейса, но начнем, пожалуй, с интерфейса IMediaEvent (IMediaEventEx наследует и расширяет его функциональность). Итак, интерфейс IMediaEvent содержит методы для возвращения уведомлений о событиях и для переопределения обработчиков этих уведомлений, предоставляемых по умолчанию МГФ. Как уже было сказано, этот интерфейс предоставляется МГФ. Приложение может использовать его для реакции на события, происходящие в графе фильтров, такие, как конец потока или ошибка при отображении (рендеринге). Фильтры посылают события в граф фильтров, используя интерфейс IMediaEventSink. Более подробно о событиях, возникающих в графе фильтров, стоит посмотреть в соответствующем разделе справки: ... . Интерфейс IMediaEvent предоставляет следующие методы:
| Метод | Описание |
|---|---|
| CancelDefaultHandling | Отменяет действие по умолчанию, выполняемое МГФ, для указанного сообщения |
| RestoreDefaultHandling | Восстанавливает поведение, заданное МГФ по умолчанию, для указанного события |
| FreeEventParams | Освобождает ресурсы, связанные с параметрами события. Этот метод следует вызывать после вызова метода GetEvent (см. ниже). |
| GetEvent | Возвращает уведомление о следующем событии из очереди сообщений. |
| GetEventHandle | Возвращает обработчик для события со сбросом вручную (manual-reset event) (если вы забыли или вообще не знаете, что это такое, почитайте в MSDN или посмотрите у Рихтера), который остается занятым (signaled), пока очередь содержит уведомления о событиях. УЯСНИТЬ СМЫСЛ, ПРОВЕРИТЬ ПЕРЕВОД.МГФ удерживает событие о сбросом вручную, которое отражает состояние очереди сообщений. Если очередь содержит уведомления о событиях, событие со сбросом вручную пребывает в занятом состоянии. Если очередь пуста, метод IMediaEvent::GetEvent сбрасывает событие.Приложение может использовать это событие для определения состояния очереди. Сначала вызывается метод GetEventHandle для получения хэндла события. Затем нужно дождаться, когда событие перейдет в сигнальное состояние, с помощью функции вроде WaitForSingleObject. Затем нужно получить седующее уведомление о событии из очереди с помощью вызова метода IMediaEvent::GetEvent. МГФ удерживает событие в сигнальном состоянии, пока очередь пуста; затем событие сбрасывается. Не следует закрывать хэндл события, возвращаемого этим методом, т.к. он используется внутри графа фильтров. Не нужно также использовать хэндл после после освобождения МГФ, поскольку в этом случае этот хэндл не будет корректным. (Чтобы избежать этой ошибки, можно дублировать хэндл вызовом DuplicateHandle и использовать этот дубликат вместо исходного хэндла. После окончания работы с дубликатом его нужно закрыть.)Другой способ мониторинга приложением очереди сообщений - вызов метода IMediaEventEx::SetNotifyWindow (он будет рассмотрен ниже). |
| WaitForCompletion | Ждет, когда граф фильтров обработает все доступные данные. |
Для мониторинга сообщений нас, впрочем, больше будет интересовать интерфейс IMediaEventEx, который наследует интерфейс IMediaEvent и, кроме того, имеет методы для для возвращения уведомлений о событиях и для переопределения обработчиков сообщений, предоставляемых МГФ по умолчанию. IMediaEventEx расширяет интерфейс IMediaEvent методами, позволяющими окну приложения получат сообщения о происходящих событиях в МГФ. Полный список определенных системой сообщений можно посмотреть здесь. Мы же обратимся к новым методам IMediaEventEx. Их всего три:
| Метод | Описание |
|---|---|
| SetNotifyWindow | Регистрирует окно, в которое будут посылаться уведомления о событиях. |
| SetNotifyFlags | Разрешает или запрещает уведомлять о событиях |
| GetNotifyFlags | Позволяет определить, разрешено ли уведомлять о событиях |
Как видим, смысл методов достаточно прозрачен, а чтобы еще сильнее понять их предназначение, смотрим в MSDN здесь.
Вернемся теперь к вопросу о реализации в классе TFilterGraph возможности обработки сообщений. Итак, в методе SetActive получаем интерфейс IMediaEventEx, разрешаем мониторинг событий (IMediaEventEx::SetNotifyFlags(0)) и устанавливаем окно, в которое МГФ будет посылать уведомления о происходящих событиях (IMediaEventEx::SetNotifyWindow(хэндл нашего окна, WM_GRAPHNOTIFY (некая заранее объявленная константа), ...)). Оконная процедура имеет следующий вид:
procedure TFilterGraph.WndProc(var Msg: TMessage); |
А вот реализация метода HandleEvents:
procedure TFilterGraph.HandleEvents; // соответствующий обработчик |
Рекомендую посмотреть в инспекторе объектов на обработчики событий, а я буду считать, что успешно закончил рассмотрение этого класса.
TFilter
Следующий интересующий нас класс - TFilter. Не нужно быть особо догадливым, чтобы понять, что он есть оболочкой над интерфейсом, представляющим фильтр - IBaseFilter. Этот интерфейс предоставляет методы для управления фильтром. Его реализуют все фильтры DirectShow. МГФ использует этот интерфейс для управления фильтрами. Приложения же могут его использовать для перечисления контактов или для запроса информации о фильтре, но не для изменения состояния фильтра (каждый фильтр может находиться в одном из трех состояний: запущенном, остановленном или приостановленном (пауза)). Для этой цели нужно использовать интерфейс IMediaControl, предоставляемый МГФ. Поскольку этот интерфейс уже неоднократно упоминался, займемся им более плотно. Он предоставляет методы для управления потоком данных через граф фильтров. Вдобавок к методам, наследуемым от IDispatch, интерфейс IMediaControl предоставляет следующие:
| Метод | Описание |
|---|---|
| Run | Запускает все фильтры в графе фильтров |
| Pause | Приостанавливает все фильтры в графе фильтров |
| Stop | Останавливает все фильтры в графе фильтров |
| StopWhenReady | Приостанавливает граф фильтров, позволяет фильтрам поставить приходящие им данные в свои очереди, а затем останавливает граф фильтров |
| GetState | Возвращает текущее состояние графа фильтров |
В особые объянения этих методов я входить не буду, считая их интуитивно понятными, хотя на самом деле поговорить есть о чем - исследование деталей изменения состояний отдельных фильтров и всего графа фильтров - это отдельный и интересный разговор.
Сделав это отступление, вернемся к нашему интерфейсу IBaseFilter. Но, поскольку он наследует другой интерфейс - IMediaFilter, рассмотрим сначала методы последнего (ТУТ БЫ (ИЛИ ДАЛЬШЕ) - КАРТИНКУ С ИЛЛЮСТАЦИЕЙ НАСЛЕДОВАНИЯ ИНТЕРФЕЙСОВ). Именно он предоставляет методы для управления потоковым состоянием фильтра. Как уже было сказано, его предоставляют все фильтры DirectShow (поскольку они предоставляют интерфейс IBaseFilter, наследующий IMediaFilter; нужно, кстати, сказать, что сам IMediaFilter наследует от интерфейса IPersist). Итак, интерфейс IMediaFilter предоставляет методы для изменения состояния фильтра (остановка, запуск, пауза(приостановка)); для возвращения текущего состояния фильтра; для установки ссылочных часов. Приложения же, однако, не должны использовать интерфейса IMediaFilter, предоставляемых фильтрами. Вместо этого они могут использовать только тот же интерфейс - IMediaFilter, предоставляемый МГФ. Впрочем, они могут использовать только два метода этого интерфейса - SetSyncSource (для установки ссылочных часов для графа) и GetSyncSource (для получения этих часов). Остальные методы они вызывать не должны. Вместо этого рекомендуется использовать соответствующие методы интерфейса IMediaControl. Несмотря на это, для справки приведу таблицу методов IMediaFilter (кроме тех, что наследуются от IPersist):
| Метод | Описание |
|---|---|
| Stop | Останавливает фильтр |
| Pause | Приостанавливает фильтр |
| Run | Запускает фильтр |
| GetState | Возвращает состояие фильтра (запущен, остановлен, приостановлен) |
| SetSyncSource | Устанавливает ссылочные часы для фильтра или графа фильтров |
| GetSyncSource | Возвращает текущие ссылочные часы |
Теперь пришел черед вернуться к методам интерфейса IBaseFilter. Кроме методов IMediaFilter, он предоставляет еще и следующие:
| Метод | Описание |
|---|---|
| EnumPins | Перечисляет контакты фильтра |
| FindPin | Возвращает контакт с указанным идентификатором |
| JoinFilterGraph | Уведомляет фильтр, что он связан или left(?) графом фильтров.Когда МГФ добавляет фильтр в граф фильтров, он вызывает этот метод с указателем на себя. Имя экземпляра фильтра связывается посредством второго параметра. Когда МГФ удаляет фильтр из графа, он вызывает этот метод, передавая в качестве указателя на граф значение nil. Приложениям этот метод вызывать не следует. Для добавления фильтра в граф нужно вызвать метод AddFilter интерфейса IFilterGraph, полученного у МГФ |
| QueryFilterInfo | Возвращает информацию о фильтре |
| QueryVendorInfo | Возвращает строку, содержащую информацию о производителе |
Это, в общем, и все, что я хотел сказать об интерфейсе IBaseFilter и, соответственно, классе TFilter, поскольку последний реализует простейшие и очевиднейшие вещи, необходимые для того, чтобы называться враппером этого интерфейса. Если вас заинтересуют подробности упомянутых выше интерфейсов или их методов, смотрите их в MSDN.
TVideoWindow
Мы же продолжим знакомство с классами-обертками над COM-объектами DirectShow. Следующим будет TVideoWindow - компонент, используемый для отображения (рендеринга - rendering) проходящих через граф фильтров данных. Он есть оберточным классом над, в первую очередь, интерфейсом IVideoWindow и, во вторую (в зависимости от режима отображения), над IVMRWindowlessControl9. О последнем интерфейсе поговорим чуть позже, а сейчас займемся IVideoWindow, - понимание работы с ним и задач, им выполняемых, чрезвычайно важно, в первую очередь, для понимания множества нюансов собственно видеовывода в DirectShow.
IVideoWindow
Итак, интерфейс IVideoWindow предназначен для работы со свойствами окна видеовывода. Приложения могут использовать его для установки владельца окна, его положения и размеров и других свойств. Его (этот интерфейс, IVideoWindow) предоставляют как фильтр видеорендеринга, так и МГФ. Приложения должны использовать версию МГФ этого интерфейса. МГФ перенаправляет вызовы всех методов видеорендереру. Пересылает он, также, и отдельные оконные сообщения, такие, как WM_DISPLAYCHANGE, которые нужны видеорендереру для самообновления. Если поместить окно видеовывода на дочернем окне, то оно уже не будет напрямую получать оконные сообщения. Но их будет пересылать МГФ.
Однако, если граф фильтров содержит более одного видеорендерера, МГФ взаимодействует только с одним из них (указанным отдельно). Таким образом, работая с несколькими видеоокнами, приложение должно использовать интерфейс IVideoWindow на соответствующем фильтре напрямую. В этом случае нужно пересылать оконные сообщения каждому видеорендереру, используя метод IVideoWindow::NotifyOwnerMessage.
Приложения, которые устанавливают видеоокна в дочерние окна, должны устанавливать пустым обработчик сообщения WM_ERASEBKGND, чтобы избежать неверного отображения содержания окна.
Если видеорендерер не соединен с другими фильтрами, все методы на этом его интерфейсе возвращают код ошибки VFW_E_NOT_CONNECTED. Множество свойств видеорендерера постоянно между моментами успешного соединения и отсоединения. Поскольку этот интерфейс совместим с автоматизацией, булевы значения могут быть OAFALSE(0)или OATRUE(-1).
Кроме методов, наследуемых от IDispatch, интерфейс IVideoWindow предоставляет методы, описанные в далееидущей табличке:
| Метод | Описание |
|---|---|
| get_AutoShow | Всегда ли видеорендерер автоматически показывает видеоокно, когда он получает видеоданные |
| get_BackgroundPalette | Всегда ли видеоокно освобождает свою палитру в бэкграунде |
| get_BorderColor | Возвращает цвет, который появляется около краев прямоугольника назначения |
| get_Caption | Возвращает надпись видеоокна |
| get_FullScreenMode | Всегда ли видеорендерер работает в полнооконном режиме |
| get_Height | Возвращает высоту видеоокна |
| get_Left | Возвращает x-коордианту окна |
| get_MessageDrain | Возвращает окно, которое получает сообщения от мышки и клавиатуры |
| get_Owner | Возвращает родительское окно видеоокна |
| get_Top | Возвращает y-координату окна |
| get_Visible | Видимо ли видеоокно |
| get_Width | Возвращает ширину видеоокна |
| get_WindowState | Запрашивает состояние видеоокна (видимое, скрытое, минимизированное, максимизированное) |
| get_WindowStyle | Возвращает стиль видеоокна |
| get_WindowStyleEx | Возвращает расширенный стиль видеоокна |
| GetMaxIdealImageSize | Возвращает максимальный идеальный размер изображения для видеоокна |
| GetMinIdealImageSize | Возвращает минимальный идеальный размер изображения для видеоокна |
| GetRestorePosition | Возвращает положение восстановленного видеоокна (есть смысл вызывать, если оно находится в минимизированном или максимизированном состоянии) |
| GetWindowPosition | Возвращает позицию видеоокна |
| HideCursor | Показывает или скрывает курсор, когда мышка находится над окном |
| IsCursorHidden | Скрыт ли курсор |
| NotifyOwnerMessage | Пересылает сообщения видеоокну. МГФ вызывает этот метод для пересылки различных сообщений рендереру |
| put_AutoShow | Устанавливает, должен ли видеорендерер автоматически показывать видеоокно при получении видеоданных (по умолчанию, при изменении графом фильтров состояния с приостановленного к запущенному, видеорендерер показывает видеоокно и устанавливает его на переднем плане; если пользователь зарывает окно, оно не будет автоматически открыто снова) |
| put_BackgroundPalette | Устанавливает, должно ли видеоокно освобождать свою палитру в бэкграунде. Если должно и видеоизображение требует палитру, видеорендерер должен освободить палитру в бэкграунде. Все цвета, используемые палитрой, должны быть заменены их близким соответствием в палитре дисплея для рисования. Это гарантирует то, приложение не нарушит палитру. Но это скажется на быстродействии |
| put_BorderColor | Устанавливает цвет, который появляется около краев прямоугольника назначения (эта операция имеет смысл, если прямоугольник, в котором происходит отображение видео, меньше клиенского простанства видеоокна) |
| put_Caption | Устанавливает надпись видеоокна |
| put_FullScreenMode | Устанавливает или отменяет полноэкранный режим |
| put_Height | Устанавливает высоту видеоокна |
| put_Left | Устанавливает x-координату видеоокна |
| put_MessageDrain | Устанавливает окно, которое должно получать сообщения от клавиатуры и мыши от видеоокна |
| put_Owner | Устанавливает родительское окно для видеоокна |
| put_Top | Устанавливает y-координату видеоокна |
| put_Visible | Показывает или скрывает видеоокно |
| put_Width | Устанавливает ширину видеоокна |
| put_WindowState | Показывает, скрывает, минимизирует или максимизирует видеоокно |
| put_WindowStyle | Устанавливает оконный стиль для видеоокна |
| put_WindowStyleEx | Устанавливает расширенный оконный стиль для видеоокна |
| SetWindowForeground | Устанавливает положение видеоокна на самом верху Z-порядка |
| SetWindowPosition | Устанавливает положение видеоокна |
Как видим, интерфейс IVideoWindow имеет достаточно много методов (об их подробностях можно узнать из того же MSDN'а), большинство из которых имеет совершенно прозрачный смысл, и только для некоторых (как, например, put_FullScreenMode) характерны важные нюансы.
VMR Windowless Mode
Здесь - VMR Windowless Mode
IVMR WindowlessControl9
Теперь обратимся к интерфейсу IVMRWindowlessControl9. Я не хотел пока заострять на нем внимания,- нам вполне хватило бы и IVideoWindow, но упомянуть все же нужно. Этот интерфейс управляет отображением фильтра рендеринга VMR-9 (Video Mixing Renderer Filter 9; почему он так называется и что оно все такое - сейчас не важно) видеопотока без окна-контейнера. Перед использованием методов этого интерфейса приложения должны установить VMR-9 в безоконный (windowless) режим.
IVMRWindowlessControl9 кроме методов, наследуемых от IUnknown, предоставляет также и следующие:
| Метод | Описание |
|---|---|
| DisplayModeChange | Информирует VMR о том, что приложение получило сообщение WM_DISPLAYCHANGE.Приложение должно вызывать этот метод всегда, когда оно получает оконное сообщение WM_DISPLAYCHANGE, но только если VMR находится в безоконном (windowless) режиме |
| GetAspectRatioMode | Возвращает текущий aspect ratio режима отображения |
| GetBorderColor | Возвращает текущий цвет рамки, используемый VMR'ом |
| GetCurrentImage | Возвращает копию текущего изображения, показываемого VMR'ом. Изображение возвращается в формате пакованного Windows DIB. Этот метод может быть вызван в любое время, независимо от того, в каком состоянии находится фильтр. Вызывающий ответственен за освобождение возвращаемой памяти с помощью вызова coTaskMemFree. Использование этой функции замедляет воспроизведение |
| GetMaxIdealVideoSize | Возвращает максимальный размер видео, который может быть отображен VMR'ом без incurring significant performance или ухудшения качества изображения |
| GetMinIdealVideoSize | Возвращает минимальный размер видео, который может быть отображен VMR'ом без incurring significant performance или ухудшения качества изображения |
| GetNativeVideoSize | Возвращает un-stretched размер видео и aspect ratioвидео |
| GetVideoPosition | Возвращает текущие прямоугольники источника и назначения, используемые для отображения видео |
| RepaintVideo | Перерисовывает текущий видеокадр |
| SetAspectRatioMode | Устанавливает текущий aspect ratio режима отображения |
| SetBorderColor | Устанавливает цвет рамки для использования VMR'ом.Этот цвет используется для заливки любой площади прямоугольника назначения, не содержащего видео. Обычно используется в следующих двух случаях:Когда видео straddles два монитораWhen the VMR is trying to maintain the aspect ratio of the movies by letter-boxing the video to fit within the specified destination rectangle. See SetAspectRatioMode. |
| SetVideoClippingWindow | Указывает содержащему окну, что видео должно быть clipped to. |
| SetVideoPosition | Устанавливает для видео прямоугольники источника и назначения. |
Итак, класс TVideoWindow может пребывать в двух режимах - vmNormal и vmVMR, в зависимости от чего (в методе NotifyFilter) создает либо Video Renderer Filter, либо Video Mixing Renderer Filter 9 - фильтры для видеоотображения - и присваивает созданный фильтр рендеринга внутренней переменной FBaseFilter; имеет также внутреннюю переменную, представляющую менеджер графа фильтров. Естественно, что прочие поля и методы TVideoWindow предназначены для вызова методов интерфейсов IVideoWindow или IVMRWindowlessControl9 и фильтров VMR или VMR9. Поэтому сначала совсем коротко рассмотрим фильтры рендеринга, а затем - прочие свойства и методы TVideoWindow.
Краткий экскурс в фильтры видеоотображения
Video Renderer Filter
Этот фильтр - подходящий вариант для надежного видеовоспроизведения.
Нужно заметить, что в Windows XP фильтром видеовоспроизведения по умолчанию есть Video Mixing Renderer (VMR). И VMP, и Video Renderer имеют общее "дружественное имя" - "Video Renderer". На всех других платформах по умолчанию фильтром видеовоспроизведения есть Video Renderer, хотя приложения и могут использовать VMR-9 для использования дополнительных возможностей видеовоспроизведения.
Video Renderer использует DirectDraw и оверлейные поверхности, если их поддерживает видеокарта. Менеджер графа фильтров предоставляет интерфейс IVideoWindow, который позволяет приложению устанавливать и возвращать свойства Video Renderer'а. На новых видеокартах Video Renderer поддерживает полноэкранный режим отображения. В других случаях МГФ автоматически переключается на фильтр полноэкранного отображателя ( Full Screen Renderer ) для полноэкранного режима. См. также IVideoWindow::put_FullScreenMode.
Нужно заметить, что обычно фильтры таких видеоокон обрабатывают сообщения на рабочем потоке, созданном МГФ. Однако, если приложение напрямую создает фильтр с использованием вызова CoCreateInstance, видеокно обрабатывает сообщение на потоке приложения. В этом случае поток приложения должен иметь цикл обработки для диспетчеризации сообщений, предназначенных видеоокну. Этот поток не должен прекращать выполнение до тех пор, пока Video Renderer не вызовет финальный Release, который происходит в том случае, когда прекращает работу МГФ. В противном случае приложение может получить взаимоблокировку.
В следующей таблице описываются свойства фильтра Video Renderer:
| Свойство | Описание |
|---|---|
| Интерфейсы фильтра | IBaseFilter, IBasicVideo, IBasicVideo2, IDirectDrawVideo, IKsPropertySet, IMediaPosition, IMediaSeeking, IQualityControl, IQualProp, IVideoWindow |
| Медиатип входящего контакта | MEDIATYPE_Video |
| Интерфейсы входящего контакта | IMemInputPin, IOverlay, IPin, IPinConnection, IQualityControl |
| Медиатипы исходящего контакта | - |
| Интерфейсы исходящего контакта | - |
| CLSID фильтра | CLSID_VideoRenderer |
| CLSID страницы свойств | - |
| Исполнимый файл | quartz.dll |
| Merit | Windows 98, Me, NT, 2000: MERIT_PREFERRED Windows XP: MERIT_UNLIKELY |
| Категория фильтров | CLSID_LegacyAmFilterCategory |
Другие подробности, связанные с особенностями функционирования фильтра и поддержки отладки приложений quartz.dll, следует смотреть в MSDN.
Video Mixing Renderer Filter 9
Этот фильтр расширяет возможности воспроизведения видео для всех платформ, поддерживающих DirectX. Он полностью интегрирован с 3D свойствами DirectX9. Например, можно легко добавлять видео к играм и другим 3D оболочкам или преобразовывать видеоизображения с использованием теневых пикселей Direct3D (3D pixel shaders) и других эффектов.
Этот фильтр не поддерживает видеопорты ( о видеопортах нужно что-нибудь сказать ).
Для обеспечения обратной совместимости VMR-9 не является фильтром отображения по умолчанию ни на какой системе. Для использования этого фильтра нужно добавить его к графу фильтров явно и отконфигурировать его перед соединением с любыми из его входящих контактов. VMR-9 использует собственный набор интерфейсов, структур, перечислителей, которые не всегда идентичны соответствующим типам данных, используемых VMR-7. Более подробно вопрос освещен в соответствующих разделах MSDN - Using the Video Mixing Renderer.
Video Mixing Renderer Filter 7
Свойства, методы и интерфейсы для TVideoWindow
Теперь мы можем перейти к рассмотрению класса TVideoWindow. Он предназначен для отображения видео и объявлен в файле DSPack.pas путем
TVideoWindow = class(TCustomControl, IFilter, IEvent) |
Класс TCustomContol есть наследником TWinControl, поэтому понятно, что TVideoWindow дает возможность, среди прочего, работать с ним как с окном. Будем считать, что с этим предком все ясно, и сосредоточимся на интерфейсах IFilter и IEvent. Они объявлены как
IFilter = interface ['{887F94DA-29E9-44C6-B48E-1FBF0FB59878}'] |
( НУЖНО УКАЗАТЬ, В КАКОМ ЗАГОЛОВОЧНОМ ФАЙЛЕ ЭТО ЗАДАНО )
и
IEvent = interface ['{6C0DCD7B-1A98-44EF-A6D5-E23CBC24E620}'] |
Эти функции должны быть реализованы классом TVideoWindow. Кроме них, наиболее интересными есть функции SetFullScreen, VMRGetBitmap. Кроме этого, обратим внимание на для два private - члена - FAllocatorClass типа TAbstractAllocatorClass и FCurrentAllocator типа TAbstractAllocator. Их объявления:
TAbstractAllocator = class(TInterfacedObject) |
( ПУТЕМ ТРАССИРОВАНИЯ ВЫЯСНИТЬ, ЧТО ЭТО ЗА CREATE )
Рассмотрим также некоторые дополнительные интерфейсы, важные для понимания процесса отображения. Среди них - IVMRSurfaceAllocator, IVMRSurfaceAllocatorNotify, IVMRImagePresenter.
IVMRSurfaceAllocator
Интерфейс IVMRSurfaceAllocator реализуется аллокатором-презентером (allocator-presenter), заданным по умолчанию для фильтра VMR-7. Он должен реализовываться любым plug-in allocator-presenter, который приложение предоставляет фильтру VMR-7. VMR-7 использует методы на этом интерфейсе для выделения, подготовку и освобождение поверхностей DirectDraw. Приложения не используют этот интерфейс. Для VMR-9 используется IVMRSurfaceAllocator9. Вдобавок к методам IUnknown, интерфейс IVMRSurfaceAllocator предоставляет такие методы:
| Метод | Описание |
|---|---|
| AdviseNotify | Вызывается VMR'ом для предоставления распределителя-презентера с указателем интерфейса для колбэк-уведомлений |
| AllocateSurface | Выделяется поверхность DirectDraw |
| FreeSurface | Освобождается поверхность DirectDraw |
| PrepareSurface | Подготавливается поверхность DirectDraw для декодирования в нее следующего видеофрейма |
А интерфейс IVMRSurfaceAllocator9 такие:
| Метод | Описание |
|---|---|
| AdviseNotify | Вызывается VMR'ом для предоставления распределителя-презентера с указателем интерфейса для колбэк-уведомлений |
| GetSurface | Возвращает DirectDraw-поверхность |
| InitializeDevice | Инициализируется устройство Direct3D |
| TerminateDevice | Освобождается устройство Direct3D |
IVMRImagePresenter
Интерфейс IVMRImagePresenter реализован задаваемым по умолчанию аллокатором-презентером для VMR-7. Он должен также быть реализован любым плагином аллокатора-презентера, предоставляемым приложением для VMR-7. VMR-7 использует методы на этом интерфейсе для информирования аллокатора-презентера, что будет представлен видеофрейм, содержащий поддерживаемую поверхность DirectDraw. Соответствующим фильтром для VMR-9 есть интерфейс IVMRImagePresenter9.
Вдобавок к методам, наследуемым от IUnknown, интерфейс IVMRImagePresenter предоставляет следующие методы:
| Метод | Описание |
|---|---|
| PresentImage | Вызывается в точно тот момент, когда будет видеокадр будет представлен. |
| StartPresenting | Вызывается только перед началом видеотображения |
| StopPresenting | Вызывается только после окончания видеопроигрывания |
IVMRSurfaceAllocatorNotify
Интерфейс IVMRSurfaceAllocatorNotify реализован фильтром VMR-7. Приложения используют этот интерфейс для установки собственного аллокатора-презентера и аллокатор-презентер использует этот интерфейс для информирования VMR-7 об изменениях системного окружения, влияющего на поверхности DirectDraw.
Для того, чтобы приложение получило этот интерфейс, VMR должен быть запущен в renderless режиме.
Соответствующим интерфейсом для VMR-9 есть интерфейс IVMRSurfaceAllocatorNotify9.
Вдобавок к интерфейсам, наследуемым от IUnknown, этот интефейс предоставляет следующие методы:
| Метод | Описание |
|---|---|
| AdviseSurfaceAllocator | Вызывается приложением для информирования VMR об использовании пользовательского аллокатора-презентера |
| ChangeDDrawDevice | Уведомляет VMR, что проигрывающее устройство DirectDraw было изменено |
| NotifyEvent | Вызывается аллокатором-презентером для информирования VMR'а о любых значимых событиях DirectShow на всем протяжении процесса выделения или представления |
| RestoreDDrawSurface | Уведомляет VMR о том, что была обнаружена потеря устройства DirectDraw |
| SetBorderColor | Указывается для VMR'а, какой цвет используется в области экранного прямоугольника, который не будет использован для видео, например, когда видео выводится как "letterbox" |
| SetDDrawDevice | Устанавливает начальное устройство и монитор DirectDraw для использования для видеопроигрывания. |
SetAllocator
Этот метод имеет очень простую реализацию:
procedure TVideoWindow.SetAllocator(Allocator: TAbstractAllocatorClass; UserID: Cardinal); |
Этот метод используется, в частности, для установки класса аллокатора в методах класса TBCTransInPlaceFilter, TBCTransInPlaceOutputPin.
О нем (этом методе) нужно бы еще что-нибудь, хотя бы вскользь, сказать.
NotifyFilter
Объявление этого метода -
procedure TVideoWindow.NotifyFilter(operation: TFilterOperation; Param: integer); |
Передаваемый параметр operation имеет перечислимый тип:
TFilterOperation = ( |
И метод выполняет разнообразные вспомагательные операции при уведомлениях с различным значением операции и в зависимости от разных текущих режимов - установка режима отображения, информирование VMR'а об использовании пользовательского аллокатора-презентера, установки пропорций и т.п.
SetFullScreen
Устанавливает полноэкранный режим (путем простой установки размеров окна вывода в полный экран) или отказывается от использования полноэкранного режима.
GraphEvent
Используется для обработки двух типов событий - связанных с изменением палитры (EC_PALETTE_CHANGED) и установкой типа механизма рендеринга (EC_VMR_RENDERDEVICE_SET). В обоих случаях суть сводится к вызову методов интерфейса IVideoWindow - put_Caption (для установки заголовка видеоокна) и put_MessageDrain (для указания окна для пересылки сообщений от мышки и клавиатуры от видеоокна).
ControlEvent
Используется для обработки управляющих событий ceDVDRendered (фильтр был удален) и cePlay (было начато проигрывание).
VMRGetBitmap
Используется для записи в поток текущего изображения.
Класс TDSVideoWindowEx2
Есть альтернативой для обычного способа отображения видео (с помощью TVideoWindow), который дает простой способ для использования оверлейной графики в приложениях.
Наиболее важными private-членами этого класса, необходимыми для достижения декларируемых целей есть:
FVideoWindow - представляющий интерфейс IVideoWindow; |
И методы, наиболее важные из которых мы сейчас и рассмотрим.
Метод
procedure NotifyFilter(operation: TFilterOperation; Param: integer = 0) |
выполняет задачи, сходные с задачами одноименного метода класса TVideoWindow но с, естественно, совершенно другой реализацией.
То же самое относится и к следующейму методу –
procedure GraphEvent(Event, Param1, Param2: integer), |
но обрабатывается также событие изменения часов синхронизации.
Метод
procedure ControlEvent(Event: TControlEvent; Param: integer = 0), |
как и прежде, обрабатывает события ceDVDRendered, cePlay, но вдобавок к ним еще и cePause, ceStop,ceFileRendered. И обработка становится более слоджной, поскольку в данном случае мі имеем бОльшее количество взаимодействующих компонент.
В методе
procedure SetFilterGraph(AFilterGraph: TFilterGraph) |
производится установка графа фильтров.
Очень важным методом есть
function UpdateGraph : HResult; |
который служит для обновления графа фильтров и вызывается при различных изменениях состояний класса. Он строит ту часть графа фильтров, которая ответственна за видеотображение. Если используется OverlayMixer, то устанавливается эксклюзивный полноэкранный режим с помощью интерфейса IDDrawExclModeVideo. В случе же, когда используется фильтр VMR, производится попытка его вместо OverlayMixer'а. В процессе этого соединения производится проверка, не используется ли фильтр декодера Line21, поскольку Overlay Mixer не может быть соединен с Line21 Decoder2. А затем производится соединения VMR'а c Overlay Mixer'ом (вновь созданным, в случае необходимости).