Создание пользовательского источника мультимедиа

В этом разделе описывается, как реализовать пользовательский источник мультимедиа в Microsoft Media Foundation. Он содержит следующие подразделы:

Создание дескриптора презентации

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

  1. Создайте массив типов мультимедиа. Каждый тип мультимедиа в массиве представляет возможный формат для потока. Дополнительные сведения о создании типов мультимедиа см. в разделе Типы мультимедиа.
  2. Вызовите MFCreateStreamDescriptor , чтобы создать дескриптор потока. Передайте массив типов мультимедиа. Функция возвращает указатель IMFStreamDescriptor .
  3. Вызовите IMFStreamDescriptor::GetMediaTypeHandler , чтобы получить обработчик типа мультимедиа дескриптора потока.
  4. Вызовите IMFMediaTypeHandler::SetCurrentMediaType , чтобы задать формат потока по умолчанию. Используйте один из типов мультимедиа, созданных на шаге 1. Как правило, следует использовать формат с наивысшим качеством.
  5. При необходимости задайте атрибуты для дескриптора потока. Список атрибутов, которые применяются к дескрипторам потоков, см. в разделе Атрибуты дескриптора потока.

Теперь создайте дескриптор презентации:

  1. Вызовите MFCreatePresentationDescriptor и передайте массив дескрипторов потока. Функция возвращает указатель IMFPresentationDescriptor .
  2. Выберите поток по умолчанию, вызвав IMFPresentationDescriptor::SelectStream , чтобы выбрать один или несколько потоков. В конфигурации по умолчанию должен быть выбран по крайней мере один поток.
  3. При необходимости задайте атрибуты в дескрипторе презентации. Список атрибутов, которые применяются к дескрипторам потоков, см. в разделе Атрибуты дескриптора презентации.

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

Запуск источника мультимедиа

Метод IMFMediaSource::Start запускает источник мультимедиа или ищет новую позицию. Вызов метода Start вызывает поиск , когда предыдущее состояние было приостановлено или запущено, а также указано новое время начала. В противном случае метод Start вызывает запуск. После завершения операции Запуска отправьте следующие события.

  1. Отправьте событие MENewStream для каждого нового потока, то есть для каждого потока, который ранее был отменен и теперь выбран. Данные события являются указателем на поток.
  2. Отправьте событие MEUpdatedStream для каждого потока, который был ранее выбран и по-прежнему выбран. Данные события являются указателем на поток. (Не отправляйте событие для отмененных потоков.)
  3. Если источник ищет, отправьте событие MESourceSeeked . В противном случае отправьте событие MESourceStarted . Данные события — это время начала, указанное в методе Start . Для события MESourceStarted, если время начала VT_EMPTY, задайте атрибут MF_EVENT_SOURCE_ACTUAL_START для события. Значение атрибута — это фактическое время начала.
  4. Для каждого потока, если ищет источник, отправьте событие MEStreamSeeked . В противном случае отправьте событие MEStreamStarted . Данные события — это время начала. (Источник мультимедиа может поместить событие в поток в очередь, вызвав метод IMFMediaEventGenerator::QueueEvent потока.)

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

Формат времени для метода Start задается в параметре pguidTimeFormat . Стандартный формат времени, указанный GUID_NULL, составляет 100 наносекундных единиц. Источник мультимедиа должен поддерживать этот формат времени.

Ищут

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

Начальное время, заданное в исходных событиях (MESourceStarted, MESourceSeeked, MEStreamStarted и MEStreamSeeked), — это запрошенное начальное время (значение, заданное в методе Start ), независимо от фактической начальной позиции.

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

Образец 1 2 3 4
Time 33 мс 66 мс 100 мс 133 мс
Ключевой кадр? Да Нет Нет Да

 

Если метод Start вызывается со значением 100 миллисекундах, источник должен выводить видео, начиная с кадра 1, первого ключевого кадра до этого времени. Событие start по-прежнему будет указывать на 100 миллисекундах в данных события.

Приостановка источника мультимедиа

Метод IMFMediaSource::P мауз приостанавливает работу источника мультимедиа.

Пока источник приостановлен, поток может создавать новые примеры и хранить их в очереди, но поток не доставляет примеры. Ниже приведены некоторые исключения из этого правила.

  • Динамические источники должны удалять данные во время приостановки.
  • Если источник получает данные из сети, он может приостановить работу сервера.

Если клиент вызывает IMFMediaStream::RequestSample во время приостановки источника, запрос также помещается в очередь, пока источник не будет запущен снова. Запросы не следует удалять.

