Захват, интерфейсы и устройства

Источник: 
"DirectX и Delphi. 
Разработка графических и 
мультимедийных приложений"

Захват видео

Термин "захват видео" обозначает возможность приложения записывать видео данные, полученные от различных источников, используя API подсистемы DirectShow. Под устройствами захвата изображения понимаются не только камеры (web-камеры, IP-камеры и т.п.), но и TV-тюнеры, различные видео-регистраторы и т.п. Захватываемое видео изображение может быть записано на диск или просмотрено в режиме реального времени.

Большое количество новых устройств захвата изображения используют Модель Драйверов Windows (Windows Driver Model - WDM). Данная архитектура включает набор аппаратно-независимых драйверов, называемых драйверами класса, и набор аппаратно-зависимых мини-драйверов, которые поставляет производитель оборудования. Мини-драйвера реализуют в себе всю специфичную для устройства функциональность.

Граф фильтров DirectShow представляет любое устройство захвата WDM как Фильтр Захвата WDM (WDM Capture Filter). Данный фильтр настраивается в зависимости от характеристик драйвера.

Некоторые старые устройства захвата видео все еще используют драйвера Видео для Windows (Video for Windows - VFW). Несмотря на то, что данные драйвера уже устарели, в подсистеме DirectShow имеется специальный фильтр (VFW Capture), обеспечивающий работу с данным драйвером.

Захват аудио

Используя подсистему DirectShow, мы можем разрабатывать приложения, в которых используется возможность записи звука. Такая возможность уже была рассмотрена нами при изучении подсистемы DirectSound. Разница здесь заключается в механизме записи данных и поддерживаемых форматах.

Запись звука может осуществляться с микрофона, проигрывателя, либо иного устройства, подключенного к звуковой плате компьютера. К основным причинам, для чего все это нужно, можно смело отнести следующие:

  • запись звука для дублирования видео потока;
  • перевод аналогового аудио потока в цифровой формат;
  • передача звука по сети.

Подсистема DirectShowпозволяет записывать звук с различных аналоговых устройств, подключенных к звуковой плате посредством фильтра захвата аудио (AudioCaptureFilter). Этот фильтр использует API Microsoft® PlatformSDKwaveInXXX для управления всеми устройствами, чьи драйвера поддерживают это API. Каждая звуковая плата представлена отдельными экземплярами данного фильтра.

Фильтр захвата звука представляет каждый вход на звуковой плате, будь то микрофон или MIDI-вход, как входной контакт (InputPin). Приложение может использовать данный контакт для разрешения или запрещения ввода, для настройки частот, положения на панораме и т.д. Возможности управления зависят от драйвера. Для полного использования всех возможностей звуковой платы может даже понадобиться документация изготовителя карты.

Интерфейсы

В начале данной части книги, мы с вами изучили ряд интерфейсов и понятий, которые повсеместно используются в подсистеме DirectShow.

Граф фильтров, который выполняет захват аудио или видео, называется графом захвата(capturegraph). Данный граф имеет более сложную структуру, чем обычный граф воспроизведения. Для упрощения построения графа захвата приложением, подсистема DirectShowпредоставляет специальный объект, именуемый построителем графа захвата(CaptureGraphBuilder). Объект представлен интерфейсом ICaptureGraphBuilder2, содержащим методы построения и управления графом захвата.

Общую схему взаимодействия графов захвата и воспроизведения в приложении можно представить следующим образом:

Схема взаимодействия графов захвата и воспроизведения в приложении

Интерфейс ICaptureGraphBuilder2содержит следующие методы:

  • SetFiltergraph – установка графа фильтров для использования;
  • GetFiltergraph – получение используемого в настоящий момент графа;
  • SetOutputFileName– создаем файл для записи данных из графа;
  • FindInterface – поиск интерфейса в графе, начиная с указанного фильтра;
  • RenderStream – построение графа захвата;
  • ControlStream – установка времени старта и остановки для различных потоков захвачиваемых данных;
  • AllocCapFile – предварительная установка нужного размера файла для записи данных;
  • CopyCaptureFile – копирование проверенных мультимедиа данных из файла захвата;
  • FindPin– поиск определенного контакта в фильтре, или проверка соответствия контакта указанному критерию.

Для работы нам будет достаточно всего четырех методов данного интерфейса.

Установка графа фильтров для использования:

function SetFiltergraph(pfg: IGraphBuilder): HResult; stdcall;

  • pfg – указатель на объект графа с интерфейсомIGraphBuilder.

