Хорошо, становится все интереснее! На данный момент специализированный фильтр готов к использованию. Единственное, чего не хватает, это класса, который был создавал соответствующие объекты DirectShow (включая ваш фильтр), загружал бы видео файл и управлял бы воспроизведением видео.
На самом деле, вам необходимо использовать один из основных интерфейсов DirectShow- IGraphBuilder. Интерфейс IGraphBuilder, называемый построитель графов, создает и содержит список фильтров (называемый графом фильтров), которые используются при декодировании медиа файла. Построитель графов загружает медиа файл и настраивает соответствующие фильтры, необходимые для обработки данных этого файла. Давайте рассмотрим этот очень важный объект поближе.
Использование построителя графов
Объект IGraphBuilder очень похож на все остальные интерфейсы COM. Чтобы использовать IGraphBuilder, необходимо создать его экземпляр и вызвать CoCreate-Instance для создания фактического объекта и получения его интерфейса.
IGraphBuilder *pGraph;
CoCreateInstance(CLSID_FilterGraph,NULL,\
CLSCTX_INPROC_SERVER,IID_IGraphBuilder, \ (void**)&pGraph);
После создания интерфейса IGraphBuilder вы можете использовать его для регистрации вашего фильтра, чтобы он мог быть использован для загрузки медиа файлов. Однако прежде чем регистрировать фильтр, необходимо создать его копию, которую построитель графов будет держать в памяти при воспроизведении медиа. Для создания экземпляров класса фильтра вы можете использовать оператор new, после чего вы можете зарегистрировать его с помощью функции IGraphBuilder::AddFilter.
Совет. Прежде чем использовать СOМ-объекты, необходимо убедиться, что система COM инициализирована. Вы можете инициализировать систему COМ, вставив следующую строчку кода в начало вашего приложения (обычно, в начало функции WinMain):
CoInitialize(NULL);
При выходе из приложения необходимо деинициализировать систему COM, используя следующую строчку кода:
CoUninitialize();
Замечание. Функция CoCreatelnstance возвращает S_OK, если она была выполнена успешно. Любое другое возвращаемое значение означает, что при создании СОМ-объекта произошла ошибка. Для получения дополнительной информации о значении возвращаемых кодов обратитесь к Win32 SDK.
Вот пример кода, создающего фильтр и регистрирующего его в графопостроителе:
/ / pD3DDevice=указатель на предварительно инициализированный объект 3D //устройства
// Создать экземпляр фильтра cTextureFilter *pTextureFilter = \
new cTextureFilter(pD3DDevice, NULL, &hr) ;
/ / Добавить фильтр в построитель графов
IBaseFilter *pFilter = (IBaseFilter*)pTextureFilter;
pGraph->AddFilter(pFilter, L"ANIMATEDTEXTURE");
Функция AddFilter построителя графов принимает в качестве параметров указатель на фильтр (преобразовываемый к интерфейсу класса IBbaseFilter, от которого наследуются все фильтры) и присваиваемое ему имя. В предыдущем примере построителю графов были переданы указатель на фильтр текстур (pFilter) и его имя (ANIMATEDTEXTURE). После этого имя фильтра больше не используется; оно необходимо для ваших собственных целей и для внешнего просмотрщика фильтров, проверяющего все фильтры DirectShow.
После того как вы зарегистрировали фильтр, вы можете использовать его, выбрав исходный видео файл.
Выбор исходного файла
После того как вы зарегистрировали фильтр в DirectShow (при помощи интерфейса IGraphBuilder), необходимо указать DirectShow импортируемый видео файл. Функция IGraphBuilder::AddSourceFilter предназначена для установки исходного файла и создания соответствующих фильтров для импортирования видео данных.
HRESULT IGraphBuilder::AddSourceFilter( LPCWSTR lpwstrFileName, LPCWSTR lpwstrFilterName, IBaseFilter **ppFilter);
Функция AddSourceFilter имеет три параметра: имя загружаемого медиа файла, имя исходного фильтра (lpwstrFilterName, о котором вы прочитаете чуть ниже) и указатель (ppFilter) на объект фильтра IBaseFilter, который используется для получения доступа к объекту фильтра видео.
К чему все эти разговоры об исходном фильтре? Я знаю, что ранее говорил, что для декодирования медиа файла используется набор фильтров. Как показано на рис. 14.2, исходный фильтр на самом деле является способом доступа ко всем остальным фильтрам, используемым графопостроителем.
Создав исходный фильтр (используя интерфейс IBaseFilter) и назвав его (с помощью параметра lpwstrFilterName), вы можете быстро получить доступ к разнообразным фильтрам построителя графов через один интерфейс исходного фильтра.
Теперь чувствуется разница, не так ли? Для создания базового фильтра и загрузки медиа файла вызовите AddSourceFilter. Базовый фильтр будет иметь имя SOURCE (преобразованной к Unicode строке с помощью макроса L"") и требует созданного экземпляра интерфейса IBaseFilter.
// Filename = имя используемого видео файла IBaseFilter *pSrcFilter;
// Преобразовать их ASCII текста в Unicode:

