Статьи‎ > ‎

Microsoft DirectX Media Objects (DMO)

Вступление

Microsoft DirectX Media Objects (DMOs) - медиаобъекты DirectX - это базирующиеся на COM'е компоненты потоковых данных. В некотором смысле DMO - это упрощенные фильтры DirectShow. Подобно им, DMO берут входящие данные и используют их для продуцировния исходящих. Но API для DMO проще соответствующего API для DirectShow. В результате DMO легко создавать, тестировать и использовать. DMO ест смысл использовать в следующих случаях:

  • Приложения, базирующиеся на DirectShow, могут использовать DMO через DirectShow-фильтры, вызывая фильтр-враппер над DMO. Разница между фильтрами и DMO прозрачна для приложений. Приложение не вызывает прямо API DMO.
  • Приложения для DirectSound могут использовать аудио DMO-эффекты. Снова-таки, приложение имеет дело не с низкоуровневым API DMO, а с высокоуровневым DirectSound API.
  • Приложения могут использовать DMO напрямую.

При написании DMO можно создавать компоненты, использующиеся в широком спектре приложений.

О DMO

DMO предлагает следующие преимущества:

  • Они намного меньше и проще, чем DirectShow-фильтры, т.к. поддерживают меньшую функциональность.
  • Они гибче DirectShow-фильтров, т.к. они не требует графа фильтров. Вы можете использовать их с DirectShow, когда вам необходимы сервисы, поддерживаемые DirectShow - такие как синхронизация, intelligent connect, автоматическая поддержка пересылки данных и управление потоками. Клиенты, которым эти сервисы не необходимы, могут использовать DMO напрямую.
  • DMO всегда выполняют синхронизацию обработки данных, которые устраняют много потоковых деталей, которые нужно предусмотреть при написании собственных фильтров.
  • В отличие от традиционных ACM и VCM кодеков, DMO базируются на COM'е, так что они могут быть расширены через QueryInterface.
  • DMO поддерживают более общую потоковую модель, чем ACM и VCM кодеки. Подобно DirectShow-фильтрам, DMO могут поддерживать множественные входы и выходы.

По этим причинам DMO теперь рекомендуется как решение для написания кодеков, декодеков и аудиоэффектов. Возможны также, в зависимости от потребностей, и другие способы использования.

Чем же отличаются DMO и фильтры DirectShow?

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

  • Выделение буферов.
  • Договор о медиатипах и соединение с другими фильтрами.
  • Проталкивание данные через граф фильтров.
  • Посылка сообщений менеджеру графа фильтров.
  • Синхронизизация многочисленных потоков.

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

Архитектура DMO

Главные понятия DMO-архитектуры - потоки, медиатипы и буфера.

DMO - это объекты имеющие m входов и n выходов. Входы и выходы называются потоками (stream). Каждый DMO имеет по меньшей мере один поток. Потоки - это не объекты; они просто ссылаются на DMO по индексу. Количество потоков фиксируется во время разработки.

Все данные обычно используют медиатип, который определяется как интерпретируемое содержание данных (контент). Например, 320 на 240 24-битное RGB видео - это один тип; 44.1 килогерц 16-битное стерео PCM-аудио - другой тип. Медиатипы описываются с использованием структуры DMO_MEDIA_TYPE. Перед тем, как клиент сможет обрабатывать данные, он должен установить медиатип для каждого потока на DMO.

Обычно потоки могут работать с несколькими медиатипами. Одни DMO поддерживают более широкий спектр типов, чем другие. Интерфейсы DMO определяют методы для клиентов для поддержки типов. Например, один DMO может поддерживать RGB-видео любой глубины, а другой - только 24-битный. Также в DMO может быть ограниченной комбинация входов и выходов. Например, если входящий тип - 16-битное видео, то исходящий поток может требовать какую-то определенную глубину. Клиент может перечислить все поддерживаемые потоками типы и оттестировать затем определенные комбинации.

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

