Примеры и распределители
[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие API, чтобы по возможности использовать новые API.]
Когда контакт доставляет данные мультимедиа другому контакту, он не передает прямой указатель в буфер памяти. Вместо этого он доставляет указатель на COM-объект, который управляет памятью. Этот объект, называемый примером мультимедиа, предоставляет интерфейс IMediaSample . Получающее закрепление обращается к буферу памяти путем вызова методов IMediaSample , таких как IMediaSample::GetPointer, IMediaSample::GetSize и IMediaSample::GetActualDataLength.
Примеры всегда перемещают вниз по течению, от выходного контакта к входному контакту. В модели принудительной отправки выходной контакт доставляет пример, вызывая метод IMemInputPin::Receive во входном контакте. Входной контакт будет обрабатывать данные синхронно (то есть полностью внутри метода Receive ) или обрабатывать их асинхронно в рабочем потоке. Входной пин-код может блокироваться в методе Receive , если ему нужно дождаться ресурсов.
Другой COM-объект, называемый распределителем, отвечает за создание примеров мультимедиа и управление ими. Распределители предоставляют интерфейс IMemAllocator . Всякий раз, когда фильтру требуется образец носителя с пустым буфером, он вызывает метод IMemAllocator::GetBuffer , который возвращает указатель на образец. Каждое закрепленное соединение использует один распределитель. При подключении двух контактов они решают, какой фильтр будет предоставлять распределитель. Контакты также задают свойства распределителя, такие как количество буферов и размер каждого буфера. (Дополнительные сведения см. в разделе Подключение фильтров и согласование распределителей.)
На следующем рисунке показаны связи между распределителем, примерами мультимедиа и фильтром.
Примеры ссылок на носители
Распределитель создает конечный пул выборок. Некоторые примеры могут использоваться в любое время, а другие доступны для вызовов GetBuffer . Распределитель использует подсчет ссылок для отслеживания примеров. Метод GetBuffer возвращает пример с числом ссылок 1. Если количество ссылок равно нулю, пример возвращается в пул распределителя, где его можно использовать в следующем вызове GetBuffer . Пока число ссылок остается выше нуля, выборка недоступна для GetBuffer. Если используется каждый образец, принадлежащий распределителю, метод GetBuffer блокируется до тех пор, пока пример не станет доступным.
Например, предположим, что входной пин-код получает образец. Если он обрабатывает образец синхронно, внутри метода Receive он не увеличивает число ссылок. После получения выходные данные освобождают образец, количество ссылок переходит к нулю, а образец возвращается в пул распределителя. С другой стороны, если входной контакт обрабатывает образец в рабочем потоке, он увеличивает количество ссылок перед выходом из метода Receive . Теперь число ссылок равно 2. Когда выходной контакт освобождает пример, счетчик переходит к 1; Пример еще не возвращается в пул. После завершения работы с примером рабочий поток вызывает Release , чтобы освободить пример. Теперь пример возвращается в пул.
Когда контакт получает образец, он может скопировать данные в другой пример или изменить исходный пример и доставить его в следующий фильтр. Потенциально пример может перемещаться по всей длине графа, каждый фильтр по очереди вызывает AddRef и Release . Таким образом, выходной контакт никогда не должен повторно использовать пример после вызова Receive, так как нижестоящий фильтр может использовать образец. Для получения нового примера выходной пин-код всегда должен вызывать GetBuffer .
Этот механизм сокращает объем выделения памяти, так как фильтры повторно используют одни и те же буферы. Кроме того, фильтры не могут случайно записывать данные, которые не были обработаны, так как распределитель ведет список доступных примеров.
Фильтр может использовать отдельные распределители для входных и выходных данных. Это можно сделать, если развернуть входные данные (например, распаковка). Если размер выходных данных не превышает входные данные, фильтр может обработать данные на месте, не скопировав их в новый образец. В этом случае два или более pin-подключений могут совместно использовать один распределитель.
Фиксация и списание распределителей
Когда фильтр впервые создает распределитель, он не резервирует буферы памяти. На этом этапе все вызовы метода GetBuffer завершатся ошибкой. При запуске потоковой передачи выходной пин-код вызывает IMemAllocator::Commit, который фиксирует распределитель, в результате чего он выделяет память. Теперь контакты могут вызывать GetBuffer.
При остановке потоковой передачи пин-код вызывает IMemAllocator::D ecommit, что приводит к списанию распределителя. Все последующие вызовы GetBuffer завершаются ошибкой , пока распределитель не будет зафиксирован снова. Кроме того, если какие-либо вызовы GetBuffer в настоящее время блокируются в ожидании примера, они немедленно возвращают код сбоя. Метод Decommit может освободить или не может освободить память в зависимости от реализации. Например, класс CMemAllocator ожидает, пока его метод деструктора не освободит память.
Связанные темы