Рис. 14.2. Исходный фильтр позволяет использовать один интерфейс для представления набора объектов фильтров
WCHAR wFilename[MAX_PATH]; mbstowcs(wFilename, Filename, MAX_PATH);
// Добавить исходный фильтр
pGraph->AddSourceFilter(wFilename, L"SOURCE", &pSrcFilter);
После вызова AddSourceFilter вы получите указатель на исходный фильтр. Этот указатель pSrcFilter используется для соединения выходной pin источника видео и входной pin фильтра текстуры.
ЗЗаметние.Яконвертировалмулътибайтную текстовую строку Filename вуникодовскую строку, потому что функция AddSourceFilter (как и большинство функций DirectShow) принимает в качестве параметров символыуникода.
Соединение разъемов
О чем это я говорю тут - что за разъемы (pins) и что они делают? Каждый фильтр имеет набор разъемов, которые в каком-то смысле являются трубами, выдающими и принимающими данные медиа в и из фильтра. Данные медиа поступают на входной разъем первого фильтра. Данные обрабатываются фильтром и передаются следующему через выходной разъем. Процесс продолжается до тех пор, пока не закончатся данные, в настоящем случае представленные используемой поверхностью текстуры. Посмотрите на рис. 14.3, чтобы лучше понять, что я имею ввиду.
Каждый фильтр выполняет с данными все необходимые операции и предает их следующему фильтру. Что же касается нашего фильтра, он просто ожидает данных медиа и вставляет их на поверхность текстуры. Опять же, зачем необходимы разъемы?