Опционально DMO может поддерживать in-place обработку. В этом случае DMO записывает выход непосредственно во входящий буфер, поверх исходных данных. In-place - обработка исключает необходимость отдельных буферов. С другой стороны, это меняет исходные данные, что может быть недопустимо для некоторых приложений.

Задаваемая по умолчанию (не in-place) буферная модель поддерживается посредством интерфейса IMediaObject. Все DMO должны реализовывать этот интерфейс. Если DMO поддерживает in-place обработку, это предоставляется через интерфейс IMediaObjectInPlace. Клиент отвечает за распределение всех буферов - и входящих, и исходящих.

Использование DMO

Опишем же, как использовать DMO. Сначала рассмотрим непосредственное использование DMO-объектов, а потом их использование в DirectShow.

Приложение может передавать данные непосредственно DMO, DMO создаст выход, и приложение сможет использовать этот выход для рендеринга, дальнейшей обработки и т.д. Приложение отвечает за такие вещи, как распределение памяти, учет времени, синхронизацию и нити. Эти требования зависят от природы приложения.

Информация, приведенная тут, также может быть использована для написания компонент, которые работают в качестве прослойки между приложением и DMO (например, ActiveX, котрый управляет DMO). Вдобавок этот раздел полезно почитать, если вы пишете DMO, т.к. в нем описана функциональность, которую должны реализовывать DMO.

Использование медиатипов на DMO

Перед тем, как DMO сможет обрабатывать какие бы то ни было данные, клиент должен установить медиатип для каждого потока. (Есть, правда, небольшое исключение из этого правила, см. ниже - опциональные потоки). Чтобы найти количество потоков, нужно вызвать метод IMediaObject::GetStreamCount:

DWORD cInput = 0, cOutput = 0;
pDMO->GetStreamCount(&cInput, &cOutput);

Этот метод вернет два значения - количество входов и выходов. Эти значения не могут изменяться на протяжении существования DMO.

С каждым потоком DMO связывает список возможных медиатипов в порядке предпочтения. Например, предпочтительными могут быть (в порядке предпочтения) такие типы: 32-бит-RGB, 24-бит-RGB, 16-бит-RGB. Когда клиент устанавливает медиатипы, он может использовать эти списки как подсказки. Для нахождения предпочтительного типа потока нужно вызвать метод IMediaObject::GetInputType или метод IMediaObject::GetOutputType. Указывается номер потока и индексное значение типа (начиная с нуля). Например, следующий код находит первый предпочтительный тип для первого входящего контакта:

DMO_MEDIA_TYPE mt hr= pDMO->GetInputType(0, 0, &mt);
if (SUCCEEDED(hr)) { // Прверка этого медиатипа (не показано)/* ... */// Освобождение
MoFreeMediaType(&mt);
}

Для перечисления всех предпочтительных медиатипов на данном потоке нужно использовать цикл, в котором приращивается индекс типа до тех пор, пока метод не вернет DMO_E_NO_MORE_ITEMS, как показано в следующем примере:

DMO_MEDIA_TYPE mt;
DWORD iType = 0;
while (hr =pDMO->GetInputType(0, iType, &mt), SUCCEEDED(hr)) { // Проверка этого
медиатипа (не показано)
/* ... */// Освобождение
MoFreeMediaType(&mt);
++dwType;
}