Построение графа захвата:

function RenderStream(
pCategory,
pType: PGUID;
pSource: IUnknown;
pfCompressor,
pfRenderer: IBaseFilter): HResult; stdcall;

  • pCategory – указатель на уникальный идентификатор, определяющий категорию контакта. Может принимать нулевое значение для использования контакта независимо от категории. Мы будем использовать следующие значения:

·PIN_CATEGORY_CAPTURE – захватданных;

·PIN_CATEGORY_PREVIEW – предварительный просмотр;

  • pType– указатель на уникальный идентификатор, задающий тип выходных данных, либо нулевое значение для использования любого контакта, независимо от категории. Идентификатор формата данных может принимать одно из следующих значений:

·MEDIATYPE_AnalogAudio– аналоговое аудио;

·MEDIATYPE_AnalogVideo– аналоговое видео;

·MEDIATYPE_Audio– аудио;

·MEDIATYPE_AUXLine21Data– используется для закрытых заголовков;

·MEDIATYPE_File – файл (устаревший);

·MEDIATYPE_Interleaved– чередуемое аудио и видео. Используется для цифрового видео (DigitalVideo– DV);

·MEDIATYPE_LMRT– устаревший формат (не используется);

·MEDIATYPE_Midi– форматMIDI;

·MEDIATYPE_MPEG2_PES – пакетыMPEG-2 PES;

·MEDIATYPE_MPEG2_SECTION – данныесекцииMPEG-2;

·MEDIATYPE_ScriptCommand– данные в виде сценария. Используетсяв закрытых заголовках;

·MEDIATYPE_Stream – потокданныхбезвременныхметок;

·MEDIATYPE_Text– текст;

·MEDIATYPE_Timecode– данныеразбитыпокадрам. В подсистеме DirectShowнет фильтров, поддерживающих данный формат;

·MEDIATYPE_URL_STREAM– устаревший формат (не используется);

·MEDIATYPE_Video – видео;

  • pSource– указатель на стартовый фильтр в цепи графа или на выходной контакт;
  • pfCompressor – фильтр сжатия. Может принимать нулевое значение;
  • pfRenderer– фильтр-приемник, такой как фильтр воспроизведения или мультиплексор. Может принимать нулевое значение.

Создание файла для записи данных из графа:

function SetOutputFileName(
const pType: TGUID;
lpstrFile: PWCHAR;
out ppf: IBaseFilter;
out ppSink: IFileSinkFilter): HResult; stdcall;

  • pType– указатель на уникальный идентификатор, определяющий подтип выходных мультимедиа данных или идентификатор класса мультиплексора или фильтра записи данных в файл. Если мы хотим задать подтип мультимедиа данных, то должны использовать одно из следующих значений:

·MEDIASUBTYPE_Avi– данные в формате AVI;

·MEDIASUBTYPE_Asf – данные в формате ASF;

  • lpstrFile– имя файла;
  • ppf– адрес указателя, куда будет передан интерфейс мультиплексора;
  • ppSink– адрес указателя, куда будет передан интерфейс записи данных в файл IFileSinkFilter. Может принимать нулевое значение.

Поиск интерфейса в графе, начиная с указанного фильтра:

function FindInterface(
pCategory,
pType: PGUID;
pf: IBaseFilter;
const riid: TGUID;
out ppint): HResult; stdcall;

  • pCategory– указатель на уникальный идентификатор, определяющий критерий поиска;
  • pType– указатель на уникальный идентификатор, задающий тип выходных данных;
  • pf– фильтр, с которого начинается поиск;
  • riid– идентификатор искомого интерфейса;
  • ppint– адрес переменной, в которую и будет записан искомый интерфейс.

Еще один интерфейс, который нами будет использован – это интерфейс управления мультиплексором IConfigAviMux. Его методы:

  • SetMasterStream– установка основного потока для синхронизации с другими потоками в файле;
  • GetMasterStream– получение основного потока;
  • SetOutputCompatibilityIndex– установка индекса формата AVI-файла;
  • GetOutputCompatibilityIndex– получение индекса формата AVI-файла.

Из методов данного интерфейса нам потребуется всего один – SetMasterStream. При одновременном захвате видео данных и аудио потока мы будем выставлять в качестве основного потока звуковой:

function SetMasterStream(iStream: Longint): HResult; stdcall;

  • iStream– индекс потока или -1 при отсутствии основного потока. Потоки нумеруются с нуля.

