Создание графов с помощью построителя графов записи

[Функция, связанная с этой страницей, DirectShow, является устаревшей функцией. Он был заменён MediaPlayer, IMFMediaEngineи Аудио- и видеозахватом в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует в новом коде использовать MediaPlayer, IMFMediaEngine и захват аудио/видео в Media Foundation вместо DirectShow, по возможности. Корпорация Майкрософт предлагает, что существующий код, использующий устаревшие API, будет перезаписан для использования новых API, если это возможно.]

Несмотря на своё название, построитель графов захвата полезен для создания множества типов настраиваемых графов фильтров, а не только графов захвата. В этой статье представлен краткий обзор использования этого объекта.

Построитель графов записи предоставляет интерфейс ICaptureGraphBuilder2. Начните с вызова CoCreateInstance для создания построителя графов записи и диспетчера графов фильтров. Затем инициализируйте построитель графов захвата, вызвав ICaptureGraphBuilder2::SetFiltergraph с указателем на менеджер графов фильтров, как показано ниже.

IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuilder = NULL;

// Create the Filter Graph Manager.
HRESULT hr =  CoCreateInstance(CLSID_FilterGraph, NULL,
    CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

if (SUCCEEDED(hr))
{
    // Create the Capture Graph Builder.
    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
        CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, 
        (void **)&pBuilder);
    if (SUCCEEDED(hr))
    {
        pBuilder->SetFiltergraph(pGraph);
    }
};

Подключение фильтров

Метод ICaptureGraphBuilder2::RenderStream соединяет два или три фильтра в цепочке. Как правило, метод лучше всего работает, если у каждого фильтра не более одного входного контакта или выходного контакта одного типа. Это обсуждение начинается с игнорирования первых двух параметров RenderStream и сосредоточившись на последних трех параметрах. Третий параметр — это указатель IUnknown, который может указать фильтр (как указатель интерфейса IBaseFilter) или выходной вывод (в качестве указателя интерфейса IPin). Четвертые и пятые параметры указывают указатели IBaseFilter. Метод RenderStream подключает все три фильтра в цепочке. Например, предположим, что A, Bи C являются фильтрами. Предположим, что каждый фильтр имеет ровно один входной пин-код и один выходной пин-код. Следующий вызов подключает A к B, а затем B к C:

"RenderStream(NULL, NULL, A, B, C)"

Все подключения являются "интеллектуальными", что означает, что дополнительные фильтры добавляются в граф по мере необходимости. Дополнительные сведения см. в разделе Intelligent Connect. Чтобы подключить только два фильтра, задайте для среднего значения значение NULL. Например, этот вызов подключает A к C:

"RenderStream(NULL, NULL, A, NULL, C)"

Можно создать более длинные цепочки, вызвав метод дважды:

"RenderStream(NULL, NULL, A, B, C)" "RenderStream(NULL, NULL, C, D, E)"

Если последний параметр NULL, метод автоматически находит стандартный рендерер (отрисовщик). Для видео используется видео-рендер , а для звука — DirectSound Renderer . Таким образом:

"RenderStream(NULL, NULL, A, NULL, NULL)"

эквивалентно

"RenderStream(NULL, NULL, A, NULL, R)"

где R является соответствующим визуализатором. Чтобы подключить фильтр видеомиксера вместо отрисовщика видео, необходимо указать это явно.

Если в третьем параметре указан фильтр, а не пин-код, может потребоваться указать, какой выходной пин-код следует использовать для подключения. Это цель первых двух параметров метода. Первый параметр применяется только к фильтрам записи. Он задает GUID, указывающий категорию пинов. Полный список категорий см. в разделе Набор свойств pin. Две категории допустимы для всех фильтров записи:

  • PIN_CATEGORY_CAPTURE
  • PIN_CATEGORY_PREVIEW