О предпочтительных типах нужно помнить следующее:

  • DMO может вернуть тип, не имеющий блока формата. Например, DMO может специфицировать медиатип, такой, как 24-битное RGB, без указания ширины и высоты изображения. Но при установке этого типа необходимо полностью задать блок формата. (Некоторые медиатипы, такие, как MIDI, никогда не требуют блока формата, в этом случае это замечание можно игнорировать).
  • DMO не обязано поддерживать все комбинации предпочтительных типов, которые он возвращает. Например, если DMO имеет два потока, и каждый поток имеет четыре допустимых типа, имеется 16 комбинаций, но не все из них гарантированно будут поддерживаться.
  • Когда клиент устанавливает медиатип для одного потока, DMO может обновить предпочтиаемые типы для других потоков, отражая новое состояние. Но такое поведение, впрочем, необязательно.
  • Для некоторых потоков DMO может не предоставлять никаких типов. Но обычно DMO предлагает хотя бы один предпочитаемый тип для некоторого потока.
  • DMO не обязано предоставлять полный список поддерживемых им медиатипов. Они могут не афишироваться, т.е. быть поддерживаемыми, но не предлагаемыми DMO.

Если вкратце, то клиент может рассматривать предпочитаемые типы только как общее направление. Путь, которым можно точно определить, какие типы поддерживаются, есть их тестирование, как и будет сейчас описано.

Вы можете предоставить структуру DMO_MEDIA_TYPE, содержащую полное описание медиатипа. Следующий пример устанавливает медиатип на входящем контакте 0, используя 44.1kHz 16-битное стерео PCM-аудио:

DMO_MEDIA_TYPE mt; ZeroMemory(&mt, sizeof(DMO_MEDIA_TYPE));
// Выделение памяти для блока формата HRESULT
hr = MoInitMediaType(&mt, sizeof(WAVEFORMATEX));
if (SUCCEEDED(hr)) {
// Set the type GUIDs
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
mt.formattype = FORMAT_WaveFormatEx;
// Инициализация блока формата WAVEFORMATEX
*pWave = reinterpret_cast<WAVEFORMATEX*>(mt.pbFormat);
pWave->wFormatTag = WAVE_FORMAT_PCM;
pWave->nChannels = 2;
pWave->nSamplesPerSec = 44100;
pWave->wBitsPerSample = 16; pWave->nBlockAlign = (pWave->nChannels *
pWave->wBitsPerSample) / 8; pWave->nAvgBytesPerSec =
pWave->nSamplesPerSec * pWave->nBlockAlign;
pWave->cbSize = 0; // Установка
медиатипа hr = pDMO->SetInputType(0, &mt, 0);
// Освобождение блока формата
MoFreeMediaType(&mt);
}

Для тестирования медиатипа без его установки нужно вызвать методы SetInputType и SetOutputType с флагом DMO_SET_TYPEF_TEST_ONLY. Метод вернет S_OK, если тип приемлемый, S_FALSE в противоположном случае.

