Введение в 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.

Содержание

  1. Сначала вывод (в двух словах)
  2. Ориентировочные таблицы для начала
    • 2.1. Что использовать в зависимости от задачи
    • 2.2. Где проявляется COM-лицо
    • 2.3. Термины, значение которых стоит усвоить заранее
  3. Общая картина Media Foundation (диаграмма)
  4. Места, где 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
  5. Краткое руководство по выбору
    • 5.1. Случай, когда стоит начать с Source Reader
    • 5.2. Для записи в файл — Sink Writer
    • 5.3. Для воспроизведения и синхронизации — Media Session
    • 5.4. Для встраивания собственных компонентов — MFT
  6. Практический чек-лист
  7. Фрагменты кода
    • 7.1. Инициализация
    • 7.2. Создание Source Reader в синхронном режиме
    • 7.3. Создание Source Reader в асинхронном режиме
    • 7.4. Перечисление и создание MFT через MFTEnumEx
  8. Итог
  9. Источники

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 важна, но упорядочить понимание проще, если сначала посмотреть на общую картину.

Модель, где приложение обрабатывает данные напрямуюSource Reader (+ decoder)Media SourceПриложениеSink Writer (+ encoder)Media SinkМодель использования всего конвейераMFTMedia SourceMedia SinkMedia Session

В общих чертах у 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 инициализирует библиотеку COM
  • MFStartup инициализирует платформу Media Foundation

То есть одной лишь инициализации COM недостаточно, требуется ещё и инициализация со стороны Media Foundation. Здесь становится понятно: «это не просто видео-API — внутри заложен весьма основательный контракт на базе COM».

На практике полезно решить в этот момент следующее.

  • какой поток использует Media Foundation
  • будет ли этот поток STA или MTA
  • кто несёт ответственность за MFStartup / MFShutdown и CoInitializeEx / CoUninitialize

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

4.2. Передача объектов построена вокруг интерфейсов

Если читать API Media Foundation дальше, окажется, что большинство возвращаемых значений и out-параметров — это COM-интерфейсы.

  • IMFSourceReader
  • IMFMediaType
  • IMFTransform
  • IMFActivate
  • IMFSample
  • IMFMediaBuffer

Характерная особенность в том, что интерфейсами представлены не только сами данные, но и информация о типах, и объекты настроек.

Например:

  • IMFTransform — интерфейс, представляющий MFT
  • IMFAttributes — хранилище key/value
  • IMFMediaType наследует IMFAttributes и представляет собой «описание формата медиаданных»

То есть даже нечто вроде «данных настроек», как media type, хранится через COM-интерфейс. Здесь естественным образом появляется контекст IUnknown, QueryInterface, AddRef / Release и HRESULT.

IUnknownIMFAttributesIMFMediaTypeIMFActivateIMFSourceReaderIMFTransform

Дойдя до этого места, начинаешь видеть: «Media Foundation — это медиа-API, но способ выражения границ весьма COM-подобен».

4.3. Появляются Activation Object

COM-подобность Media Foundation особенно проявляется в activation object.

IMFActivate — это вспомогательный объект для создания реального объекта позже. Интуитивно проще всего воспринимать его как нечто близкое к class factory в COM.

В сценариях, где он появляется, возвращаемое значение API перечисления может быть не «сразу готовым к использованию объектом», а сначала массивом IMFActivate*. Затем через ActivateObject создаётся только то, что действительно нужно.

IMFTransform / sink и т. п.IMFActivateAPI перечисленияПриложениеIMFTransform / sink и т. п.IMFActivateAPI перечисленияПриложениеВызывает перечислениеМассив IMFActivate*Проверяет атрибутыActivateObject(...)Реальный 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 и т. п.)
  • размер кадра
  • частоту кадров
  • частоту дискретизации
  • число каналов
IMFMediaTypeMF_MT_MAJOR_TYPEMF_MT_SUBTYPEРазмер / 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, реализация упрощается.

IMFSourceReaderCallbackMF work queue (MTA)Source ReaderПоток приложенияIMFSourceReaderCallbackMF work queue (MTA)Source ReaderПоток приложенияReadSample(...)Возвращается немедленноОбрабатывает внутри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 обладает именно как платформа обработки медиаданных.

Частичная топологияSource -> OutputTopology LoaderПолная топологияSource -> Decoder MFT -> Output

Media Foundation использует COM для выражения контрактов между компонентами и поверх этого работает как платформа обработки медиаданных. Если рассматривать это в две ступени, заблудиться становится труднее.

5. Краткое руководство по выбору

При выборе первой точки входа часто достаточно этой схемы.

Прочитать кадры / сэмплыЗаписать в файлНужен контроль воспроизведения или синхронизация A/VВстроить собственный преобразовательЧто нужно сделатьЧто нужно в первую очередь?Source ReaderSink WriterMedia SessionMFT

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-поток только результат Зависания, гонки, труднообъяснимые дефекты

Особенно высокий приоритет имеют следующие три пункта.

  1. Не ошибиться с первой точкой входа API
    • сначала определить, что из Source Reader / Sink Writer / Media Session действительно необходимо
  2. Заранее решить вопрос apartment
    • если STA UI и work queue Media Foundation будут смешиваться, сначала определить способ наведения моста
  3. Не относиться небрежно к согласованию 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,
        &timestamp,
        &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

На практике структурировать понимание проще, если рассуждать в таком порядке.

  1. сначала определить, что вообще нужно — Source Reader / Sink Writer / Media Session / MFT
  2. заранее решить политику apartment и обратных вызовов
  3. аккуратно обращаться с согласованием media type и временем жизни объектов

Не обязательно пытаться понять всё сразу с самого начала. Если для начала держать в голове, что «Media Foundation — платформа обработки медиаданных, а COM глубоко встроен в её границы», и документацию, и код станет заметно легче отслеживать.

9. Источники

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

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

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Что такое 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, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог