Примеры и аллокаторы
[Функция, связанная с этой страницей, 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 на входной контакт. Входной пин будет обрабатывать данные синхронно (то есть полностью внутри метода получения) или обрабатывать их асинхронно в рабочем потоке. Входной пин-код может блокироваться в методе получения, если он должен ждать ресурсов.
Другой COM-объект, называемый распределителем, отвечает за создание и управление медиаобразцами. Распределители предоставляют интерфейс IMemAllocator. Когда фильтру требуется образец носителя с пустым буфером, он вызывает метод IMemAllocator::GetBuffer, который возвращает указатель на образец. Каждое контактное соединение делит один распределитель. Когда два контакта подключаются, они определяют, какой фильтр будет предоставлен распределителю. Закрепление также задает свойства для распределителя, например количество буферов и размер каждого буфера. (Дополнительные сведения см. в разделе Как соединяются фильтры и Переговоры о распределителях.)
На следующем рисунке показаны связи между распределителем, образцами мультимедиа и фильтром.
Счетчики ссылок на образцы мультимедиа
Распределитель создает конечный пул примеров. В любое время некоторые примеры могут использоваться, а другие доступны для вызовов GetBuffer. Распределитель использует подсчет ссылок для отслеживания примеров. Метод GetBuffer возвращает образец с количеством ссылок, равным 1. Если число ссылок уменьшается до нуля, экземпляр возвращается в пул распределителя, где его можно использовать при следующем вызове GetBuffer. Если число ссылок остается выше нуля, пример недоступен для GetBuffer. Если каждый образец, принадлежащий распределителю, используется, то метод GetBuffer блокируется, пока не станет доступен образец.
Например, предположим, что входной контакт получает сигнал. Если он обрабатывает образец синхронно, внутри метода Receive он не увеличивает счетчик ссылок. После возврата получения выходной пин-код освобождает образец, число ссылок переходит к нулю, а образец возвращается в пул распределителя. С другой стороны, если входной пин обрабатывает данные в рабочем потоке, он увеличивает число ссылок перед выходом из метода Receive. Теперь число ссылок равно 2. Когда выходной контактор освобождает образец, счётчик устанавливается на 1; образец еще не возвращается в пул. После завершения работы с примером рабочий поток вызывает Release, чтобы освободить пример. Образец возвращается в пул.
Когда пин-код получает пример, он может скопировать данные в другой пример или изменить исходный образец и доставить его к следующему фильтру. Возможно, образец может проходить по всей длине графа, каждый фильтр вызывает AddRef и Release по очереди. Таким образом, выходной вывод никогда не должен повторно использовать образец после вызова Receive, поскольку нижестоящий фильтр может использовать этот образец. Выходной пин-код всегда должен вызывать GetBuffer, чтобы получить новый пример.
Этот механизм уменьшает объем выделения памяти, так как фильтры повторно используют одни и те же буферы. Он также предотвращает случайную перезапись данных фильтрами, которые не были обработаны, так как аллокатор поддерживает список доступных образцов.
Фильтр может использовать отдельные распределители для входных и выходных данных. Это может сделать, если оно расширяет входные данные (например, распаковывая их). Если выходные данные не превышают входные данные, фильтр может обрабатывать данные на месте, не копируя его в новый пример. В этом случае два или более контактных соединений могут совместно использовать один распределитель.
фиксация и отмена фиксации выделителей
Когда фильтр сначала создает распределитель, распределитель не резервирует буферы памяти. На этом этапе все вызовы метода GetBuffer завершаются ошибкой. При запуске потоковой передачи выходные пин-коды вызывают IMemAllocator::Commit, что фиксирует распределитель, что приводит к выделению памяти. Теперь пин-коды могут вызывать GetBuffer.
При остановке стриминга pin вызывает IMemAllocator::Decommit, который отменяет выделение. Все последующие вызовы GetBuffer завершаются ошибкой, пока распределитель снова не будет подтверждён. Кроме того, если какие-либо вызовы GetBuffer в настоящее время заблокированы в ожидании образца, они немедленно возвращают код сбоя. Метод Decommit может или не освободить память в зависимости от реализации. Например, класс CMemAllocator ожидает, пока метод его деструктора не освободит память.
Связанные разделы