if (S_OK == pDMO->SetInputType(0, &mt, DMO_SET_TYPEF_TEST_ONLY)
{
// Media type is OK.
}

Из-за того, что установки на одном потоке могут влиять на другой поток, вам может быть необходимо очистить медиатип на потоке. Для того, чтобы это сделать, нужно вызвать методы SetInputType и SetOutputType c флагом DMO_SET_TYPEF_CLEAR.

Для DMO-декодера клиент обычно устанавливает сначала входящий тип, а затем выбирает исходящий тип. Для DMO-кодера клиент сначала устанавливает исходящий тип, а затем - входящий.

Обработка данных в DMO

Перейдем к объяснению обработки потока данных с использованием DMO. Шаги, описанные здесь, относятся к поведению по умолчанию (не in-place); все DMO должны поддерживать описанные здесь методы. Эти методы используют раздельные буферы для входа и выхода. Некоторые DMO могут поддерживать in-place - обработку, используя один буфер. Этот будет описано позже.

Выделение буферов

Клиент отвечает за распределение всех буферов. После того, как вы установили медиатипы на DMO, требуется сделать запрос к DMO на предмет каждого потокового буфера. Это может изменить зависимость медиатипов. Для каждого потока вызывается метод IMediaObject::GetInputSizeInfo или IMediaObject::GetOutputSizeInfo. Эти методы возвращают следующую информацию:

  • Минимальный размер буфера в байтах.
  • Требуемое выравниванивание. Буфер выравнивается, если начальный адрес делится на некоторые специальные целые числа.
  • Максимальное количество данных DMO, которые удерживаются для предпросмотра. Это количество применимо только к входящим потокам. Для некоторых типов данных (например, MPEG-кодированных), DMO может быть необходимо просмотреть некоторое количество данных дальше по потоку. Эта величина предпросмотра указывает на то, сколько входящих данных DMO затребует перед тем, как сможет начать вывод данных.

Клиент должен выделить буфера, которые ожидаются для этих требований. Вдобавок, DMO может иметь требования к упаковке клиентом входящих данные. Например, DMO может требовать, чтобы один буфер содержал только один сэмпл (или видеокадр). Для определения этих требований нужно вызвать метод IMediaObject::GetInputStreamInfo. Метод IMediaObject::GetOutputStreamInfo просто возвращает информацию об исходящем потоке.

В потоковой модели, принятой по умолчанию, клиент не передает укзатель на буфер в DMO. Вместо этого используется облегченный COM-объект, предоставляющий интерфейс IMediaBuffer. Интерфейс IMediaBuffer действует как COM-оболочка для блока памяти. Т.к. это COM-объект, он поддерживает счетчик ссылок, который дает уверенность в том, что буфера не будут освобождены, пока они используются.

Примечание: Функции серверного интерфейса IMediaBuffer проще интерфейса IMediaSample в DirectShow.

Клиент должен реализовать объект IMediaBuffer. См. также Реализация ImediaBuffer'а.

Обработка данных

Для обработки данных нужно сделать следующее:

  1. Для каждого входящего потока залить буфер входящими данными.
  2. Вызвать метод IMediaObject::ProcessInput для пересылки каждого буфера.
  3. Вызвать метод IMediaObject::ProcessOutput для обработки данных. Этот метод касается массива буферов, по одному на каждый исходящий поток.
  4. Повторять эти операции до тех пор, пока больше не останется входящих данных.

Метод ProcessInput принимает на вход один поток в один вызов. Обычно метод возвращает результат тотчас же, и DMO удерживает счетчик ссылок на объект IMediaBuffer. Освобождается объект после обработки всех данных в буфере, или когда приложение сбрасывает (flushes) DMO. Не используйте буфер снова, пока DMO не освободит его. Для того, чтобы определить, может ли входящий поток принять данные, можно вызвать метод IMediaObject::GetInputStatus. Этот метод возвращает флаг DMO_INPUT_STATUSF_ACCEPT_DATA в том случае, если поток может принимать входящие данные.

Метод ProcessOutput генерирует выход для всех исходящих потоков сразу. Приложение передает в массив структуры DMO_OUTPUT_DATA_BUFFER, одну на каждый исходящий поток. Каждая структура в массиве имеет указатель на объект IMediaBuffer. DMO пишет так много исходящих данных, сколько их может поместиться в буфера. Устанавливаются также различные флаги для отчета о статусе операции. Флаг DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE свидетельствует о том, что DMO может производить больше выходов из существующего входа. В этом случае клиент может снова вызвать метод ProcessOutput. С другой стороны, можно вызывать метод ProcessInput с бОльшим количесвтом входящих данных. DMO никогда не модифицирует данные во входящем буфере, они пишутся только в исходящие буфера.

После того, как все данные будут пересланы во входящий поток, нужно вызвать метод IMediaObject::Discontinually. DMO больше не примет входных данных, пока не исчерпаются исходящие данные (или DMO не будет сброшен (flush)).

В любом месте после старта потоков DMO может принять вход или спродуцировать выход, или и то, и другое. Но, однако, каждый вызов метода GetInputStatus возвращает DMO_INPUT_STATUSF_ACCEPT_DATA, или метод ProcessOutput возвращает DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE. Приложение удерживает текущие данные, тестируя эти флаги, и вызывая, соответственно, методы ProcessInput и ProcessOutput. Для прерывания потока данных вызывается метод IMediaObject::Flush. Этот метод приводит к сбросу всех буфером, удерживаемых DMO.

In-Place обработка

Некоторые преобразования данных могут быть выполнены прямой модификацией данных. Это называется in-place обработкой. Многие аудио и видео эффекты могут быть выполнены таким манером. Если DMO поддерживает in-place обработку, он предоставляет интерфейс IMediaObjectInPlace. In-place обработка, вообще говоря, более эффективна, чем использование отдельных буферов для выхода. (Есть важное исключение, когда буфер находится в видеопамяти. В этом случае операции чтения намного медленне операций записи, так что in-place обработка в этом случае не рекомендуется.)

Для in-place обработки данных клиент делает один вызов метода IMediaObjectInPlace::Process, а не отдельные вызовы ProcessInput и ProcessOutput. Метод Process - синхронный; вся обработка выполняется внутри вызова. In-place обработка не использует и объектов IMediaBuffer. Метод Process имеет дело непосредственно с указателем на буфер памяти.

DMO, поддерживающий in-place обработку, тоже должно реализовывать интерфейс IMediaObject, включая методы ProcessInput и ProcessOutput. Клиент может выбрать, что ему использовать - in-place обработку или раздельные буфера. Но нельзя смешивать эти два типа обработки. Если вы вызываете Process, не вызывайте ProcessInput и ProcessOutput, и наоборот.

Хвостовые эффекты

In-place DMO может создавать некоторый дополнительный вывод после того, как вход будет уже остановлен. Это называется хвостовым эффектом. Например, эффект эха продолжается после того, как входящий сигнал уже затих. Если это есть хвостовым эффектом, метод Process возвращает S_FALSE. Как только приложение обработает все свои данные, оно сгенерирует хвостовой эффект отправкой пустого буфера методу Process.

Опциональные потоки

DMO может обозначить некоторые из своих исходящих потоков как опциональные. Опциональные потоки производят данные, которые приложение может отбросить, either completely or on occasional samples. Например, опциональный поток может содержать дополнительную информацию о первичном потоке.

Для запроса, есть ли поток опциональным, используется вызов метода IMediaObject::GetOutputStreamInfo и проверка параметра pdwFlags. В случае опциональном потока будет возвращен флаг DMO_OUTPUT_STREAMF_DISCARDABLE или флаг DMO_OUTPUT_STREAMF_OPTIONAL. Эти флаги значат почти одно и то же; небольшая разница между ними будет коротко объяснена.

Если поток опционален, клиент может дать инструкцию DMO о том, что нужно отбросить (discard) данные из этого потока, когда производится вывод. Для того, чтобы это сделать, нужно вызвать метод IMediaObject::ProcessOutput и установить исходящий буфер в NULL для каждого потока, для которого нужно отбрасывать данные. (Исходящий буфер указывается в члене pBuffer для DMO_OUTPUT_DATA_BUFFER.) Нужно также флаг dwFlags установить в DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER.

Для каждого потока, для которого pBuffer равен NULL, DMO будет пытаться отбрасывать данные. ЕСли поток опционален, DMO гарантировано отбросит данные. Если поток не опционален, DMO будет отбрасывать данные, когда это возможно, но он не гарантирует это. Если данные не могут быть отброшены, он выставляет флаг DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE. Если вы выставите указатель pBuffer в NULL, но не укахите флага DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER, DMO не будет отбрасывать данных. В этом случае DMO выводит каждый буфер внутрь или просто возвращает ошиьку на вызов ProcessOutput.

Отличия между флагами DMO_OUTPUT_STREAMF_OPTIONAL и DMO_OUTPUT_STREAMF_DISCARDABLE следующие:

  • Флаг DMO_OUTPUT_STREAMF_OPTIONAL свидетельствует о том, что клиент не имеет множества медиатипов на этом потоке. Однако, если клиент начинает обработку данных без установки медиатипа для потока, то он должен отбрасывать данные из этого потока, пока он не завершит работу. Если вы хотите отбрасывать сэмплы выборочно, то вы должны установить медиатип.
  • Флаг DMO_OUTPUT_STREAMF_DISCARDABLE свительствует о том, что, хотя поток и есть опциональным, он всегда требует медиатипа.

Реализация IMediaBuffer’а

В принятой по умолчанию потоковой модели DMO буфера управляются через интерфейс IMediaBuffer. Клиент DMO отвечает за реализацию объекта, предоставляющего этот интерфейс. Интерфейс IMediaBuffer имеет три метода:

  • GetBufferAndLength - возвращает адрес буфера (т.е. блока памяти, в котором находятся данные) и размер любых верных данных в буфере.
  • GetMaxLength - возвращает размер буфера.
  • SetLength - указывает длину правильных (valid) данных в буфере.

In-place обработка не требует интерфейса IMediaBuffer. Следующий код дает минимальную реализацию IMediaBuffer'а:

// Класс CMediaBuffer#include <new>
#include <dmo.h>
class CMediaBuffer : public IMediaBuffer
{
private:
DWORD m_cbLength;
const DWORD m_cbMaxLength;
LONG m_cRef;
BYTE *m_pbData;

public:
CMediaBuffer(DWORD cbMaxLength) :
m_cRef(0),
m_cbMaxLength(cbMaxLength),
m_cbLength(0),
m_pbData(NULL)
{
m_pbData = new BYTE[cbMaxLength];
if (!m_pbData) throw std::bad_alloc();
}

~CMediaBuffer()
{
if (m_pbData) {
delete [] m_pbData;
}
}

// Функция для создания новый объект IMediaBuffer и возвращает
 // указатель на интерфейс AddRef (???)static HRESULT CreateBuffer(long cbMaxLen, REFIID iid, void **ppUnk)
{
try {
CMediaBuffer *pBuffer = new CMediaBuffer(cbMaxLen);
return pBuffer->QueryInterface(iid, ppUnk);
}
catch (std::bad_alloc)
{
return E_OUTOFMEMORY;
}
}

// Методы IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (ppv == NULL) {
return E_POINTER;
}
if (riid == IID_IMediaBuffer || riid == IID_IUnknown) {
*ppv = static_cast<IMediaBuffer *>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) Release()
{
LONG lRef = InterlockedDecrement(&m_cRef);
if (lRef == 0) {
deletethis;
// m_cRef is no longer valid! Return lRef.
}
return lRef;
}

// Методы IMediaBuffer
STDMETHODIMP SetLength(DWORD cbLength)
{
if (cbLength > m_cbMaxLength) {
return E_INVALIDARG;
} else {
m_cbLength = cbLength;
return S_OK;
}
}

STDMETHODIMP GetMaxLength(DWORD *pcbMaxLength)
{
if (pcbMaxLength == NULL) {
return E_POINTER;
}
*pcbMaxLength = m_cbMaxLength;
return S_OK;
}

STDMETHODIMP GetBufferAndLength(BYTE **ppbBuffer, DWORD *pcbLength)
{
if (ppbBuffer == NULL || pcbLength == NULL) {
return E_POINTER;
}
*ppbBuffer = m_pbData;
*pcbLength = m_cbLength;
return S_OK;
}
};