Приостановка разрешена только в запущенном состоянии. В противном случае приостановка должна вернуть MF_E_INVALID_STATE_TRANSITION.

Создание исходных данных

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

Образец запросов

Клиент запрашивает новый пример, вызвав IMFMediaStream::RequestSample. Ниже приведена последовательность операций.

  1. Клиент вызывает IMFMediaStream::RequestSample. Аргумент является указателем на необязательный объект маркера , который клиент использует для отслеживания запроса. Клиент реализует маркер. Маркеры должны предоставлять интерфейс IUnknown . Клиент также может передать указатель NULL вместо маркера.

  2. Если клиент предоставил маркер, поток мультимедиа вызывает AddRef для маркера и помещает маркер в очередь первого входа. Метод возвращает , а остальные шаги выполняются асинхронно.

  3. При наличии дополнительных данных поток мультимедиа создает новый пример. (Этот шаг описан более подробно в следующем разделе.)

  4. Поток мультимедиа извлекает первый маркер из очереди.

  5. Если маркер не равен NULL, поток мультимедиа задает атрибут MFSampleExtension_Token в образце носителя. Значение атрибута является указателем на токен.

  6. Поток мультимедиа отправляет событие MEMediaSample . Данные события являются указателем на интерфейс IMFSample образца.

  7. Если клиент предоставил маркер, поток мультимедиа вызывает Release для объекта маркера.

Если поток мультимедиа не может выполнить запрос RequestSample клиента, он извлекает маркер из очереди и вызывает Release для маркера, но не отправляет событие MEMediaSample .

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

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

Поток отвечает за буферизацию любых данных, которые накапливаются между вызовами RequestSample.

Когда поток мультимедиа достигает конца потока, он отправляет событие MEEndOfStream после последней выборки. После завершения каждого потока источник мультимедиа отправляет событие MEEndOfPresentation . После того как поток мультимедиа отправляет событие MEEndOfStream, метод RequestSample возвращает MF_E_END_OF_STREAM , пока источник не будет перезапущен.

Выделение примеров

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

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

Метка времени в образце не обязательно равна времени презентации. Сеанс мультимедиа преобразует из исходного времени в время презентации. Для сжатых данных поток должен создавать данные, начиная с ближайшего ключевого кадра до времени начала. Это позволяет декодеру доставить кадр, который отображается в запрошенный момент начала. (В противном случае декодеру потребуется дождаться следующего ключевого кадра.)

Если скорость воспроизведения выше или медленнее, чем 1,0, конвейер корректирует частоту часов презентации. Источник не корректирует метки времени для выборок.

Источник может задать дополнительные сведения о образце, задав атрибуты. Список примеров атрибутов см. в разделе Примеры атрибутов.

Пробелы в потоке

Если поток содержит пробел значительной длины, рекомендуется, чтобы поток отправил событие MEStreamTick . Это событие уведомляет клиента о том, что пример отсутствует. Данные события — это метка времени отсутствующих выборок в единицах 100 наносекунд (VT_I8). Это событие может спасти подчиненные компоненты от ожидания образцов, которые не будут поступать. Поток может отправлять столько событий MEStreamTick, сколько необходимо для охвата разрыва в потоке.

Завершение работы источника мультимедиа

Когда клиент использует источник мультимедиа, он вызывает IMFMediaSource::Shutdown. Внутри этого метода источник мультимедиа должен прервать любое число циклических ссылок. Как правило, между источником мультимедиа и потоками мультимедиа имеются циклические ссылки.

Если вы используете очередь событий для реализации IMFMediaEventGenerator, вызовите IMFMediaEventQueue::Shutdown в очереди событий. Этот метод завершает работу очереди событий и сообщает всем вызывающим абонентам, которые в настоящее время ожидают события.

После завершения работы все методы в источнике возвращают MF_E_SHUTDOWN, за исключением методов IUnknown .

Динамические источники

Начиная с Windows 7, Media Foundation автоматически поддерживает устройства аудио- и видеозахвата. Для видео устройство должно предоставить мини-диск для потоковой передачи ядра (KS) в категории захвата видео. Media Foundation использует путь PnP для перечисления устройства. Для аудиофайла Media Foundation использует API устройства Windows Multimedia (MMDevice) для перечисления конечных звуковых устройств. Если устройство соответствует этим критериям, нет необходимости реализовывать пользовательский источник мультимедиа.

Однако может потребоваться реализовать пользовательский источник мультимедиа для другого типа устройства или другого активного источника данных. Существует лишь несколько различий между динамическим источником и другими источниками мультимедиа:

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

Источники мультимедиа

Руководство. Создание пользовательского источника мультимедиа