Для управления свойствами фильтров и контактов нами будут использоваться следующие интерфейсы:

  • IAMStreamConfig– интерфейс управления форматом данных выходного потока;
  • ISpecifyPropertyPages– интерфейс управления страницами свойств.

Интерфейс IAMStreamConfigвыступает в качестве вспомогательного для получения объекта с интерфейсом ISpecifyPropertyPages. Интерфейс управления страницами свойств ISpecifyPropertyPagesсодержит всего один метод – метод получения страниц свойств:

function GetPages(out pages: TCAGUID): HResult; stdcall;

  • pages– указатель на элемент структуры TCAGUID, которую заполняет вызывающий оператор. Для заполнения поля pElemsданной структуры будет вызван метод CoTaskMemAllocи, в конце работы, память должна быть освобождена вызовом CoTaskMemFree.

Перечисление устройств определенного класса

Мы разобрали несколько необходимых для работы интерфейсов, но до сих пор не обсуждали способы получения собственно самого устройства захвата. Для работы со списком устройств нам потребуются следующие интерфейсы:

  • IMoniker– интерфейс позволяет получить указатель на объект, идентифицируемый моникером или получить доступ к хранилищу свойств объекта;
  • IEnumMoniker– используется для перечисления моникеров;
  • ICreateDevEnum– интерфейс нужен для создания объекта перечисления указанной категории устройств;
  • IPropertyBag– предоставляет доступ к коллекции свойств объекта.

Рассмотрим подробнее перечисленные интерфейсы.

Первым делом мы должны создать объект с интерфейсом ICreateDevEnumдля перечисления указанной категории устройств, содержащий всего один метод:

function CreateClassEnumerator(
const clsidDeviceClass: TGUID;
out ppEnumMoniker: IEnumMoniker;
dwFlags: DWORD): HResult; stdcall;

  • clsidDeviceClass– идентификатор класса категории устройств;
  • ppEnumMoniker– адрес переменной, которая получит интерфейс перечисления моникеров IEnumMoniker;
  • dwFlags– битовая комбинация флагов, задающих режим работы данного метода:

·CDEF_DEVMON_CMGR_DEVICE – перечисление аудио и видео кодеков, использующих менеджер сжатия аудио (AudioCompressionManager – ACM) или менеджер сжатия видео (VideoCompressionManager – VCM);

·CDEF_DEVMON_DMO – перечислениеобъектовDMO (DirectX Media Objects);

·CDEF_DEVMON_FILTER – перечисление собственных фильтров DirectShow;

·CDEF_DEVMON_PNP_DEVICE – перечисление устройств Plug-and-Play.

При нулевом значении будут перечислены все фильтры, которые попадают в указанную категорию.

Затем, при помощи интерфейса IEnumMonikerмы организуем цикл перебора всех моникеров. Нужен нам для этого метод IEnumMoniker.Next:

function Next(
celt: Longint;
out elt;
pceltFetched: PLongint): HResult; stdcall;

  • celt– число возвращаемых моникеров;
  • elt– переменная, в которую будет занесена ссылка на моникер;
  • pceltFetched– по завершении работы метода данная переменная будет содержать реальное количество моникеров.

В интерфейсе IMoniker, позволяющему получить доступ к объекту либо хранилищу свойств, нам интересны всего два метода. Это метод получения интерфейса хранилища:

function BindToStorage(
const bc: IBindCtx;
const mkToLeft: IMoniker;
const iid: TIID;
out vObj): HResult; stdcall;

  • bc– указатель на интерфейс IBindCtx. В нашем случае NIL;
  • mkToLeft– если используется составной моникер, то значением будет моникер, стоящий слева от текущего моникера. В нашем случае NIL;
  • iid– уникальный идентификатор запрашиваемого интерфейса, указатель на который будет занесен в vObj;
  • vObj– адрес указателя, в который будет занесен искомый интерфейс.

И метод получения нужного нам интерфейса идентифицируемого моникером объекта:

function BindToObject(
const bc: IBindCtx;
const mkToLeft: IMoniker;
const iidResult: TIID;
out vResult): HResult; stdcall;

  • bc– указатель на интерфейс IBindCtx. В нашем случае NIL;
  • mkToLeft– если используется составной моникер, то значением будет моникер, стоящий слева от текущего моникера. В нашем случае NIL;  
  • iidResult– ссылка на идентификатор интерфейса, который мы желаем получить для связи с объектом, идентифицируемым моникером;
  • vResult– адрес указателя, в который будет занесен искомый интерфейс.

 

Comments