Использование DMO в DirectShow

Приложения, основанные на DirectShow, могут использовать DMO в графе фильтров через оболочечные фильтр DMO (DMO wrapper filter). Этот фильтр включает (аггрегирует -aggregates) DMO и управляет всеми деталями использования DMO, такими как передача данных в и из DMO, выделение объектов IMediaBuffer и т.д.

Т.к. DMO аггрегируется фильтром, приложение может запросить у фильтра любой интерфейс, предоставляемый DMO. Но приложение должно разрешить фильтру управлять всеми потоковыми операциями на DMO. Например, не устанавливать медиатипы, обабатывать любые буфера, сбрасывать (flush) DMO, блокировать DMO, разрешать и запрещать контроль качества и устанавливать видеооптимизацию.

Если вы знаете идентификатор класса (CLSID) указанного DMO, который вы хотите использовать, вы можете инициализировать фильтр оболочки DMO этого DMO следующим образом:

  1. Вызвать CoCreateInstance для создания оболочечного DMO-фильтра.
  2. Запросить у оболочечного DMO-фильтра интерфейс IDMOWrapperFilter.
  3. Вызвать метод IDMOWrapperFilter::Init. Указать CLSID для DMO и добавить GUID категории DMO. Список категорий DMO указан в соответствующем списке.

