Введение в Media Foundation — понимание API через призму COM
· Го Комура · Media Foundation, COM, C++, Разработка Windows
Когда начинаешь работать с Media Foundation, легко возникает ощущение: «вроде бы я использую видео- и аудио-API Windows, но вдруг стало слишком много разговоров о COM». CoInitializeEx, MFStartup, IMFSourceReader, IMFMediaType, IMFTransform, IMFActivate, HRESULT, GUID — всё это появляется разом, атмосфера резко становится похожа на Win32/COM, и понять, что же такое Media Foundation, становится труднее.
Эта статья не пытается охватить весь Media Foundation, как словарь, — она сосредоточена на трёх вопросах.
- почему при использовании Media Foundation разговор о COM возникает естественным образом
- в каких местах COM-окраска становится наиболее насыщенной
- с чего начать в первую очередь — с Source Reader / Sink Writer / Media Session / MFT
Примеры кода даны на C++, но сам подход в целом одинаков и при работе через обёртки, например, из .NET.
Содержание
- Сначала вывод (в двух словах)
- Ориентировочные таблицы для начала
- 2.1. Что использовать в зависимости от задачи
- 2.2. Где проявляется COM-лицо
- 2.3. Термины, значение которых стоит усвоить заранее
- Общая картина Media Foundation (диаграмма)
- Места, где Media Foundation проявляет COM-лицо
- 4.1.
CoInitializeExиMFStartupидут рядом при инициализации - 4.2. Передача объектов построена вокруг интерфейсов
- 4.3. Появляются Activation Object
- 4.4. Настройки и информация о типах строятся вокруг
IMFAttributesи GUID - 4.5. Асинхронность, обратные вызовы и потоки тоже обрабатываются в духе COM
- 4.6. Но Media Foundation ≠ COM
- 4.1.
- Краткое руководство по выбору
- 5.1. Случай, когда стоит начать с Source Reader
- 5.2. Для записи в файл — Sink Writer
- 5.3. Для воспроизведения и синхронизации — Media Session
- 5.4. Для встраивания собственных компонентов — MFT
- Практический чек-лист
- Фрагменты кода
- 7.1. Инициализация
- 7.2. Создание Source Reader в синхронном режиме
- 7.3. Создание Source Reader в асинхронном режиме
- 7.4. Перечисление и создание MFT через
MFTEnumEx
- Итог
- Источники
1. Сначала вывод (в двух словах)
- Media Foundation — это платформа для работы с видео и аудио, и API целиком не является просто чистым COM
- Однако границы между source, transform, sink, activation, attributes и callback выражены через COM-интерфейсы, поэтому при использовании естественным образом возникают темы
IUnknown,HRESULT, GUID и apartment - Проще всего структурировать освоение так: начать с Source Reader / Sink Writer, перейти к Media Session, когда понадобится контроль воспроизведения, и к MFT, когда понадобится собственный преобразователь
Иными словами, Media Foundation — это платформа обработки медиаданных, в границы которой глубоко встроен COM.
Если усвоить это заранее, становится намного понятнее, «почему она вдруг проявляет COM-лицо».
2. Ориентировочные таблицы для начала
2.1. Что использовать в зависимости от задачи
Если сначала посмотреть на эту таблицу, точку входа выбрать проще.
| Что нужно сделать | Что использовать в первую очередь | Насыщенность COM | Примечание |
|---|---|---|---|
| Получить кадры / сэмплы из файла или камеры | Source Reader | Средняя | При необходимости сам позаботится и о decoder |
| Записать сгенерированное аудио / видео в файл | Sink Writer | Средняя | При необходимости объединяет работу encoder и media sink |
| Обрабатывать воспроизведение, остановку, перемотку, синхронизацию A/V, контроль качества | Media Session | Высокая | Требует понимания topology и session |
| Встроить собственный преобразователь или codec-подобный компонент | MFT | Высокая | Рассуждать вокруг IMFTransform |
| Сначала просмотреть перечисленные кандидаты, а затем создать только нужные | IMFActivate |
Высокая | Возвращается не сам объект, а иногда activation object |
2.2. Где проявляется COM-лицо
| Точка | Что появляется | Что важно понять в первую очередь |
|---|---|---|
| Инициализация | CoInitializeEx, MFStartup |
Инициализация COM и инициализация Media Foundation — это разные вещи |
| Создание и передача объектов | IMFSourceReader, IMFMediaType, IMFTransform |
В основном это указатели на интерфейсы + HRESULT |
| Настройки | IMFAttributes, GUID |
Значения настроек и информация о типах выражены как key/value + GUID |
| Перечисление / отложенное создание | IMFActivate, ActivateObject |
Результат перечисления не всегда сам целевой объект |
| Асинхронность | IMFSourceReaderCallback, work queue |
Нужно учитывать обратные вызовы и apartment |
| Контроль воспроизведения | Topology, Media Session | Общий поток конвейера — специфичное для Media Foundation понятие |
2.3. Термины, значение которых стоит усвоить заранее
| Термин | Значение здесь |
|---|---|
| Media Source | Точка входа, подающая медиаданные в конвейер: файл, сеть, устройство захвата и т. п. |
| MFT | Media Foundation Transform. Общая модель для декодеров, энкодеров, видеопреобразователей и т. п. |
| Media Sink | Место назначения медиаданных: показ на экране, аудиовывод, запись в файл и т. п. |
| Media Session | Механизм, управляющий потоком всего конвейера; отвечает за воспроизведение и синхронизацию |
| Topology | Схема соединений, описывающая, как связаны source / transform / sink |
| Activation Object | Вспомогательный объект для создания реального объекта позже; представлен через IMFActivate |
| Attributes | Хранилище key/value с ключами-GUID; активно используется во всём Media Foundation |
Если заранее иметь эти термины в словарном запасе, чтение документации станет заметно менее спотыкающимся.
3. Общая картина Media Foundation (диаграмма)
В целом Media Foundation — это рассказ о медиаконвейере. Тема COM важна, но упорядочить понимание проще, если сначала посмотреть на общую картину.
flowchart TB
subgraph Pipeline["Модель использования всего конвейера"]
Source1["Media Source"] --> Transform1["MFT"]
Transform1 --> Sink1["Media Sink"]
Session["Media Session"] --- Source1
Session --- Transform1
Session --- Sink1
end
subgraph Direct["Модель, где приложение обрабатывает данные напрямую"]
Source2["Media Source"] --> Reader["Source Reader (+ decoder)"]
App["Приложение"] --> Writer["Sink Writer (+ encoder)"]
Writer --> Sink2["Media Sink"]
end
В общих чертах у Media Foundation есть два способа использования.
- Модель использования всего конвейера
- вы соединяете source / transform / sink, а Media Session управляет потоком данных и синхронизацией A/V
- Модель, где приложение обрабатывает данные напрямую
- вы извлекаете данные из source через Source Reader и подаёте их в sink через Sink Writer
Второй вариант проще, когда хочется самостоятельно обрабатывать кадры и сэмплы. С другой стороны, если хочется доверить платформе воспроизведение и синхронизацию, правильный путь — первый.
Стоит зафиксировать: суть Media Foundation — это платформа обработки медиаданных, и ощущение от неё немного отличается от прямой работы с набором COM-объектов.
Однако как только начинаешь рассматривать границы между её компонентами, COM-лицо резко становится насыщеннее. В следующей главе эти точки рассматриваются по порядку.
4. Места, где Media Foundation проявляет COM-лицо
4.1. CoInitializeEx и MFStartup идут рядом при инициализации
Именно здесь у многих впервые возникает ощущение странности. Раньше разговора об открытии файла или захвате с камеры сначала появляются CoInitializeEx и MFStartup.
CoInitializeExинициализирует библиотеку COMMFStartupинициализирует платформу Media Foundation
То есть одной лишь инициализации COM недостаточно, требуется ещё и инициализация со стороны Media Foundation. Здесь становится понятно: «это не просто видео-API — внутри заложен весьма основательный контракт на базе COM».
На практике полезно решить в этот момент следующее.
- какой поток использует Media Foundation
- будет ли этот поток STA или MTA
- кто несёт ответственность за
MFStartup/MFShutdownиCoInitializeEx/CoUninitialize
Если продвигаться дальше, оставив это проектирование расплывчатым, позже станет труднее разобраться с обратными вызовами и интеграцией с UI.
4.2. Передача объектов построена вокруг интерфейсов
Если читать API Media Foundation дальше, окажется, что большинство возвращаемых значений и out-параметров — это COM-интерфейсы.
IMFSourceReaderIMFMediaTypeIMFTransformIMFActivateIMFSampleIMFMediaBuffer
Характерная особенность в том, что интерфейсами представлены не только сами данные, но и информация о типах, и объекты настроек.
Например:
IMFTransform— интерфейс, представляющий MFTIMFAttributes— хранилище key/valueIMFMediaTypeнаследуетIMFAttributesи представляет собой «описание формата медиаданных»
То есть даже нечто вроде «данных настроек», как media type, хранится через COM-интерфейс. Здесь естественным образом появляется контекст IUnknown, QueryInterface, AddRef / Release и HRESULT.
flowchart TD
IUnknown["IUnknown"]
IUnknown --> IMFAttributes["IMFAttributes"]
IMFAttributes --> IMFMediaType["IMFMediaType"]
IMFAttributes --> IMFActivate["IMFActivate"]
IUnknown --> IMFSourceReader["IMFSourceReader"]
IUnknown --> IMFTransform["IMFTransform"]
Дойдя до этого места, начинаешь видеть: «Media Foundation — это медиа-API, но способ выражения границ весьма COM-подобен».
4.3. Появляются Activation Object
COM-подобность Media Foundation особенно проявляется в activation object.
IMFActivate — это вспомогательный объект для создания реального объекта позже. Интуитивно проще всего воспринимать его как нечто близкое к class factory в COM.
В сценариях, где он появляется, возвращаемое значение API перечисления может быть не «сразу готовым к использованию объектом», а сначала массивом IMFActivate*.
Затем через ActivateObject создаётся только то, что действительно нужно.
sequenceDiagram
participant App as Приложение
participant Enum as API перечисления
participant Act as IMFActivate
participant Obj as IMFTransform / sink и т. п.
App->>Enum: Вызывает перечисление
Enum-->>App: Массив IMFActivate*
App->>Act: Проверяет атрибуты
App->>Act: ActivateObject(...)
Act-->>App: Реальный COM-объект
Такая форма хорошо согласуется с тем, что Media Foundation спроектирован так, чтобы находить заменяемые компоненты позже и комбинировать их.
Кроме того, поскольку сам activation object может нести атрибуты, часто складывается поток: «сначала посмотреть атрибуты кандидата», «при необходимости настроить», «создать реальный объект позже». Это тоже весьма COM-подобно.
4.4. Настройки и информация о типах строятся вокруг IMFAttributes и GUID
При работе с Media Foundation есть момент, когда настройки внезапно начинают казаться сплошными GUID. В центре этого — IMFAttributes, хранилище key/value с ключами-GUID. Оно очень активно используется во всём Media Foundation.
Особенно важен IMFMediaType, который наследует IMFAttributes и хранит информацию о формате медиаданных как атрибуты.
Например, такую информацию:
- major type (аудио или видео)
- subtype (H.264, AAC, RGB32, PCM и т. п.)
- размер кадра
- частоту кадров
- частоту дискретизации
- число каналов
flowchart LR
MediaType["IMFMediaType"] --> Major["MF_MT_MAJOR_TYPE"]
MediaType --> Subtype["MF_MT_SUBTYPE"]
MediaType --> Detail["Размер / FPS / частота дискретизации и т. п."]
Это легко воспринять как «лес из GUID», но на деле происходящее довольно простое.
- настройки хранятся через хранилище атрибутов
- media type тоже представлен как хранилище атрибутов
- между source, transform и sink формат согласовывается путём просмотра этих атрибутов
Речь просто о том, что для выражения настроек и информации о типах используются COM-подобные интерфейсы и GUID.
4.5. Асинхронность, обратные вызовы и потоки тоже обрабатываются в духе COM
В реальной работе с Media Foundation легко упустить из виду асинхронную обработку и потоковую модель.
Например, Source Reader по умолчанию работает в синхронном режиме. В синхронном режиме ReadSample блокирует поток.
В зависимости от состояния файла, сети или устройства это ожидание может стать заметным по времени.
Чтобы перейти в асинхронный режим, при создании Source Reader передают callback.
Порядок такой: подготовить объект, реализующий IMFSourceReaderCallback, установить его в атрибут MF_SOURCE_READER_ASYNC_CALLBACK, а затем создать reader.
Ещё несколько более важен вопрос apartment. Асинхронная обработка Media Foundation использует work queue, а потоки work queue относятся к MTA. Поэтому если и сторону приложения склонить к MTA, реализация упрощается.
sequenceDiagram
participant App as Поток приложения
participant Reader as Source Reader
participant Queue as MF work queue (MTA)
participant Cb as IMFSourceReaderCallback
App->>Reader: ReadSample(...)
Reader-->>App: Возвращается немедленно
Reader->>Queue: Обрабатывает внутри
Queue->>Cb: OnReadSample(...)
Вокруг обратных вызовов стоит обратить внимание на такие моменты.
- не трогать STA-объекты UI-потока напрямую со стороны callback
- делать реализацию callback потокобезопасной
- если нужно обновление UI, передавать в UI-поток только результат
- заранее зафиксировать в голове, «с какого потока приходят обратные вызовы Media Foundation»
Media Foundation не берёт на себя автоматическое улаживание особенностей STA-объектов. Поэтому проще держать порядок, если склонить воркер, использующий Media Foundation, к MTA и явно построить мост к UI.
4.6. Но Media Foundation ≠ COM
Дочитав до этого места, легко подумать: «в итоге Media Foundation — это просто COM». Но это не совсем так.
В Media Foundation есть понятия, специфичные для платформы, которые общими рассуждениями о COM не объяснить.
MFStartup/MFShutdown- Media Session
- Topology
- Topology loader
- Presentation clock
- Source Reader / Sink Writer
Всё это — собственная роль Media Foundation: как прогонять медиаконвейер.
Например, в Media Session, когда приложение передаёт partial topology, topology loader дополняет её нужными transform и разрешает в full topology. Это не общий разговор о COM, а функциональность, которой Media Foundation обладает именно как платформа обработки медиаданных.
flowchart LR
Partial["Частичная топология<br/>Source -> Output"] --> Loader["Topology Loader"]
Loader --> Full["Полная топология<br/>Source -> Decoder MFT -> Output"]
Media Foundation использует COM для выражения контрактов между компонентами и поверх этого работает как платформа обработки медиаданных. Если рассматривать это в две ступени, заблудиться становится труднее.
5. Краткое руководство по выбору
При выборе первой точки входа часто достаточно этой схемы.
flowchart TD
Start["Что нужно сделать"] --> Q1{"Что нужно в первую очередь?"}
Q1 -- "Прочитать кадры / сэмплы" --> A1["Source Reader"]
Q1 -- "Записать в файл" --> A2["Sink Writer"]
Q1 -- "Нужен контроль воспроизведения или синхронизация A/V" --> A3["Media Session"]
Q1 -- "Встроить собственный преобразователь" --> A4["MFT"]
5.1. Случай, когда стоит начать с Source Reader
Source Reader — весьма удобная точка входа, когда нужно извлекать данные из файлов или устройств.
Он подходит, например, для таких случаев.
- получить кадры из видеофайла
- декодировать аудиофайл и получить сэмплы
- получить кадры с камеры
- подключить source Media Foundation к собственному конвейеру обработки
Source Reader при необходимости загружает decoder и передаёт данные приложению. При этом он не берёт на себя управление presentation clock, синхронизацию A/V и саму отрисовку на экране.
Проще всего воспринимать его как точку входа не для «воспроизведения», а для «получения данных».
5.2. Для записи в файл — Sink Writer
Sink Writer — точка входа, когда нужно записать аудио или видео в файл.
Типичные варианты применения такие.
- сохранить сгенерированные кадры в видеофайл
- закодировать и записать аудиосэмплы
- преобразовать прочитанные данные в другой формат и сохранить
Sink Writer при необходимости находит и загружает encoder, а также управляет потоком данных к media sink. Его часто сочетают с Source Reader, но оба компонента независимы, поэтому использовать их обязательно вместе не нужно.
5.3. Для воспроизведения и синхронизации — Media Session
Если цель не «получить данные из файла», а полноценно воспроизвести, естественнее выстраивать логику вокруг Media Session.
Черёд Media Session наступает при таких требованиях.
- нужно обрабатывать воспроизведение / остановку / перемотку
- нужно доверить синхронизацию аудио и видео платформе
- нужно управлять конвейером, включая контроль качества и смену формата
- нужно строить поток source / transform / sink через topology
Войдя в этот слой, вы приближаетесь к «самому Media Foundation» сильнее, чем при работе с Source Reader / Sink Writer. Соответственно, растёт и число специфичных для Media Foundation понятий — topology, session event и т. д.
5.4. Для встраивания собственных компонентов — MFT
MFT — общая модель transform в Media Foundation.
Сюда переходят в таких ситуациях.
- нужно создать собственный декодер или энкодер
- нужно встроить в конвейер компонент обработки видео или аудио
- нужно перечислить codec’и или преобразователи и выбрать самостоятельно
- нужен более глубокий контроль, чем стандартное автоматическое разрешение
В мире MFT COM-подобные контракты выходят на первый план весьма сильно: IMFTransform, IMFActivate, media type negotiation, управление сэмплами и буферами.
Поэтому понятнее не заходить сразу в MFT как в первую точку входа, а сначала определить, что из Source Reader / Sink Writer / Media Session действительно необходимо.
6. Практический чек-лист
Напоследок соберём в одной таблице то, что стоит проверить в первую очередь на практике.
| Пункт | Что проверять | Что часто случается, если упустить |
|---|---|---|
| Ответственность за инициализацию | Решить, где вызываются CoInitializeEx и MFStartup и кто отвечает за завершение |
Пропуски инициализации, путаница в порядке завершения |
| Apartment | Заранее решить, будет ли поток, работающий с MF, STA или MTA | Путаница вокруг обратных вызовов, конфликты с UI |
| Режим Source Reader | Решить синхронный или асинхронный режим при создании | ReadSample блокирует неожиданно, переключить позже нельзя |
| Согласование media type | Перечислить выходные форматы и явно указать используемый | MF_E_INVALIDMEDIATYPE, приходит не тот формат, что ожидался |
| Время жизни объектов | Чётко определить ответственность за Release, Unlock, ShutdownObject |
Утечки памяти, удержание буферов, несогласованность при завершении |
| Activation object | Различать, является ли результат перечисления самим объектом или IMFActivate |
Ошибка из-за предположения, что QueryInterface сработает |
| Topology | Понимать, с частичной или полной topology вы работаете | Затор из-за предположения «должно соединиться автоматически» |
| Проверка ошибок | Каждый раз проверять HRESULT, stream flags, события |
Пропуск частичного сбоя |
| Интеграция с UI | Не трогать UI напрямую из callback, передавать в UI-поток только результат | Зависания, гонки, труднообъяснимые дефекты |
Особенно высокий приоритет имеют следующие три пункта.
- Не ошибиться с первой точкой входа API
- сначала определить, что из Source Reader / Sink Writer / Media Session действительно необходимо
- Заранее решить вопрос apartment
- если STA UI и work queue Media Foundation будут смешиваться, сначала определить способ наведения моста
- Не относиться небрежно к согласованию media type
- если продвигаться исходя из «наверное, это тот формат», позже это станет весьма запутанным
7. Фрагменты кода
Здесь приводятся не полные примеры, а лишь фрагменты, достаточные для того, чтобы понять, где именно проявляется COM-лицо.
7.1. Инициализация
template <class T>
void SafeRelease(T** pp)
{
if (pp != nullptr && *pp != nullptr)
{
(*pp)->Release();
*pp = nullptr;
}
}
HRESULT InitializeMediaFoundationForCurrentThread()
{
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr))
{
return hr;
}
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
CoUninitialize();
return hr;
}
return S_OK;
}
void UninitializeMediaFoundationForCurrentThread()
{
MFShutdown();
CoUninitialize();
}
Здесь CoInitializeEx и MFStartup стоят рядом.
Это первая точка, где при работе с Media Foundation атмосфера COM резко становится насыщеннее.
В реальной реализации инициализацией COM иногда уже занимается другой слой. Даже тогда безопаснее заранее зафиксировать, кто несёт эту ответственность.
7.2. Создание Source Reader в синхронном режиме
HRESULT ReadOneVideoSample(PCWSTR path)
{
IMFSourceReader* pReader = nullptr;
IMFMediaType* pType = nullptr;
IMFSample* pSample = nullptr;
HRESULT hr = MFCreateSourceReaderFromURL(path, nullptr, &pReader);
if (FAILED(hr)) goto done;
hr = MFCreateMediaType(&pType);
if (FAILED(hr)) goto done;
hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
if (FAILED(hr)) goto done;
hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
if (FAILED(hr)) goto done;
hr = pReader->SetCurrentMediaType(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
nullptr,
pType);
if (FAILED(hr)) goto done;
DWORD streamFlags = 0;
LONGLONG timestamp = 0;
hr = pReader->ReadSample(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
nullptr,
&streamFlags,
×tamp,
&pSample);
if (FAILED(hr)) goto done;
// Извлекаем IMFMediaBuffer из pSample и обрабатываем
done:
SafeRelease(&pSample);
SafeRelease(&pType);
SafeRelease(&pReader);
return hr;
}
Здесь видны такие моменты.
- и reader, и media type — это COM-интерфейсы
- настройки строятся на основе GUID
- возвращаемое значение —
HRESULT - в синхронном режиме
ReadSampleблокирует поток
Даже когда хочется «просто прочитать один кадр», на границе Media Foundation это оборачивается весьма COM-подобным лицом.
7.3. Создание Source Reader в асинхронном режиме
HRESULT CreateSourceReaderAsync(
PCWSTR path,
IMFSourceReaderCallback* pCallback,
IMFSourceReader** ppReader)
{
IMFAttributes* pAttributes = nullptr;
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if (FAILED(hr))
{
return hr;
}
hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, pCallback);
if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromURL(path, pAttributes, ppReader);
}
SafeRelease(&pAttributes);
return hr;
}
Здесь, чтобы включить асинхронный режим, callback помещается в атрибуты ещё до создания reader.
То есть:
- сам callback — это COM-интерфейс
- настройка асинхронности идёт через
IMFAttributes - режим определяется в момент создания
На практике важно сделать реализацию IMFSourceReaderCallback потокобезопасной и не привносить в неё напрямую объекты UI.
7.4. Перечисление и создание MFT через MFTEnumEx
HRESULT FindH264Decoder(IMFTransform** ppTransform)
{
*ppTransform = nullptr;
IMFActivate** ppActivate = nullptr;
UINT32 count = 0;
MFT_REGISTER_TYPE_INFO inputType = {};
inputType.guidMajorType = MFMediaType_Video;
inputType.guidSubtype = MFVideoFormat_H264;
HRESULT hr = MFTEnumEx(
MFT_CATEGORY_VIDEO_DECODER,
MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT,
&inputType,
nullptr,
&ppActivate,
&count);
if (FAILED(hr))
{
return hr;
}
if (count == 0)
{
CoTaskMemFree(ppActivate);
return MF_E_TOPO_CODEC_NOT_FOUND;
}
hr = ppActivate[0]->ActivateObject(
__uuidof(IMFTransform),
reinterpret_cast<void**>(ppTransform));
for (UINT32 i = 0; i < count; ++i)
{
ppActivate[i]->Release();
}
CoTaskMemFree(ppActivate);
return hr;
}
Здесь результат перечисления возвращается не сразу как IMFTransform*, а как IMFActivate**.
И только вызвав ActivateObject, вы наконец получаете реальный IMFTransform.
Этот поток весьма хорошо передаёт то ощущение, что Media Foundation «вдруг проявляет COM-лицо».
8. Итог
То, что при работе с Media Foundation резко увеличивается число разговоров о COM, — не случайность.
- Media Foundation — это платформа обработки медиаданных
- её границы — source / transform / sink / activation / callback и т. п. — выражены через COM-интерфейсы
- поэтому естественным образом возникают темы
IUnknown,HRESULT, GUID, apartment, callback - однако суть Media Foundation — это медиаконвейер с Media Session и topology, а не просто переработка COM
На практике структурировать понимание проще, если рассуждать в таком порядке.
- сначала определить, что вообще нужно — Source Reader / Sink Writer / Media Session / MFT
- заранее решить политику apartment и обратных вызовов
- аккуратно обращаться с согласованием media type и временем жизни объектов
Не обязательно пытаться понять всё сразу с самого начала. Если для начала держать в голове, что «Media Foundation — платформа обработки медиаданных, а COM глубоко встроен в её границы», и документацию, и код станет заметно легче отслеживать.
9. Источники
- Media Foundation and COM - Microsoft Learn
- Overview of the Media Foundation Architecture - Microsoft Learn
- Initializing Media Foundation - Microsoft Learn
- Source Reader - Microsoft Learn
- Using the Source Reader to Process Media Data - Microsoft Learn
- Using the Source Reader in Asynchronous Mode - Microsoft Learn
- Sink Writer - Microsoft Learn
- Activation Objects - Microsoft Learn
- About Topologies - Microsoft Learn
- IMFAttributes interface - Microsoft Learn
- IMFMediaType interface - Microsoft Learn
- IMFTransform interface - Microsoft Learn
- MFTEnumEx function - Microsoft Learn
-
[Основы COM STA/MTA — модели потоков и как избежать зависаний KomuraSoft Blog](https://comcomponent.com/ru/blog/2026/01/31/000-sta-mta-com-relationship/) -
[Вызов нативных DLL из C#: обёртка на C++/CLI или P/Invoke KomuraSoft Blog](https://comcomponent.com/ru/blog/2026/03/07/000-cpp-cli-wrapper-for-native-dlls/)
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Как встроить изображение и текст в кадры MP4 с помощью Media Foundation
В статье разбирается, как средствами Media Foundation встроить изображение и текст в каждый кадр MP4-видео и получить новый MP4: распреде...
Как преобразовать YUV в RGB с помощью Media Foundation
Разбираем способы преобразования YUV-кадров в RGB в Media Foundation: автоматическое преобразование через Source Reader и самостоятельное...
Как извлечь статичное изображение из MP4 по заданному времени с помощью Media Foundation
Как с помощью Source Reader извлечь из MP4 кадр, ближайший к заданному времени, привести в порядок stride и alpha-байт RGB32 и сохранить ...
Обратная совместимость интерфейсов DLL и COM — таблица решений: какие изменения ломают вызывающую сторону
Какие изменения DLL или COM-компонента на самом деле ломают вызывающую сторону? Разбираем три слоя совместимости — бинарную, исходную и п...
Работают ли бизнес-приложения на Windows на Arm — реальность x64-эмуляции (Prism) и нативных DLL/COM
Отвечаем разработчикам и ИТ-специалистам на вопрос «заработает ли наше бизнес-приложение на Windows на Arm». Разбираем принцип работы x64...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Миграция ActiveX
Решения о сохранении, обёртке или замене компонентов COM / ActiveX / OCX.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Обработка медиаданных в Windows с использованием Media Foundation, COM и HRESULT близка к тем темам реализации, которые мы ведём как разработку Windows-приложений.
Технические консультации и ревью дизайна
Если сначала нужно разобраться с COM-подобными границами и порядком инициализации, к этому можно подойти со стороны проектирования — как к технической консультации с ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Что такое Media Foundation? Чем это отличается от COM?
- Media Foundation — это платформа обработки медиаданных для работы с видео и аудио в Windows, и API целиком не является просто чистым COM. Однако границы между такими компонентами, как source, transform, sink, activation, attributes и callback, выражены через COM-интерфейсы, поэтому при использовании естественным образом возникают темы IUnknown, HRESULT, GUID и apartment. Точнее всего воспринимать это так: «платформа обработки медиаданных, в границы которой глубоко встроен COM».
- Почему нужны и MFStartup, и CoInitializeEx?
- Потому что у них разные роли. CoInitializeEx инициализирует библиотеку COM, а MFStartup — платформу Media Foundation. Одной лишь инициализации COM недостаточно, требуется ещё и инициализация со стороны Media Foundation. На практике, если заранее решить, какой поток использует Media Foundation, будет ли он STA или MTA, и кто несёт ответственность за MFStartup / MFShutdown и CoInitializeEx / CoUninitialize, дальнейшая работа с обратными вызовами и интеграцией с UI становится проще.
- Как выбирать между Source Reader, Sink Writer, Media Session и MFT?
- Если нужно извлекать кадры или сэмплы из файла или камеры, точка входа — Source Reader; если нужно записывать сгенерированное аудио или видео в файл — Sink Writer. Если хочется доверить платформе воспроизведение, остановку, перемотку, синхронизацию A/V и контроль качества, стоит выстраивать логику вокруг Media Session. Если нужно встроить в конвейер собственный декодер или преобразователь, переходят к MFT, но там на первый план выходят COM-подобные контракты, поэтому рекомендуется сначала определить, что из первых трёх действительно необходимо.
- На что обращать внимание в асинхронных обратных вызовах Media Foundation?
- Асинхронная обработка Media Foundation использует work queue, а её потоки — MTA, поэтому если склонить и сторону приложения к MTA, реализация упрощается. Важно сделать реализацию IMFSourceReaderCallback потокобезопасной и не трогать STA-объекты UI-потока напрямую из обратного вызова. Если нужно обновление UI, в UI-поток передают только результат. Также стоит учитывать, что режим (синхронный или асинхронный) Source Reader определяется при создании и позже переключить его нельзя.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки