Как написать исходный фильтр для DirectShow

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

В этом разделе описывается, как написать пользовательский фильтр источника для DirectShow.

Заметка

В этом разделе описываются только источники push-уведомлений; Он не описывает источники извлечения, такие как фильтр асинхронного средства чтения или фильтры разделения, которые подключаются к источникам извлечения. Различие между отправкой и источниками см. в потоке данных для разработчиков фильтров.

Модель потоковой передачи DirectShow

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

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

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

Поток потоковой передачи запускает цикл со следующей структурой:

until (stopped)
  1. Get a media sample from the allocator.
  2. Fill the sample with data.
  3. Time stamp the sample. 
  4. Deliver the sample downstream.

Если образцы недоступны, шаг 1 приостанавливается, пока образец не станет доступен. Шаг 4 также может блокироваться; Например, он может блокироваться при приостановке графа.

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

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

Базовые классы DirectShow включают два класса, которые поддерживают push-источники: CSource и CSourceStream.

  • CSource является базовым классом для фильтра и реализует интерфейсIBaseFilter.
  • CSourceStream является базовым классом для выходных контактов и реализует интерфейс IPin.

Выходные закрепления

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

Форматы выходных данных

Выходной вывод обрабатывает согласование форматов с помощью следующих методов CSourceStream:

Метод Описание
GetMediaType Возвращает тип носителя из выходного контакта.
Пин должен предложить по крайней мере один тип носителя, так как последующий фильтр может не предложить никакие типы. В большинстве случаев нижестоящий фильтр будет декодером или отрисовщиком в зависимости от того, предоставляет ли исходный фильтр сжатые или несжатые данные. Фильтр визуализатора обычно требует полного медиа-типа, содержащего все сведения о формате, необходимые для отрисовки потока. Для декодера объем информации, необходимой в типе носителя, зависит от формата кодирования.
ПроверьтеТипНосителя Проверяет, принимает ли выходной пин-код заданный тип носителя. Переопределение этого метода является необязательным в зависимости от способа реализации GetMediaType.

Метод GetMediaType перегружен:

  • GetMediaType (1) принимает один параметр, указатель на объект CMediaType.
  • GetMediaType (2) принимает переменную индекса и указатель на объект CMediaType.

Если выходной пин-код исходного фильтра поддерживает ровно один формат мультимедиа, необходимо переопределить (1), чтобы инициализировать объект CMediaType с этим форматом. Оставьте реализацию по умолчанию (2) и оставьте реализацию по умолчанию CheckMediaType.

Если пин поддерживает несколько форматов, переопределите его (2). Инициализируйте объектCMediaTypeв соответствии со значением переменной индекса. Пин-код должен возвращать форматы в виде упорядоченного списка. В этом случае необходимо также переопределить CheckMediaType, чтобы проверить тип носителя в списке форматов.

Для несжатых форматов видео следует помнить, что подчиненный фильтр может предлагать форматы с различными значениями шага. Фильтр должен принять любое допустимое значение шага. Дополнительные сведения см. в разделе BITMAPINFOHEADER.

Кроме того, необходимо переопределить метод CBaseOutputPin::DecideBufferSize. Используйте этот метод, чтобы задать размер буферов образца.

Стриминг

Класс CSourceStream создает поток потоковой передачи для пин-кода. Процедура потока реализована в методе CSourceStream::DoBufferProcessingLoop. Этот метод вызывает метод чисто виртуальной CSourceStream::FillBuffer, который производный класс должен переопределить. Этот метод предназначен для заполнения буфера данными. Например, если ваш фильтр предоставляет несжатое видео: здесь вы будете выводить видеокадры.

Базовый класс автоматически запускает и останавливает цикл потока в подходящий момент, когда фильтр приостанавливается или останавливается. В этом случае класс CSourceStream вызывает некоторые методы для уведомления производного класса:

Эти методы можно переопределить, если необходимо добавить любую специальную обработку. В противном случае реализации по умолчанию просто возвращают S_OK.

Поиск

Если у вас есть исходный фильтр с одним пин-кодом вывода, можно использовать класс CSourceSeeking в качестве отправной точки для реализации поиска. Наследуйте класс пин-кода от CSourceStream и CSourceSeeking.

Заметка

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

Класс CSourceSeeking управляет скоростью воспроизведения, временем начала, временем остановки и длительностью. Производный класс должен задать начальное время остановки и длительность. При каждом изменении одного из этих значений вызывается метод CSourceSeeking::ChangeRate, метод CSourceSeeking::ChangeStartили метод CSourceSeeking::ChangeStop. Эти методы являются чистыми виртуальными методами. Производный класс Pin переопределяет методы, чтобы выполнить следующее:

  1. Вызовите IPin::BeginFlush на нижнем пин-коде. Это приводит к тому, что последующий фильтр освобождает случаи, которые он удерживает, и отклоняет новые случаи.
  2. Вызовите CSourceStream::Stop, чтобы остановить поток потоковой передачи. Исходный фильтр приостанавливает создание новых данных.
  3. Вызовите IPin::EndFlush на выходном пине. Это сигнализирует нижестоящим фильтрам принимать новые данные.
  4. Вызов IPin::NewSegment с новым временем начала, временем остановки и скоростью.
  5. Установите свойство разрывов для следующего образца.

Дополнительные сведения см. в разделе «Поиск в исходном фильтре».

Если фильтр поддерживает поиск, позиция потока теперь не зависит от времени презентации. После поиска метки времени сбрасываются до нуля. Общая формула меток времени:

  • выборка времени начала = время, прошедшее / скорость воспроизведения
  • время завершения выборки = время начала выборки + (время на кадр / скорость воспроизведения)

где истекшее время — это время, прошедшее с запуска фильтра или со времени последней команды поиска.

Форматы времени для поиска

По умолчанию команды поиска определяются в единицах по 100 наносекунд. Исходный фильтр может поддерживать дополнительные форматы времени, такие как поиск по номеру кадра. Каждый раз формат времени определяется идентификатором GUID; см. GUID формата времени.

Для поддержки дополнительных форматов времени необходимо реализовать следующие методы на пин-коде вывода:

Если приложение задает новый формат времени, все параметры положения в методах IMediaSeeking интерпретируются с точки зрения нового формата времени. Например, если формат времени является кадрами, метод IMediaSeeking::GetDuration должен возвращать длительность в кадрах.

На практике несколько фильтров DirectShow поддерживают дополнительные форматы времени, а в результате несколько приложений DirectShow используют эту возможность.

Создание исходных фильтров