Следующий код демонстрирует эти шаги:

// Создаем оболочечный фильтр над DMO
IBaseFilter *pFilter;
HRESULT hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
reinterpret_cast<void**>(&pFilter));

if (SUCCEEDED(hr))
{
// Запрашиваем интерфейс IDMOWrapperFilter
IDMOWrapperFilter *pDmoWrapper;
hr = pFilter->QueryInterface(IID_IDMOWrapperFilter,
reinterpret_cast<void**>(&pDmoWrapper));

if (SUCCEEDED(hr))
{
// Инициализируем фильтр
hr = pDmoWrapper->Init(CLSID_MyDMO, DMOCATEGORY_VIDEO_EFFECT);
pDmoWrapper->Release();

if (SUCCEEDED(hr))
{
// Добавляем фильтр к графу
hr = pGraph->AddFilter(pFilter, L"My DMO");
}
}
pFilter->Release();
}

Функция DMOEnum перечисляет DMO из реестра. Эта функция использует пазличные наборы категорий GUID'ов из него, которые используются фильтрами DirectShow.

Вместо того, чтобы создавать DMO напрямую, можно использовать системный перечислитель устройств (System Device Enumerator), который может перечислять любую категорию DMO, поддерживаемую методом DMOEnum. Системный перечислитель устройств включает также DMO, которые перечисляются исключительно категориями фильтров DirectShow (не совсем въехал). Следующая таблица показывает отображение между категориями DMO и категориями DirectShow.

