Поделиться через


Альтернативные отрисовщики видео

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

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

Заметка

Вместо написания пользовательского отрисовщика видео рекомендуется написать подключаемый модуль для средства просмотра видео (VMR) или расширенного отрисовщика видео (EVR). Этот подход даст вам все преимущества VMR/EVR, включая поддержку ускорения видео DirectX (DXVA), аппаратного деинтерлакирования и шага кадров, и, скорее всего, будет более надежным, чем пользовательский отрисовщик видео. Дополнительные сведения см. в следующих разделах:

 

Написание альтернативного отрисовщика

Microsoft DirectShow предоставляет отрисовщик видео на основе окон; он также предоставляет полноэкранный отрисовщик во время выполнения. Базовые классы DirectShow можно использовать для записи альтернативных отрисовщиков видео. Чтобы альтернативные отрисовщики правильно взаимодействовали с приложениями на основе DirectShow, отрисовщики должны соответствовать рекомендациям, описанным в этой статье. Вы можете использовать классы CBaseRenderer и CBaseVideoRenderer для выполнения этих рекомендаций при реализации альтернативной отрисовки видео. Из-за текущей разработки DirectShow периодически просматривайте реализацию, чтобы убедиться, что отрисовщики совместимы с последней версией DirectShow.

В этом разделе рассматривается множество уведомлений о том, что отрисовщик отвечает за обработку. Краткий обзор уведомлений DirectShow может помочь настроить этап. В DirectShow существуют три типа уведомлений:

  • уведомления Stream, которые являются событиями, происходящими в потоке мультимедиа, и передаются из одного фильтра в следующий. Они могут быть начальными, сквозными или конечными уведомлениями о потоке и отправляться путем вызова соответствующего метода для входного пин-кода нижнего фильтра (например, IPin::BeginFlush).
  • уведомления графа фильтра, которые отправляются из фильтра в диспетчер графов фильтров, например EC_COMPLETE. Это достигается путем вызова метода IMediaEventSink::Notify в диспетчере графов фильтров.
  • уведомления приложений, которые извлекаются из диспетчера графов фильтров с помощью управляемого приложения. Приложение вызывает метод IMediaEvent::GetEvent диспетчера фильтров для получения этих событий. Часто диспетчер графов фильтров проходит через события, которые он получает в приложение.

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

Обработка конечных потоков и уведомлений об очистке

Уведомление о конце потока начинается с вышестоящего фильтра (например, исходного фильтра), когда этот фильтр обнаруживает, что он не может отправлять больше данных. Он передается через каждый фильтр в графе и в конечном итоге заканчивается на отрисовщике, который отвечает за последующее отправку уведомления EC_COMPLETE диспетчеру графов фильтров. Отрисовщики имеют особые обязанности, когда речь идет об обработке этих уведомлений.

Отрисовщик получает уведомление о конце потока, когда метод iPin::EndOfStream вызывается методом вышестоящего фильтра. Отрисовщик должен отметить это уведомление и продолжать отображать все полученные данные. После получения всех оставшихся данных отрисовщик должен отправить уведомление EC_COMPLETE диспетчеру графов фильтра. Уведомление EC_COMPLETE должно отправляться только один раз отрисовщиком при каждом достижении конца потока. Кроме того, EC_COMPLETE уведомления никогда не должны отправляться, за исключением случаев, когда выполняется граф фильтров. Таким образом, если граф фильтров приостановлен, когда исходный фильтр отправляет уведомление о конце потока, EC_COMPLETE не следует отправлять, пока граф фильтра не будет запущен.

Все вызовы методов IMemInputPin::Receive или IMemInputPin::ReceiveMultiple после уведомления о завершении потока должны быть отклонены. E_UNEXPECTED является наиболее подходящим сообщением об ошибке для возврата в этом случае.

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

Отрисовщики видео часто зависят от уведомлений о конце потока больше, чем отправка уведомлений EC_COMPLETE. Например, если поток завершил воспроизведение (т. е. отправляется уведомление о завершении потока), а другое окно перетаскивается через окно отрисовщика видео, будет создано несколько сообщений WM_PAINT окна. Как правило, при выполнении отрисовщиков видео следует воздержаться от переопределения текущего кадра при получении WM_PAINT сообщений (исходя из предположения, что будет получен другой кадр). Однако при отправке уведомления о завершении потока отрисовщик находится в состоянии ожидания; он по-прежнему работает, но знает, что он не получит никаких дополнительных данных. В этих обстоятельствах отрисовщик обычно рисует область воспроизведения черной.