Рис. 14.3. Демонстрационный набор из четырех фильтров используется для получения данных медиа файла, ихдекодирования и визуализации
Следующим шагом к использованию видео текстуры является нахождение входного разъема (in pin) вашего фильтра (разъема, который принимает входящие данные), выходного разъема (out pin) исходного фильтра (того, из которого данные выходят) и их соединение.
Чтобы найти эти два разъема (входной разъем вашего фильтра и выходной разъем фильтра источника), необходимо использовать функцию FindPin.
HRESULT IBaseFilter::FindPin(
LPCWSTR Id, // Имя искомого разъема IPin **ppPin) ; // Объект интерфейса pin
Я знаю, что говорил вам, что мы не будем иметь дела с множеством интерфейсов DirectShow, но я обещаю вам, их осталось немного. Функция FindPin имеет два параметра: имя искомого разъема (в данном случае либо In, либо Out, причем имена представлены Unicode символами) и интерфейс IPin.
В данном случае интерфейсы IPin нам не важны; они нам необходимы только для того, чтобы графопостроитель мог соединить их. Сначала посмотрите код, ищущий оба разъема, а потом переходите к их соединению.
// Найти входной и выходной разъемы и соединить их IPin *pFilterPinIn; IPin *pSourcePinOut;
pFilter->FindPin(L"In", &pFilterPinIn); pSourceFilter->FindPin(L"Output", &pSourcePinOut) ;
He может быть ничего проще. Сделав вызов еще одной функции, вы можете соединить разъемы.
рСгарп->СоппесЬ(рЗоигсеР1п0иЬ, рРд.1ЬегРд.п1п) ;
На данный момент вы загрузили медиа файл и соединили фильтры, подготовив их к работе! Осталось только начать проигрывание видео потока. Проблемой является то, что построитель графов не содержит функций управления проигрыванием. Что за бездельник! Однако не расстраивайтесь, потому что с помощью построителя графов вы можете получить интерфейсы, управляющие проигрыванием видео потоков.
Получение интерфейсов
Чтобы управлять проигрыванием видео данных, необходимо из построителя графов получить интерфейс управления медиа - ImediaControl. Интерфейс управления медиа может проигрывать, останавливать, приостанавливать и продолжать проигрывание видео потока.
Для определения завершения проигрывания видео необходимо получить из графопостроителя интерфейс медиа событий - ImediaEvent. Наконец, для определения текущего положения или даже времени воспроизведения видео необходимо получить еще один дополнительный интерфейс.
Используйте следующий код для получения необходимых интерфейсов:
/ / Создать экземпляры объектов медиа IMediaControl *pMediaControl; IMediaPosition *pMediaPosition; IMediaEvent *pMediaEvent;
// Получить интерфейсы
pGraph->QueryInterface(IID_IMediaControl, \
(void **)&pMediaControl); pGraph->QueryInterface(IID_IMediaPosition, \
(void **)&pMediaPosition); pGraph->QueryInterface(IID_IMediaEvent, \
(void **)&pMediaEvent) ;
He сдавайтесь - мы уже почти закончили! Осталось только начать воспроизведение видео, определять текущее положение видео по мере его воспроизведения и наблюдать за различными возникающими событиями медиа.
Управление проигрыванием и обработка событий
Последним шагом к получению анимированной текстуры является начало воспроизведение видео источника при помощи только что созданного объекта IMediaControl. Интерфейс IMediaControl содержит две интересующие нас функции: IMediaControl::Run и IMediaControl::Stop.
Функция IMediaControl::Run начинает воспроизведение видео с текущего положения, что в свою очередь инициирует передачу данных вашему фильтру и, в конце концов, поверхности текстуры. Функция Run не имеет никаких параметров и может быть использована так:
pMediaControl->Run();
Во время воспроизведения видео вы можете остановить его, вызвав функцию IMediaControl::Stop, которая также не имеет параметров. Следующий код останавливает воспроизведение видео:
pMediaControl->Stop();
Замечательным является то, что вы можете использовать функции Run и Stop для приостановки и продолжения видео воспроизведения. Вызов функции Stop приостанавливает воспроизведение, в то время как последующий вызов Run продолжает его.
Чтобы переместиться в заданное положение в видео (например, в начальное положение, когда вы хотите "перемотать" его) используется интерфейс IMediaPosition. Чтобы переместиться на заданное время в видео файле, вызовите функцию IMedia-Position::put_CurrentPosition.
HRESULT IMediaPosition::put_CurrentPosition( REFTIME llTime) ;
При ближайшем рассмотрении параметр REFTIME функции putCurrentPosition оказывается вещественным значением; время llTime устанавливается в любое значение для перемещения видео потока. (Например, 2.0 означает переместиться на 2.0 секунды в потоке.) Функция put_CurrentPosition в основном используется для перемотки видео и воспроизведения его сначала, как я сделал тут:
// Переместиться в начало потока видео (0 секунд) pMediaPosition->put_CurrentPosition(0.0f) ;
Последним, в трио используемых медиа объектов, идет IMediaEvent, который получает все возникающие события. Нас интересует только событие, означающее окончание воспроизведения видео, чтобы его можно было начать заново, process some function to stream in another video или сделать еще что-нибудь.
Чтобы получить событие с помощью интерфейса IMediaEvent, необходимо использовать функцию IMediaEvent: :GetEvent.
HRESULT IMediaEvent::GetEvent(
long *lEventCode, // Код события long *lParam1, // Первый параметр события long *lParam2, // Второй параметр события long msTimeout); // Время ожидания события
Функция GetEvent имеет четыре параметра, первые три из которых являются указателями на переменные типа long, а последний сам является переменной типа long. После вызова GetEvent первые три переменные (lEventCode, lParaml и lРаrаm2) будут содержать информацию о текущем событии, такую как код события и два его параметра. Четвертую переменную msTimeout вы устанавливаете равной величине времени, равной задержке между проверкой на возникновение события, или вы можете установить 0, чтобы ждать бесконечно.
Единственное событие, которое вы хотим получить, это окончание видео, представленное макросом ECCOMPLETE. He надо бесконечно ждать события; вместо этого необходимо подождать несколько миллисекунд, а потом продолжать получать события, пока их не останется. Как узнать, что больше не осталось событий? Как только функция GetEvent вернет ошибку, можно смело полагать, что больше событий нет.
Вот пример кода, который циклически получает текущее событие из интерфейса событий медиа (останавливаясь, когда событий больше не осталось), и небольшой блок кода, обрабатывающий событие, - EC_COMPLETE.
// Обработать все ожидающиеся события long lEventCode, lParami, lParam2; while(1) {
// Получить события и подождать миллисекунду
if(FAILED(pMediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2,
1)))
break;
// Обработать события окончания видео, вызвав специальную функцию
if(lEventCode == EC_COMPLETE)
EndOfAnimation(); // Здесь может быть любая функция // Освободить ресурсы события
pMediaEvent->FreeEventParams(lEventCode, lParami, lParam2);
}
Xa! Я попытался обмануть вас, использовав вызов новой функции интерфейса ImediaEvent - FreeEventParams. Каждый раз, когда вы получаете события с помощью функции GetEvent, необходимо сопровождать их соответствующим вызовом FreeEventParams, чтобы объект события медиа мог освободить все ресурсы, используемые для события.
Вот и все. Теперь вы можете воспроизводить, останавливать, приостанавливать, продолжать и изменять положение воспроизведения видео текстуры, а также проверять завершение воспроизведения (по достижении которого вы можете начать воспроизведение заново или сделать что-либо другое).
⇐Создание специализированного фильтра || Оглавление || Создание менеджера анимированных текстур⇒