Категория DMOКатегория DirectShow
DMOCATEGORY_AUDIO_ENCODERCLSID_AudioCompressorCategory
DMOCATEGORY_AUDIO_DECODERCLSID_LegacyAmFilterCategory
DMOCATEGORY_VIDEO_ENCODERCLSID_VideoCompressorCategory

Системный перечислитель устройств возвращает список моникеров (псевдонимов, кличек) объектов. Если моникер представляет DMO, то метод IMoniker::BindToObject автоматически создает оболочечный фильтр DMO и инициализирует его с этим DMO. Так DMO становится невидимым для приложения. Для получения дополнительной информации об использовании системного перечислителя устройств см. раздел "Использования системного перечислителя устройств" в документации по DirectShow.

Существуют ограничения на использование DMO в DirectShow:

  • Оболочечный фильтр DMO не поддерживает DMO с нулевым количеством входов, множественными выходами или нулевым количеством выходов.
  • Все контактные соединения оболочечного DMO фильтра используют интерфейс IMemInputPin.
  • Текущая версия DES не поддерживает эффекты и сдвиги, базирующиеся на DMO.

Создание DMO

В этом разделе рассказывается о том, как писать DMO. Перед чтением этого раздела нужно почитать раздел "Использование DMO" для понимания функционирования DMO с точки зрения клиента.

Минимальные требования для DMO

Все DMO должны соответствовать следующим минимальным требованиям:

  • Они должны поддерживать аггрегацию.
  • Они должны предоставлять интерфейс IMediaObject.
  • Потоковая модель должна быть 'both'. DMO должны корректно функционировать в свободно-потоковой среде.