Обработка очистки является дополнительным осложнением для отрисовщиков. Очистка выполняется через пару методов IPin, называемых BeginFlush и EndFlush. Очистка по сути является дополнительным состоянием, которое должен обрабатывать отрисовщик. Это недопустимо для исходного фильтра вызывать BeginFlush без вызова EndFlush, поэтому, надеюсь, состояние является коротким и дискретным; однако отрисовщик должен правильно обрабатывать данные или уведомления, получаемые во время перехода с очисткой.

Все данные, полученные после вызова BeginFlush, должны быть отклонены немедленно, возвращая S_FALSE. Кроме того, все кэшированные уведомления о конце потока также должны быть удалены при очистке отрисовщика. Отрисовщик обычно очищается в ответ на поиск. Очистка гарантирует очистку старых данных из графа фильтров перед отправкой свежих выборок. (Как правило, воспроизведение двух разделов потока, один за другим, лучше всего обрабатываться с помощью отложенных команд, а не ожидать завершения одного раздела, а затем выдачи команды поиска.)

Обработка изменений состояния и приостановка завершения

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

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

Средство отрисовки видео должно отображать и выпускать примеры только во время выполнения. При приостановке фильтр может отображать их (например, при рисовании статического изображения плаката в окне), но не следует их выпускать. Отрисовщики звука не будут выполнять отрисовку во время приостановки (хотя они могут выполнять другие действия, такие как подготовка устройства волны, например). Время отрисовки примеров должно быть получено путем объединения времени потока в образце со временем ссылки, переданным в качестве параметра методу IMediaControl::Run. Отрисовщики должны отклонять примеры с временем начала меньше или равно времени окончания.

Когда приложение приостанавливает граф фильтра, граф фильтров не возвращается из его IMediaControl::P ause метод до тех пор, пока данные не будут помещены в очередь на отрисовщиках. Чтобы убедиться в этом, когда отрисовщик приостановлен, он должен возвращать S_FALSE, если нет данных, ожидающих отрисовки. Если у него есть данные в очереди, он может вернуть S_OK.

Диспетчер фильтров проверяет все возвращаемые значения при приостановке графа фильтра, чтобы убедиться, что отрисовщики имеют данные в очереди. Если один или несколько фильтров не готовы, диспетчер графов фильтров опрашивает фильтры в графе путем вызова IMediaFilter::GetState. Метод GetState принимает параметр времени ожидания. Фильтр (обычно отрисовщик), который по-прежнему ожидает поступления данных, прежде чем завершить изменение состояния, возвращает VFW_S_STATE_INTERMEDIATE, если истекает срок действия метода GetState . Когда данные поступают в отрисовщик, GetState должны быть возвращены немедленно с S_OK.

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

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

Обработка завершения

Отрисовщики видео должны правильно обрабатывать события завершения от пользователя. Это означает правильное скрытие окна и знание того, что делать, если окно впоследствии принудительно отображается. Кроме того, отрисовщики видео должны уведомить диспетчер графа фильтров при уничтожении окна (или более точно, когда отрисовщик удаляется из графа фильтра) для освобождения ресурсов.

Если пользователь закрывает окно видео (например, нажав клавиши ALT+F4), это соглашение немедленно скрывает окно и отправляет уведомление EC_USERABORT диспетчеру графов фильтров. Это уведомление передается приложению, которое остановит воспроизведение графа. После отправки EC_USERABORTотрисовщик видео должен отклонить все дополнительные примеры, доставленные в него.

Флаг, остановленный графом, должен оставаться на отрисовщике до тех пор, пока он не будет остановлен, в какой момент его необходимо сбросить, чтобы приложение может переопределить действие пользователя и продолжить воспроизведение графа при необходимости. Если клавиши ALT+F4 нажимаются во время работы видео, окно будет скрыто, а все дополнительные образцы, доставленные, будут отклонены. Если окно впоследствии отображается (возможно, через IVideoWindow::p ut_Visible), то не следует создавать уведомления EC_REPAINT.

