Альтернативные отрисовщики видео
[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие API, чтобы по возможности использовать новые API.]
В этом разделе описывается создание пользовательского отрисовщика видео для DirectShow.
Примечание
Вместо написания пользовательского отрисовщика видео рекомендуется написать подключаемый модуль allocator-presenter для отрисовщика смешивания видео (VMR) или расширенного отрисовщика видео (EVR). Этот подход даст вам все преимущества VMR/EVR, включая поддержку ускорения видео DirectX (DXVA), аппаратное деинтерлейирование и пошаговое выполнение кадров, и, скорее всего, будет более надежным, чем пользовательский отрисовщик видео. Дополнительные сведения см. в следующих разделах:
Написание альтернативного отрисовщика
Microsoft DirectShow предоставляет оконный отрисовщик видео; он также предоставляет полноэкранный отрисовщик во время установки во время выполнения. Вы можете использовать базовые классы DirectShow для написания альтернативных отрисовщиков видео. Чтобы альтернативные отрисовщики правильно взаимодействовали с приложениями на основе DirectShow, отрисовщики должны соответствовать рекомендациям, описанным в этой статье. Вы можете использовать классы CBaseRenderer и CBaseVideoRenderer для выполнения этих рекомендаций при реализации альтернативной отрисовки видео. Из-за текущей разработки DirectShow периодически проверяйте свою реализацию, чтобы убедиться, что отрисовщики совместимы с последней версией DirectShow.
В этом разделе рассматриваются многие уведомления, за обработку которых отвечает отрисовщик. Краткий обзор уведомлений DirectShow может помочь в настройке этапа. Существует три типа уведомлений, которые происходят в DirectShow:
- Потоковые уведомления, которые представляют собой события, которые происходят в потоке мультимедиа и передаются от одного фильтра к другому. Это могут быть уведомления о начале сброса, завершении очистки или конце потока. Они отправляются путем вызова соответствующего метода во входном контакте нижестоящего фильтра (например , 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 является наиболее подходящим сообщением об ошибке для возврата в этом случае.
При остановке графа фильтра все кэшированные уведомления об окончании потока должны быть очищены и не будут повторно передаваться при следующем запуске. Это связано с тем, что диспетчер фильтров graph всегда приостанавливает все фильтры непосредственно перед их выполнением, чтобы обеспечить правильную очистку. Таким образом, например, если граф фильтра приостановлен и получено уведомление об окончании потока, а затем граф фильтра останавливается, отрисовщик не должен отправлять уведомление 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 use , пока данные не будут помещены в очередь в отрисовщиках. Чтобы обеспечить это, при приостановке отрисовщика он должен возвращать 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 (указывающим, что отрисовщик будет удален из графа фильтра), а не дожидаться уничтожения фактического окна видео. Отправка этого уведомления позволяет распространителю подключаемого модуля в диспетчере фильтров Graph передавать ресурсы, зависящие от фокуса окна, другим фильтрам, таким как звуковые устройства.
Обработка изменений динамического формата
В некоторых случаях фильтр вышестоящий отрисовщика может попытаться изменить формат видео во время воспроизведения видео. Чаще всего это декомпрессор видео, который инициирует динамическое изменение формата.
Фильтр вышестоящий, пытающийся динамически изменять форматы, должен всегда вызывать метод 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 уведомления, он будет заполнять диспетчер фильтров Graph EC_REPAINT запросами (которые относительно дорого обрабатываются). Например, если отрисовщик не имеет изображения для рисования, а другое окно перетаскивается через окно отрисовщика в ходе операции полного перетаскивания, отрисовщик получает несколько WM_PAINT сообщений. Только первый из них должен создавать уведомление о событии EC_REPAINT от отрисовщика в диспетчер фильтров Graph.
Отрисовщик должен отправить входной пин-код в качестве первого параметра в уведомление EC_REPAINT . При этом присоединенный выходной пин-код будет запрашиваться для IMediaEventSink, и при его поддержке сначала будет отправлено уведомление о EC_REPAINT . Это позволяет выходным контактам обрабатывать перерисовки перед касанием графа фильтров. Это не будет сделано, если граф фильтра остановлен, так как буферы не будут доступны в распределителье отрисовщика.
Если выходной пин-код не может обработать запрос и граф фильтра выполняется, уведомление EC_REPAINT игнорируется. Выходной контакт должен возвращать S_OK из IMediaEventSink::Notify, чтобы сообщить об успешной обработке запроса на изменение. Выходной контакт будет вызываться в рабочем потоке диспетчера фильтров Graph Manager, что позволяет избежать вызова отрисовщиком выходных контактов напрямую и таким образом обойти любые проблемы взаимоблокировки. Если граф фильтра остановлен или приостановлен, а выходные данные не обрабатывают запрос, то выполняется обработка по умолчанию.
Обработка уведомлений в режиме Full-Screen
Подключаемый распространитель IVideoWindow (PID) в графе фильтров управляет воспроизведением в полноэкранном режиме. Он переключит видео отрисовщик на специализированный полноэкранный отрисовщик, растянет окно отрисовщика до полноэкранного или напрямую реализует полноэкранное воспроизведение. Для взаимодействия в полноэкранных протоколах отрисовщик видео должен отправлять EC_ACTIVATE уведомление всякий раз, когда его окно активировано или деактивировано. Иными словами, EC_ACTIVATE уведомление должно отправляться для каждого сообщения WM_ACTIVATEAPP, которое получает отрисовщик.
Когда отрисовщик используется в полноэкранном режиме, эти уведомления управляют переходом в полноэкранный режим и выходом из него. Деактивация окна обычно происходит, когда пользователь нажимает клавиши ALT+TAB, чтобы переключиться в другое окно, которое используется полноэкранным отрисовщиком DirectShow в качестве подсказки для возврата в стандартный режим отрисовки.
Когда при выходе из полноэкранного режима диспетчеру фильтров graph отправляется уведомление EC_ACTIVATE, диспетчер фильтра Graph отправляет 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 | Отправляется отрисовщиками видео при удалении или уничтожении фильтра, чтобы ресурсы, зависящие от фокуса окна, могли быть переданы другим фильтрам. |
Связанные темы