DMO для фудиоэффектов должны поддерживать интерфейс IMediaObjectInPlace для использования в DirectMusic и DirectSound.

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

  • ISpecifyPropertyPages, IPropertyPage: Эти интерфейсы позволяют DMO предоставлять страницы свойств для пользователей для установки свойств.
  • IPersistStream: Этот интерфейс позволяет DMO сохранять свое состояние в хранилище.
  • IAMStreamConfig, IAMVideoCompression: Эти интерфейсы позволяют клиенту конфигурировать исходящий формат кодека и установки сжатия. (Эти два интерфейса есть частью DirectShow API, но они рекомендуются и для DMO.)

Медиатипы DMO

Медиатип описывает связь формата с потоком медиаданных. Эта статья описывает работу DMO с медиатипами. Она касается, в первую очередь, разработчиков, которые пишут свои собственные DMO.

Медиатипы определяют использованием структуры DMO_MEDIA_TYPE. Она включает следующую информацию:

  • major type - глобальный уникальный идентификатор (GUID), который определяет общую категорию, такую, как аудио или видео.
  • subtype - это GUID, который определяет более специфические аспекты типа. Например, для видео подтип включает 16-битный RGB, 24-битный RGB, UYVY, DV-кодированное видео и т.д.
  • Форматирующий блок (format block) - вторая структура, полностью определяющая формат. Разметка форматирующего блока зависит от типа данных. Например, аудио PCM использует структуру WAVEFORMATEX. Видео использует различные другие структуры, включая VIDEOINFOHEADER и VIDEOINFOHEADER2. Разметка форматирующего блока идентифицируется форматным типом GUID. Например, FORMAT_WaveFormatEx специфицирует структуру WAVEFORMATEX.

Когда DMO только создается, поток не имеет медиатипа. До того, как DMO сможет обрабатывать данные, клиент должен установить медиатип для каждого потока. Этот процесс с точки зрения клиента описывался выше в разделе Установка медиатипа на DMO.

DMO может добавить список медиатипов, которые он поддерживает, в регистр, вызовом функции DMORegister. Приложение может использовать эту информацию для поиска таких DMO, которые соответствуют частичному формату. Информация в регистре вполне может и не быть исчерпывающей. Обычно туда включены только главные типы, поддерживаемые DMO. Регистр имеет отдельные ключи для входящих и исходящих типов, но не имеет различного числа индивидуальных потоков.

Функция DMORegister использует структуру DMO_PARTIAL_MEDIATYPE для описания медиатипа. Эта структура содержит подмножество информации, находящейся в структуре DMO_MEDIA_TYPE, а именно главный тип и подтип. Она не включает блок формата, поскольку последний обычно содержит слишком гранулированную информацию для включения ее в регистр, такую, как ширина и высота видеоизображения.

После того, как приложение создаст DMO, оно может запросиь DMO о поддерживаемых им медиатипах. DMO создает список медиатипов (возможно, пустой) для каждого потока, упорядоченных в порядке предпочтения. Методы IMediaObject::GetInputType и IMediaObject::GetOutputType перечисляют предпочитаемые типы. Предпочитаемые типы потоков могут динамически изменяться, когда приложение устанавливает медиатипы на другие потоки. Например, список предпочитаемых исходящих типов может измениться после установки входящего типа и наоборот. Но, однако, DMO не требует динамического обновления этих предпочитаемых типов. Приложение не может предполагать, что каждый тип есть верным. Для этого методы IMediaObject::SetInputType и IMediaObject::SetOutputType поддерживают флаг для тестирования указанного типа.

Оба метода - и GetInputType, и GetOutputType возвращают структуру DMO_MEDIA_TYPE. DMO может предоставить некоторую информацию в этом структурном бланке для указания набора типов. Главный тип и подтип могут быть равными GUID_NULL, и форматирующий блок может быть пустым (ноль байт). Если форматирующий блок пуст, тип формата должен быть GUID_NULL.

Comments