Средство отрисовки видео также должно отправлять уведомление EC_WINDOW_DESTROYED графу фильтра при завершении отрисовщика видео. На самом деле лучше всего обработать это, когда метод отрисовщика IBaseFilter::JoinFilterGraph вызывается с помощью null-параметра (указывая, что отрисовщик будет удален из графа фильтра), а не ожидая, пока фактическое окно видео будет уничтожено. Отправка этого уведомления позволяет распространителю подключаемых модулей в диспетчере фильтров передавать ресурсы, которые зависят от фокуса окна на другие фильтры, такие как звуковые устройства.

Обработка изменений динамического формата

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

Фильтр вышестоящего потока, пытающийся динамически изменять форматы, всегда должен вызывать метод IPin::QueryAccept на пин-коде ввода отрисовщика. Средство отрисовки видео имеет некоторые возможности в том, какие типы динамических форматов должны поддерживаться. Как минимум, он должен разрешить вышестоящему фильтру изменять палитры. Когда фильтр вышестоящей части изменяет типы носителей, он присоединяет тип носителя к первому образцу, доставленном в новом формате. Если отрисовщик содержит примеры в очереди для отрисовки, он не должен изменять формат, пока он не отрисовывает образец с изменением типа.

Отрисовщик видео также может запросить изменение формата от декодера. Например, он может попросить декодировщик предоставить формат, совместимый с DirectDraw, отрицательным biHeight. При приостановке отрисовщика необходимо вызвать QueryAccept на вышестоящем закреплении, чтобы узнать, какие форматы может предоставлять декодировщик. Декодировщик может не перечислять все типы, которые он может принимать, поэтому отрисовщик должен предлагать некоторые типы, даже если декодировщик не объявляет их.

Если декодатор может переключиться в запрошенный формат, он возвращает S_OK из QueryAccept. Затем отрисовщик присоединяет новый тип носителя к следующему образцу носителя на вышестоящем распределителе. Для работы отрисовщик должен предоставить пользовательский распределитель, реализующий частный метод для присоединения типа носителя к следующему образцу. (В рамках этого закрытого метода вызовите IMediaSample::SetMediaType, чтобы задать тип.)

Входной пин-код отрисовщика должен возвращать пользовательский распределитель отрисовщика в методе IMemInputPin::GetAllocator. Переопределите IMemInputPin::NotifyAllocator, чтобы он завершился ошибкой, если вышестоящий фильтр не использует распределитель отрисовщика.

При использовании некоторых декодеров установка biHeight положительным числом в типах YUV приводит к тому, что декодатор нарисовывает изображение вверх ногами. (Это неправильно и должно рассматриваться как ошибка в декоде.)

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

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

Наконец, отрисовщик видео также может обнаружить, что размер видео изменился, в этом случае он должен отправить уведомление EC_VIDEO_SIZE_CHANGED. Приложение может использовать это уведомление для согласования пространства в составном документе. Фактические измерения видео доступны через интерфейс управления IBasicVideo. Отрисовщики DirectShow определяют, изменился ли размер видео до отправки этих событий.

Обработка постоянных свойств

Все свойства, заданные через интерфейсы IBasicVideo и интерфейсы IVideoWindow, предназначены для постоянных подключений. Таким образом, отключение и повторное подключение отрисовщика не должно отображать влияния на размер окна, положение или стили. Однако если измерения видео изменяются между подключениями, отрисовщик должен сбросить исходные и целевые прямоугольники на их значения по умолчанию. Позиции источника и назначения задаются через интерфейс IBasicVideo.

Как IBasicVideo, так и IVideoWindow предоставить достаточно доступа к свойствам, чтобы разрешить приложению сохранять и восстанавливать все данные в интерфейсе в постоянном формате. Это полезно для приложений, которые должны сохранять точную конфигурацию и свойства графов фильтров во время сеанса редактирования и восстанавливать их позже.

Обработка уведомлений EC_REPAINT

Уведомление EC_REPAINT отправляется только при приостановке или остановке отрисовщика. Это уведомление сигнализирует диспетчеру графов фильтра, которому требуется данные отрисовщика. Если граф фильтров остановлен при получении одного из этих уведомлений, он приостановит граф фильтров, дождитесь получения данных (вызвав GetState), а затем остановите его снова. При остановке средство отрисовки видео должно храниться на изображении, чтобы последующие WM_PAINT сообщения могли обрабатываться.