Если фильтр захвата не предоставляет отдельные контакты для захвата и предварительного просмотра, метод RenderStream вставляет фильтр Smart Tee, который разбивает поток на поток захвата и поток предварительного просмотра. С точки зрения приложения, вы можете просто обрабатывать все фильтры захвата как отдельные контакты и игнорировать базовую топологию графа.

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

В следующем примере показано, как подключить оба потока:

// Capture to file:
pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, NULL, pCapFilter, NULL, pMux);
// Preview:
pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, NULL, pCapFilter, NULL, NULL);

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

// Capture to file:
pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL, pCapFilter, NULL, pMux);
// Preview on screen:
pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL, pCapFilter, NULL, NULL);

Второй параметр для RenderStream определяет тип носителя и обычно является одним из следующих:

  • ТИП_МЕДИА_Аудио
  • MEDIATYPE_Видео
  • MEDIATYPE_Interleaved (DV)

Этот параметр можно использовать всякий раз, когда выходные контакты фильтра поддерживают перечисление предпочтительных типов носителей. Для источников файлов конструктор графов захвата автоматически добавляет фильтр синтаксического анализа при необходимости, а затем запрашивает типы носителей у средства синтаксического анализа. (Пример см. в статье Повторное сжатие файла AVI.) Кроме того, если последний фильтр в цепочке имеет несколько входных пинов, метод пытается перечислить их мультимедийные типы. Однако не все фильтры поддерживают эту функцию.

Поиск интерфейсов на фильтрах и штырях

После создания графа обычно требуется найти различные интерфейсы, предоставляемые фильтрами и пинами в графе. Например, фильтр захвата может предоставлять интерфейс IAMDroppedFrames, а выходные контакты фильтра могут предоставлять интерфейс IAMStreamConfig.

Самый простой способ найти интерфейс — использовать метод ICaptureGraphBuilder2::FindInterface. Этот метод проходит по графу (фильтрам и пинам), пока не найдет нужный интерфейс. Можно указать начальную точку для поиска, и можно ограничить поиск фильтрами вверх или вниз по начальной точке.

В следующем примере выполняется поиск интерфейса IAMStreamConfig на пин-коде предварительного просмотра видео:

IAMStreamConfig *pConfig = NULL;
HRESULT hr = pBuild->FindInterface(
    &PIN_CATEGORY_PREVIEW, 
    &MEDIATYPE_Video,
    pVCap, 
    IID_IAMStreamConfig, 
    (void**)&pConfig
);
if (SUCCESSFUL(hr))
{
    /* ... */
    pConfig->Release();
}

Заметка

В теме Поиск интерфейса в фильтре или пине показан альтернативный подход, который использует интерфейс IGraphBuilder вместо ICaptureGraphBuilder2. Какой подход к использованию зависит от приложения. Если приложение уже использует ICaptureGraphBuilder2 для построения графа, то ICaptureGraphBuilder2::FindInterface является хорошим подходом. В противном случае рассмотрите использование методов IGraphBuilder.

 

Поиск закреплений

Менее часто может потребоваться найти отдельный штырь на фильтре, хотя в большинстве случаев методы RenderStream и FindInterface помогут вам сэкономить. Если вам нужно найти определенный вывод фильтра, полезно использовать вспомогательный метод ICaptureGraphBuilder2::FindPin. Укажите категорию, тип носителя (видео или аудио), направление и требование, чтобы пин был неподключён.

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

IPin *pPin = NULL;
hr = pBuild->FindPin(
    pCap,                   // Pointer to the filter to search.
    PINDIR_OUTPUT,          // Search for an output pin.
    &PIN_CATEGORY_PREVIEW,  // Search for a preview pin.
    &MEDIATYPE_Video,       // Search for a video pin.
    TRUE,                   // The pin must be unconnected. 
    0,                      // Return the first matching pin (index 0).
    &pPin);                 // This variable receives the IPin pointer.
if (SUCCESSFUL(hr))
{
    /* ... */
    pPin->Release();
}

захват видео