И снова Direct Show. Теория Часть 2.
Основные понятия.
Фильтр это законченный объект и основная единица графа. У фильтров есть методы, что бы получить и \ или передать данные в \ из поток (а). У каждого фильтра есть, по крайней мере, один канал через который он принимает или отдает данные.
Есть три основных типа фильтров:
- фильтры источники;
- фильтры преобразования;
- фильтры рендеринга;
Любой фильтр DirectShow с которого начинается поток, является фильтром источником. Фильтр абстрагирует нас от типа источника потока. Не зависимо от того, чем в реальности является источник, файлом на диске, или устройством Windows, прикладной программист имеет дело только с интерфейсом фильтра. Любое устройство с корректно установленными драйверами, автоматически становится доступно в виде фильтра.
Фильтром преобразования может быть и фильтр источник и вообще любой фильтр, который каким то образом воздействует на поток и передает его дальше. Например, фильтр преобразования может получать на входе AVI, а на выходе отдавать mp3.
Фильтры рендеринга выводят видео на дисплей, воспроизводят звук в динамиках и т.д.
Фильтры соединяются между собой через каналы, образуя поток, через который идут данные. Совокупность соединенных между собой фильтров образуют граф.
Синхронизация фильтров.
Однако, просто правильно составленного графа не достаточно. Граф должен иметь возможность управлять потоком, например, поставить воспроизведение на паузу, остановить поток или напротив, воспроизвести его. Кроме того, нужно как-то синхронизировать фильтры, потому, что звук и видео при воспроизведении видео фильма делятся на два потока, которые могут один опережать другой. Для синхронизации DirectShow предоставляет программные часы, которые доступны для всех фильтров графа. Часы работают с точностью до 100 нс. ( Точность часов так же зависит от используемого вами оборудования). Когда программист посылает одну из трех основных команд стоп или пауза, она передается каждому фильтру графа в отдельности, фильтры в свою очередь должны быть способны обработать команду.
Интеллектуальное соединение (Intelligent Connect).
Это механизм, который DirectShow использует для автоматического построения графа. Вообще, глядя на этот механизм глазами прикладного программиста, мы вряд ли найдем более или менее весомую причину для рассмотрения подробностей работы интеллектуального соединения. Но для полноты картины и для того, что бы потом научиться строить свои графы, мы немного его исследуем. И так, в мануале это весь процесс описывается так:
1) Если фильтр поддерживает интерфейс IStreamBulder, менеджер графа фильтра делегирует весь процесс ему. Реализуя этот интерфейс, фильтр принимает на себя ответственность за построение оставшееся части графа в низ, до самого рендерера.
2) Менеджер графа фильтра пытается использовать фильтры, кэшируемые в памяти. В процессе соединения фильтров менеджер графа может кэшировать фильтры соединенные на более ранних шагах.
3) Если менеджер графа фильтров содержит фильтры со свободными каналами, он перебирает их. Можно так же указать ему фильтр в ручную.
4) Если на предыдущих шагах дело закончилось ни чем, производится поиск в реестре.
2) Менеджер графа фильтра пытается использовать фильтры, кэшируемые в памяти. В процессе соединения фильтров менеджер графа может кэшировать фильтры соединенные на более ранних шагах.
3) Если менеджер графа фильтров содержит фильтры со свободными каналами, он перебирает их. Можно так же указать ему фильтр в ручную.
4) Если на предыдущих шагах дело закончилось ни чем, производится поиск в реестре.
Выше приведен общий алгоритм, технические детали в нем опущены (пока).
Программные интерфейсы DirectShow.
И так, настало время, познакомится с программным интерфейсом, который позволит нам осуществить выше указанные пертурбации. Начнем мы менеджера графа фильтров. Его реализует класс IGraphBuilder. Далее я привожу таблицу с его методами и поясняю их.
IGraphBuilder.
| Название | Описание |
|---|---|
|
HRESULT Connect(IPin*ppinOut,IPin* ppinIn) Параметры имеют следующее значение: ppinOut – канал который соединяем ppinIn – канал с которым соединяем предыдущий параметр. |
Соединяет два канала. Если они не будут соединяться последовательно, этот метод применяет к ним некоторые трансформации. |
|
HRESULT Render( IPin *ppinOut ); |
Добавляет цепочку фильтров к указанному каналу вывода для рендеринга. |
|
HRESULT RenderFile( LPCWSTR lpwstrFile, LPCWSTR lpwstrPlayList ); |
Простит граф прорендерить файл |
|
HRESULT SetLogFile( DWORD_PTR hFile ); |
Устанавливает лог файл |
| HRESULT Abort(void); | Запрос к менеджеру графа вернуться из текущей задачи как можно скорее. |
| HRESULT ShouldOperationContinue(void); | Запросы должна ли текущая операция продолжаться. |
IMediaControl.
| Метод | Описание |
|---|---|
| HRESULT Run(void); | Запускает все фильтры в графе |
| HRESULT Pause(void); | Приостанавливает все фильтры в графе |
| HRESULT Stop(void); | Останавливает все фильтры в графе |
| HRESULT StopWhenReady(void); | Тут я затрудняюсь дать дееспособный перевод: Pauses the filter graph, allowing filters to queue data, and then stops the filter graph |
|
HRESULT GetState( LONG msTimeout, OAFilterState *pfs ); |
Определяет состояние графа фильтров |
IMediaEvent.
| Метод | Описание |
|---|---|
|
HRESULT CancelDefaultHandling( long lEvCode ); |
Отменяет обработчик по умолчанию для события графа фильтров |
|
HRESULT FreeEventParams( long lEventCode, LONG_PTR lParam1, LONG_PTR lParam2 ); |
Освобождает параметры связанные с параметрами события |
|
HRESULT GetEvent( long *lEventCode, LONG_PTR *lParam1, LONG_PTR *lParam2, long msTimeout ); |
Определяет следующее оповещение о событии из очереди событий |
|
HRESULT GetEventHandle( OAEVENT *hEvent ); |
Retrieves a handle to a manual-reset event that remains signaled while the queue contains event notifications. |
| HRESULT RestoreDefaultHandling( long lEvCode ); |
Восстанавливает обработчик по умолчанию для события |
|
HRESULT WaitForCompletion( long msTimeout, long *pEvCode ); |
Ждет пока менеджер графа не завершит рендеринг |
Ну вот мы и познакомились с некоторой небольшой частью программного интерфейса DirectShow. Этого интерфейса достаточно, что бы воспроизвести файл. Теперь я предлагаю забыть об указателе на интерфейсы COM который я привел в первой статье и начать пользоваться его версией с упрощенным, немного ограниченным интерфейсом. Ограничения заключаются в отсутствии неявных приведений типов, в том числе приведения к bool. Вот его интерфейс. Заголовочный файл можно взять ТУТ.
#ifndef COM_POINTER_HPP
#define COM_POINTER_HPP
namespace divo {
template <class T,const IID* piid = 0> class com_pointer
{
public:
/*!
* Конструктор по умолчанию
*/
com_pointer()
: m_pointer(0)
{}
/*!
* Виртуальный деструктор.Освобождает интерфейс
*/
virtual ~com_pointer() throw()
{
release();
}
/*!
* Освобождает интерфейс
*/
void release()
{
if (m_pointer)
{
m_pointer->Release();
m_pointer = 0;
}
}
/*!
* Задать новое значение.При замене значений старый интерфейс освобождается.
* @param ptr - указатель на новый интерфейс
*/
void reset(T* ptr)
{
release();
m_pointer = ptr;
}
/*!
* @return true если указатель нулевой,false иначе
*/
bool isNull() const
{
return m_pointer == 0;
}
/*!
* @return внутреннй указатель
*/
T* get()
{
Q_ASSERT(m_pointer != 0);
return m_pointer;
}
/*!
* @return адрес указателя
*/
T** ptr()
{
return &m_pointer;
}
/*!
* @return указатель на инкапсулируемый интерфейс
*/
T* operator -> ()
{
Q_ASSERT(m_pointer != 0);
return m_pointer;
}
/*!
* @return разименованный указатель на инкапсулируемый интерфейс
*/
T& operator * ()
{
Q_ASSERT(m_pointer != 0);
return *m_pointer;
}
/*!
* Операция клонирования интерфейса,увеличивает счетчик ссылок и возвращает
* указатель на интерфейс.
*
* @return указатель на интерфейс с добавленной ссылкой
*/
T* clone()
{
Q_ASSERT(m_pointer != 0)
m_pointer->AddRef();
return m_pointer;
}
/*!
* Обертка для глобальной CoCreateInstance призвана
* избавить от жанглирования аргументами
*
* @param clsid - идентификатор класса
* @param i - указатель на агрегирующий интерфейс
* @param clsctx - контекст компонента
* @return признак усешности
*/
HRESULT createInstance(const CLSID& clsid,IUnknown* i = 0,DWORD clsctx = CLSCTX_INPROC_SERVER)
{
release();
return CoCreateInstance(clsid,i,clsctx,*piid,reinterpret_cast<void**>(&m_pointer));
}
/*!
* @return инкапсулируемый интерфейс в виде void**
*/
void** toVoidVoid() throw()
{
return reinterpret_cast<void**>(&m_pointer);
}
private:
/**
* @var m_pointer - указатель на интерфейс
*/
T* m_pointer;
};
}; // namespace divo
#endif /* !COM_POINTER_HPP */
В следующей статье я воспроизведу AVI файл несколькими строками кода. Код будет довольно очевиден, если разобраться в интерфейсах которые, я описал выше.
Комментарии
Ждем продолжения :)