Таким образом, если средство отрисовки видео получает сообщение WM_PAINT при остановке или приостановке, и он не имеет ничего, с которым нужно нарисовать окно, то он должен отправить EC_REPAINT диспетчеру графов фильтра. Если при приостановке получено уведомление EC_REPAINT, диспетчер графов фильтра вызывает IMediaPosition::p ut_CurrentPosition с текущей позицией (то есть ищет текущую позицию). Это приводит к очистке исходного фильтра графа фильтров и приводит к отправке новых данных через граф фильтров.

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

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

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

Если выходной пин-код не может обрабатывать запрос и выполняется граф фильтра, EC_REPAINT уведомление игнорируется. Выходной пин-код должен возвращать S_OK из IMediaEventSink::Notify, чтобы сообщить об успешном обработке запроса на повторную обработку. Выходной пин-код будет вызываться в рабочем потоке Диспетчера фильтров, что позволяет избежать вызова пин-кода вывода отрисовщика напрямую, и поэтому при необходимости возникают проблемы взаимоблокировки. Если граф фильтров остановлен или приостановлен, а выходные данные не обрабатывают запрос, то обработка по умолчанию выполняется.

Обработка уведомлений в режиме Full-Screen

IVideoWindow распространитель подключаемых модулей (PID) в графе фильтров управляет воспроизведением полноэкранного экрана. Он переключит отрисовщик видео для специалиста полноэкранного отрисовщика, растянет окно отрисовщика до полноэкранного или напрямую реализует воспроизведение полноэкранного экрана. Чтобы взаимодействовать с полноэкранными протоколами, средство отрисовки видео должно отправлять уведомление EC_ACTIVATE всякий раз, когда его окно активировано или деактивировано. Другими словами, уведомление EC_ACTIVATE должно отправляться для каждого WM_ACTIVATEAPP сообщения, которое получает отрисовщик.

Когда средство отрисовки используется в полноэкранном режиме, эти уведомления управляют переключением и выходом из этого полноэкранного режима. Деактивация окна обычно возникает, когда пользователь нажимает КЛАВИШИ ALT+TAB, чтобы переключиться на другое окно, которое средство отрисовки DirectShow использует в качестве подсказки для возврата к типичному режиму отрисовки.

Когда уведомление EC_ACTIVATE отправляется диспетчеру графов фильтра при переключении из полноэкранного режима, диспетчер графов фильтра отправляет уведомление EC_FULLSCREEN_LOST в управляющее приложение. Приложение может использовать это уведомление для восстановления состояния полноэкранной кнопки, например. Уведомления EC_ACTIVATE используются внутри DirectShow для управления полноэкранным переключением на подсказки от отрисовщиков видео.

Сводка уведомлений

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

Уведомление о событии Описание
EC_ACTIVATE Отправляется отрисовщиками видео в полноэкранном режиме отрисовки для каждого полученного сообщения WM_ACTIVATEAPP.
EC_COMPLETE Отправляется отрисовщиками после отрисовки всех данных.
EC_DISPLAY_CHANGED Отправляется отрисовщиками видео при изменении формата отображения.
EC_PALETTE_CHANGED Отправляется всякий раз, когда средство отрисовки видео обнаруживает изменение палитры в потоке.
EC_REPAINT Отправленные остановленными или приостановленными отрисовщиками видео при получении сообщения WM_PAINT и не отображаются данные. Это приводит к созданию кадра для отображения диспетчера графов фильтра.
EC_USERABORT Отправлены отрисовщиками видео, чтобы сигнализировать о закрытии, запрошенном пользователем (например, пользователем, закрывающим окно видео).
EC_VIDEO_SIZE_CHANGED Отправляется отрисовщиками видео всякий раз, когда обнаруживается изменение размера видео в собственном коде.
EC_WINDOW_DESTROYED Отправляемые видео отрисовщиками при удалении или уничтожении фильтра, чтобы ресурсы, зависящие от фокуса окна, могли передаваться другим фильтрам.

 

записи видео отрисовщиков