Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
В этом разделе рассматриваются потоковая передача ACX и связанная буферизация, которые имеют решающее значение для отсутствия сбоев в аудио. В нем описываются механизмы, используемые драйвером для обмена данными о состоянии потока и управления буфером для потока. Список распространенных терминов звука ACX и введение в ACX см. в обзоре расширений аудиоклассов ACX.
Типы потоковой передачи ACX
AcxStream представляет аудиопоток на оборудовании определенной цепи. AcxStream может агрегировать один или несколько объектов, подобных AcxElements.
Платформа ACX поддерживает два типа потоков. Первый тип потока, RT Packet Stream, обеспечивает поддержку выделения пакетов RT и использования пакетов RT для передачи звуковых данных на оборудование устройства или с него, а также для переходов состояния потока. Второй тип потока, базовый поток, обеспечивает только поддержку переходов состояния потока.
В одном конечном элементе цепи контур должен быть потоковым, который создает поток пакетов RT. Если для создания конечной точки подключены два или более каналов, первый канал в конечной точке является каналом потоковой передачи и создает поток пакетов RT; подключенные каналы создают базовые потоки для получения событий, связанных с переходами состояния потока.
Дополнительные сведения см. в разделе "Поток ACX " в сводке по объектам ACX. DDIs для потоков определяются в заголовке acxstreams.h.
Стек потоковой передачи данных ACX
Существует два типа обмена данными для потоковой передачи ACX. Один путь связи используется для управления поведением потоковой передачи для таких команд, как Start, Create и Allocate, который будет использовать стандартные связи ACX. Фреймворк ACX использует очереди ввода-вывода и передаёт WDF запросы через эти очереди. Поведение очереди скрыто от фактического кода драйвера с помощью обратных вызовов Evt и функций ACX, однако драйверу предоставляется возможность предварительно обработать все запросы WDF.
Второй и более интересный путь связи используется для сигналов потоковой передачи звука. Это включает в себя указание драйверу, когда пакет готов и получает данные о завершении обработки пакета.
Основные требования для потоковой передачи сигналов:
- Поддержка воспроизведения Glitch-Free
- Низкая задержка
- Все необходимые блокировки ограничены потоком, о котором идет речь.
- Простота использования для разработчика драйверов
Чтобы сообщить драйверу о состоянии потоковой передачи, ACX использует события с общим буфером и прямые вызовы IRP. Далее будет описано.
Общий буфер
Для взаимодействия с драйвером с клиентом используется общий буфер и событие. Это гарантирует, что клиенту не нужно ждать или опрашивать, и что он может определить все необходимое для продолжения потоковой передачи, сокращая или устраняя необходимость в прямых вызовах IRP.
Драйвер устройства использует общий буфер для передачи клиенту информации о том, какой пакет в данный момент отрисовывается или захватывается. Этот общий буфер включает количество пакетов (на основе 1) последнего завершенного пакета вместе со значением QPC (QueryPerformanceCounter) времени завершения. Для драйвера устройства необходимо указать эти сведения путем вызова AcxRtStreamNotifyPacketComplete. Когда драйвер устройства вызывает AcxRtStreamNotifyPacketComplete, платформа ACX обновит общий буфер с новым числом пакетов и QPC и сигнализирует о событии, совместном с клиентом, чтобы указать, что клиент может считывать новое число пакетов.
Прямые вызовы IRP
Для взаимодействия клиента с драйвером используются прямые вызовы IRP. Это сокращает сложности при обеспечении своевременного обработки запросов WDF и хорошо работает в существующей архитектуре.
Клиент может в любое время запросить текущее число пакетов или указать текущее число пакетов драйверу устройства. Эти запросы вызывают обработчики событий драйвера устройств EvtAcxStreamGetCurrentPacket и EvtAcxStreamSetRenderPacket. Клиент также может запросить текущий пакет захвата, который вызовет обработчик событий драйвера устройства EvtAcxStreamGetCapturePacket.
Сходство с PortCls
Сочетание прямых вызовов IRP и общего буфера, используемого ACX, аналогично обработке завершения буфера в PortCls. IRPs очень похожи, и общий буфер обеспечивает возможность для драйвера напрямую передавать информацию о количестве пакетов и времени без использования IRPs. Драйверам следует избегать любых действий, требующих доступа к блокировкам, которые также используются в путях управления потоками, чтобы предотвратить сбои.
Поддержка большого буфера для воспроизведения с низкой мощностью
Чтобы уменьшить объем энергии, потребляемой при воспроизведении содержимого мультимедиа, важно сократить время, которое APU тратит в состоянии высокой мощности. Так как обычное воспроизведение звука использует 10 мс буферов, APU всегда должен быть активным. Чтобы предоставить APU время, необходимое для уменьшения состояния, драйверы ACX могут объявлять поддержку значительно больших буферов в диапазоне размером 1–2 секунды. Это означает, что APU может просыпаться каждые 1–2 секунды, выполнять операции, необходимые для подготовки следующего 1-2 секундного буфера, а затем переходить к наименьшему возможному состоянию питания до тех пор, пока следующий буфер не потребуется.
В существующих моделях потоковой передачи воспроизведение с низким энергопотреблением поддерживается с помощью разгруженного воспроизведения. Звуковой драйвер поддерживает разгрузку аудиопотока через экспонирование узла AudioEngine на волновом фильтре для конечной точки. Узел AudioEngine предоставляет средства управления движком DSP, используемым драйвером для воспроизведения звука из больших буферов с требуемой обработкой.
Узел AudioEngine предоставляет следующие средства:
- Описание звукового движка, указывающее звуковому стэку, какие контакты в фильтре волн обеспечивают поддержку разгрузки и обратной связи (а также поддержку воспроизведения через хост).
- Диапазон размера буфера, который сообщает стеку звука минимальный и максимальный размер буфера, которые могут поддерживаться для разгрузки. воспроизведение. Диапазон размера буфера может динамически изменяться на основе системного действия.
- Поддержка формата, включая поддерживаемые форматы, текущий формат сочетания устройств и формат устройства.
- Объем, включая поддержку плавного увеличения, так как при большем размере буферов программная громкость не будет реагирующей.
- Loopback Protection, который сообщает драйверу отключить пин-код AudioEngine Loopback, если один или несколько отключенных потоков содержит защищенное содержимое.
- Глобальное состояние FX, чтобы включить или отключить GFX в AudioEngine.
При создании потока на разъеме Offload поток поддерживает Громкость, Local FX и Loopback Protection.
Низкое энергопотребление при воспроизведении с ACX
Платформа ACX использует ту же модель для воспроизведения с низкой мощностью. Драйвер создает три отдельных объекта ACXPIN для потоковой передачи хоста, разгрузки и обратной петли, а также элемент ACXAUDIOENGINE, описывающий, какие из этих штырей используются для хоста, разгрузки и обратной петли. Драйвер добавляет выводы и элемент ACXAUDIOENGINE в ACXCIRCUIT во время создания схемы.
Создание внезагрузного потока
Драйвер также добавит элемент ACXAUDIOENGINE в потоки, созданные для выгрузки, чтобы разрешить управление громкостью, отключением звука и пиковым индикатором.
Схема потоковой передачи
На этой схеме показан драйвер ACX с несколькими стеками.
Каждый драйвер ACX управляет отдельной частью звукового оборудования и может предоставляться другим поставщиком. ACX предоставляет совместимый интерфейс потоковой передачи ядра, позволяющий приложениям работать как есть.
Потоковые закрепления
Каждый ACXCIRCUIT имеет по крайней мере один приемный контакт и один исходный контакт. Эти пин-коды используются платформой ACX для предоставления подключений канала к стеку звука. Для канала отрисовки исходный пин-код используется для управления поведением отрисовки любого потока, созданного из канала. Для цепи захвата разъем потребителя используется для управления поведением захвата любого потока, создаваемого цепью. ACXPIN — это объект, используемый для управления потоковой передачей в звуковом пути. Потоковая передача ACXCIRCUIT отвечает за создание соответствующих объектов ACXPIN для конечной точки аудиопути во время создания цепи и регистрацию ACXPINs в ACX. ACXCIRCUIT требуется только для создания контактов рендеринга или захвата для цепи; Фреймворк ACX создаст другой контакт, необходимый для подключения к цепи и взаимодействия с ней.
Канал потоковой передачи
Если конечная точка состоит из одного канала, это канал потоковой передачи.
Если конечная точка состоит из нескольких каналов, созданных одним или несколькими драйверами устройств, каналы подключаются в определенном порядке, определяемом ACXCOMPOSITETEMPLATE, описывающей созданную конечную точку. Первый канал в конечной точке — это канал потоковой передачи для конечной точки.
Канал потоковой передачи должен использовать AcxRtStreamCreate для создания потока пакетов RT в ответ на EvtAcxCircuitCreateStream. ACXSTREAM, созданный с помощью AcxRtStreamCreate, позволит драйверу потоковой передачи выделить буфер, используемый для потоковой передачи, и управлять потоком потоковой передачи в ответ на потребности клиента и оборудования.
Следующие контуры в конечной точке должны использовать AcxStreamCreate для создания базового потока в ответ на EvtAcxCircuitCreateStream. Объекты ACXSTREAM, созданные с помощью AcxStreamCreate следующими каналами, позволяют драйверам настраивать оборудование в ответ на изменения состояния потока, такие как приостановка или запуск.
Потоковая схема ACXCIRCUIT является первой схемой для получения запросов на создание потока. Запрос включает устройство, пин-код и формат данных (включая режим).
Каждый ACXCIRCUIT в звуковом пути создаст объект ACXSTREAM, представляющий экземпляр потока канала. Платформа ACX связывает объекты ACXSTREAM вместе (так же, как объекты ACXCIRCUIT).
Цепи входящего и исходящего потоков
Создание потока начинается с канала потоковой передачи и перенаправляется в каждый последующий канал в том порядке, в котором подключены каналы. Соединения выполняются между штырями моста, созданными с параметром связи, равным AcxPinCommunicationNone. Платформа ACX создаст один или несколько контактных штырей моста для цепи, если драйвер не добавляет их во время создания цепи.
Для каждой цепи, начиная с потоковой цепи, контакт мостовой схемы AcxPinTypeSource подключается к следующей по цепи. Конечная схема будет иметь контакт конечной точки, описывающий оборудование звуковой конечной точки (например, указание, является ли оно микрофоном или динамиком и подключен ли разъём).
Для каждой цепи, следующей за потоковым контуром, фиксатор моста AcxPinTypeSink будет подключаться к следующей входящей цепи.
Согласование формата потока
Драйвер объявляет поддерживаемые форматы для создания потока, добавляя поддерживаемые форматы для каждого режима в ACXPIN, используемый для создания потока, с использованием AcxPinAssignModeDataFormatList и AcxPinGetRawDataFormatList. Для конечных точек с несколькими каналами можно использовать ACXSTREAMBRIDGE для координации режима и поддержки форматирования между каналами ACX. Поддерживаемые форматы потоков для конечной точки определяются потоками ACXPIN, созданными каналом потоковой передачи. Форматы, используемые следующими цепями, определяются контактным штифтом моста предыдущей цепи в конечной точке.
По умолчанию фреймворк ACX создаст ACXSTREAMBRIDGE между каждым контуром в конечной точке с несколькими контурами. По умолчанию, ACXSTREAMBRIDGE будет использовать стандартный формат RAW режима для контакта моста входной цепи при передаче запроса на создание потока в выходную цепь. Если пин-код моста вышестоящего канала не имеет форматов, будет использоваться исходный формат потока. Если подключенный контакт нисходящей цепи не поддерживает используемый формат, создание потока завершится ошибкой.
Если канал устройства выполняет изменение формата потока, драйвер устройства должен добавить нижестоящий формат в пин-код нижестоящего моста.
Создание потока
Первым шагом в создании потока является создание экземпляра ACXSTREAM для каждого ACXCIRCUIT в звуковом пути конечной точки. ACX вызовет EvtAcxCircuitCreateStream каждой схемы. ACX начнет с головной цепи и по порядку вызовет функцию EvtAcxCircuitCreateStream для каждой цепи, заканчивая хвостовой цепью. Порядок можно изменить, указав флаг AcxStreamBridgeInvertChangeStateSequence (определенный в ACX_STREAM_BRIDGE_CONFIG_FLAGS) для моста Stream. После того, как все цепи создадут объект потока, эти объекты будут обрабатывать логику потоковой передачи.
Запрос на создание потока отправляется в соответствующий ПИН-код, созданный в рамках создания топологии головного канала, вызывая EvtAcxCircuitCreateStream, указанный во время создания головного канала.
Канал потоковой передачи — это вышестоящий канал, который изначально обрабатывает запрос на создание потока.
- Он обновляет структуру ACXSTREAM_INIT, назначая AcxStreamCallbacks и AcxRtStreamCallbacks.
- Он создает объект ACXSTREAM с помощью AcxRtStreamCreate
- Он создает все элементы, относящиеся к потоку (например, ACXVOLUME или ACXAUDIOENGINE).
- Он добавляет элементы в объект ACXSTREAM
- Он возвращает объект ACXSTREAM, созданный в платформу ACX
Затем ACX перенаправит создание потока в следующий нисходящий контур.
- Он обновляет структуру ACXSTREAM_INIT, назначая AcxStreamCallbacks
- Он создает объект ACXSTREAM с помощью AcxStreamCreate
- Он создает все элементы, относящиеся к потоку
- Он добавляет элементы в объект ACXSTREAM
- Он возвращает объект ACXSTREAM, созданный в платформу ACX
Канал связи между каналами в звуковом пути использует объекты ACXTARGETSTREAM. В этом примере каждый контур будет иметь доступ к очереди ввода-вывода для контура перед ним и контура за ним в звуковом пути конечной точки. Кроме того, звуковой путь конечной точки является линейным и двунаправленным. Фактическая обработка очереди операций ввода-вывода выполняется платформой ACX. При создании объекта ACXSTREAM каждая цепь может добавлять информацию о контексте в объект ACXSTREAM для хранения и отслеживания частных данных для потока.
Пример потока отрисовки
Создание потока отрисовки в звуковом пути конечной точки, состоящего из трех каналов: DSP, CODEC и AMP. Схема DSP функционирует как схема потоковой передачи и предоставляет обработчик EvtAcxPinCreateStream. Канал DSP также работает в качестве канала фильтра: в зависимости от режима потока и конфигурации, он может применять обработку сигналов к звуковым данным. Канал CODEC представляет DAC, предоставляя функциональные возможности приемника звука. Канал AMP представляет аналоговое оборудование между DAC и динамиком. Канал AMP может обрабатывать обнаружение джека или другие сведения о оборудовании конечной точки.
- AudioKSE вызывает NtCreateFile для создания потока.
- Это фильтрует через ACX и заканчивается вызовом функции EvtAcxPinCreateStream схемы DSP с закреплением, форматом данных (включая режим) и сведениями об устройстве.
- Канал DSP проверяет сведения о формате данных, чтобы убедиться, что он может обрабатывать созданный поток.
- Канал DSP создает объект ACXSTREAM для представления потока.
- Канал DSP выделяет структуру частного контекста и связывает ее с ACXSTREAM.
- Схема DSP возвращает поток выполнения во фреймворк ACX, который затем вызывает следующую схему в аудиотракте оконечной точки, схему CODEC.
- Цепь CODEC проверяет информацию о формате данных, чтобы подтвердить, что она может обрабатывать визуализацию данных.
- Канал CODEC выделяет структуру частного контекста и связывает ее с ACXSTREAM.
- Контур CODEC добавляет себя как приемник потока для ACXSTREAM.
- Канал CODEC возвращает последовательность выполнения в платформу ACX, которая затем передает управление следующему каналу в конечном звуковом тракте, каналу AMP.
- Канал AMP выделяет структуру частного контекста и связывает ее с ACXSTREAM.
- Канал AMP возвращает поток выполнения в платформу ACX. На этом этапе создание потока завершено.
Большие потоки буфера
Большие потоки буферов создаются в ACXPIN, назначенном для разгрузки элементом ACXCIRCUIT ACXAUDIOENGINE.
Чтобы поддерживать потоки разгрузки, драйвер устройства должен выполнить следующие действия во время создания канала потоковой передачи:
- Создайте объекты ACXPIN Host, Offload и Loopback и добавьте их в ACXCIRCUIT.
- Создание элементов ACXVOLUME, ACXMUTE и ACXPEAKMETER. Они не будут добавлены непосредственно в ACXCIRCUIT.
- Инициализирует структуру ACX_AUDIOENGINE_CONFIG, назначая объекты HostPin, OffloadPin, LoopbackPin, VolumeElement, MuteElement и PeakMeterElement.
- Создайте элемент ACXAUDIOENGINE.
Драйверам потребуется выполнить аналогичные действия, чтобы добавить элемент ACXSTREAMAUDIOENGINE при создании потока на выгружаемой точке.
Распределение ресурсов для потоковых данных
Модель потоковой передачи для ACX основана на пакетах с поддержкой одного или двух пакетов для потока. Для оператора рендеринга или захвата ACXPIN в потоковой схеме подаётся запрос на выделение пакетов памяти, используемых в потоке. Для поддержки повторного балансировки выделенная память должна быть системной памятью, а не памятью устройства, сопоставленной с системой. Драйвер может использовать существующие функции WDF для выполнения выделения и возвратить массив указателей на выделенные буферы. Если драйверу требуется один непрерывный блок, он может выделить оба пакета в виде одного буфера, возвращая указатель на смещение буфера в качестве второго пакета.
Если выделен один пакет, пакет должен быть выровнен по страницам и сопоставляется дважды в пользовательском режиме:
| пакет 0 | пакет 0 |
Это позволяет GetBuffer возвращать указатель на один непрерывный буфер памяти, который может охватывать конец буфера до начала без необходимости обрабатывать оболочку доступа к памяти.
Если выделено два пакета, они сопоставляются с пользовательским режимом:
| пакет 0 | пакет 1 |
При первоначальной потоковой передаче пакетов ACX в начале выделяется только два пакета. Сопоставление виртуальной памяти клиента будет оставаться допустимым и неизменным на протяжении всего срока существования потока после выполнения операций выделения и сопоставления. Существует одно событие, связанное с потоком, чтобы указать завершение обработки обоих пакетов. Также будет общий буфер, который платформа ACX будет использовать для передачи информации о пакете, завершившем обработку события.
Размеры пакетов в больших потоках буферов данных
При предоставлении поддержки больших буферов драйвер также предоставит коллбек, используемый для определения минимальных и максимальных размеров пакетов для воспроизведения больших буферов. Размер пакета для выделения буфера потока определяется на основе минимального и максимального размера.
Так как минимальный и максимальный размер буфера могут быть переменными, драйвер может завершить вызов выделения пакетов, если минимальное и максимальное значение изменилось.
Указание ограничений буфера ACX
Чтобы указать ограничения буфера ACX, драйверы ACX могут использовать параметр свойств KS/PortCls — KSAUDIO_PACKETSIZE_CONSTRAINTS2 и структуру KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT.
В следующем примере кода показано, как задать ограничения размера буфера для буферов WaveRT для различных режимов обработки сигналов.
//
// Describe buffer size constraints for WaveRT buffers
// Note: 10msec for each of the Modes is the default system behavior.
//
static struct
{
KSAUDIO_PACKETSIZE_CONSTRAINTS2 TransportPacketConstraints; // 1
KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT AdditionalProcessingConstraints[4]; // + 4 = 5
} DspR_RtPacketSizeConstraints =
{
{
10 * HNSTIME_PER_MILLISECOND, // 10 ms minimum processing interval
FILE_BYTE_ALIGNMENT, // 1 byte packet size alignment
0, // no maximum packet size constraint
5, // 5 processing constraints follow
{
STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, // constraint for raw processing mode
0, // NA samples per processing frame
10 * HNSTIME_PER_MILLISECOND, // 100000 hns (10ms) per processing frame
},
},
{
{
STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, // constraint for default processing mode
0, // NA samples per processing frame
10 * HNSTIME_PER_MILLISECOND, // 100000 hns (10ms) per processing frame
},
{
STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, // constraint for movie communications mode
0, // NA samples per processing frame
10 * HNSTIME_PER_MILLISECOND, // 100000 hns (10ms) per processing frame
},
{
STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, // constraint for default media mode
0, // NA samples per processing frame
10 * HNSTIME_PER_MILLISECOND, // 100000 hns (10ms) per processing frame
},
{
STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, // constraint for movie movie mode
0, // NA samples per processing frame
10 * HNSTIME_PER_MILLISECOND, // 100000 hns (10ms) per processing frame
},
}
};
Структура DSP_DEVPROPERTY используется для хранения ограничений.
typedef struct _DSP_DEVPROPERTY {
const DEVPROPKEY *PropertyKey;
DEVPROPTYPE Type;
ULONG BufferSize;
__field_bcount_opt(BufferSize) PVOID Buffer;
} DSP_DEVPROPERTY, PDSP_DEVPROPERTY;
И создаётся массив этих структур.
const DSP_DEVPROPERTY DspR_InterfaceProperties[] =
{
{
&DEVPKEY_KsAudio_PacketSize_Constraints2, // Key
DEVPROP_TYPE_BINARY, // Type
sizeof(DspR_RtPacketSizeConstraints), // BufferSize
&DspR_RtPacketSizeConstraints, // Buffer
},
};
Далее в функции EvtCircuitCompositeCircuitInitialize вспомогательная функция AddPropertyToCircuitInterface используется для добавления массива свойств интерфейса в схему.
// Set RT buffer constraints.
//
status = AddPropertyToCircuitInterface(Circuit, ARRAYSIZE(DspC_InterfaceProperties), DspC_InterfaceProperties);
Вспомогательная функция AddPropertyToCircuitInterface принимает в качестве параметра значение AcxCircuitGetSymbolicLinkName для цепи, а затем вызывает IoGetDeviceInterfaceAlias, чтобы определить звуковой интерфейс, используемый этой цепью.
Затем функция SetDeviceInterfacePropertyDataMultiple вызывает функцию IoSetDeviceInterfacePropertyData , чтобы изменить текущее значение свойства интерфейса устройства — значения звуковых свойств KS в звуковом интерфейсе для ACXCIRCUIT.
PAGED_CODE_SEG
NTSTATUS AddPropertyToCircuitInterface(
_In_ ACXCIRCUIT Circuit,
_In_ ULONG PropertyCount,
_In_reads_opt_(PropertyCount) const DSP_DEVPROPERTY * Properties
)
{
PAGED_CODE();
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING acxLink = {0};
UNICODE_STRING audioLink = {0};
WDFSTRING wdfLink = AcxCircuitGetSymbolicLinkName(Circuit);
bool freeStr = false;
// Get the underline unicode string.
WdfStringGetUnicodeString(wdfLink, &acxLink);
// Make sure there is a string.
if (!acxLink.Length || !acxLink.Buffer)
{
status = STATUS_INVALID_DEVICE_STATE;
DrvLogError(g_BthLeVDspLog, FLAG_INIT,
L"AcxCircuitGetSymbolicLinkName failed, Circuit: %p, %!STATUS!",
Circuit, status);
goto exit;
}
// Get the audio interface.
status = IoGetDeviceInterfaceAlias(&acxLink, &KSCATEGORY_AUDIO, &audioLink);
if (!NT_SUCCESS(status))
{
DrvLogError(g_BthLeVDspLog, FLAG_INIT,
L"IoGetDeviceInterfaceAlias failed, Circuit: %p, symbolic link name: %wZ, %!STATUS!",
Circuit, &acxLink, status);
goto exit;
}
freeStr = true;
// Set specified properties on the audio interface for the ACXCIRCUIT.
status = SetDeviceInterfacePropertyDataMultiple(&audioLink, PropertyCount, Properties);
if (!NT_SUCCESS(status))
{
DrvLogError(g_BthLeVDspLog, FLAG_INIT,
L"SetDeviceInterfacePropertyDataMultiple failed, Circuit: %p, symbolic link name: %wZ, %!STATUS!",
Circuit, &audioLink, status);
goto exit;
}
status = STATUS_SUCCESS;
exit:
if (freeStr)
{
RtlFreeUnicodeString(&audioLink);
freeStr = false;
}
return status;
}
Изменения состояния потока
При изменении состояния потока каждый объект потока в конечной точке аудиопути для потока получит событие уведомления из структуры ACX. Порядок, в котором это происходит, зависит от изменения состояния и направления потока.
Для потоков визуализации, переходящих из менее активного состояния в более активное, потоковая схема (зарегистрировавшая ПРИЕМНИК) будет первой получать событие. После обработки события его получит следующая цепь в аудиопути к конечной точке.
Для потоков рендеринга, переходящих из более активного состояния в менее активное, передающий контур получит событие последним.
Для потоков Capture, переходящих из менее активного состояния в более активное, передающий контур получит событие последним.
Для потоков записи, поступающих из более активного состояния в менее активное состояние, канал потоковой передачи получит событие первым.
Приведенное выше упорядочение — это значение по умолчанию, предоставляемое платформой ACX. Драйвер может запрашивать противоположное поведение, задав AcxStreamBridgeInvertChangeStateSequence (определённый в ACX_STREAM_BRIDGE_CONFIG_FLAGS) при создании ACXSTREAMBRIDGE, который драйвер добавляет в канал потоковой передачи.
Потоковая передача звуковых данных
После создания потока и выделения соответствующих буферов поток находится в состоянии приостановки ожидания запуска потока. Когда клиент переводит поток в состояние Play, фреймворк ACX вызовет все объекты ACXSTREAM, связанные с потоком, чтобы указать, что состояние потока в режиме Play. Затем ACXPIN будет помещен в состояние воспроизведения, в котором данные начнут передаваться.
Обработка звуковых данных
После создания потока и выделения ресурсов приложение вызовет Start в потоке, чтобы начать воспроизведение. Обратите внимание, что приложение должно вызывать GetBuffer/ReleaseBuffer перед запуском потока, чтобы убедиться, что первый пакет, который начнет воспроизводиться немедленно, будет иметь допустимые звуковые данные.
Клиент начинается с предварительного переката буфера. Когда клиент вызывает ReleaseBuffer, это приведет к вызову функции в AudioKSE, которая будет задействована на уровне ACX и вызовет EvtAcxStreamSetRenderPacket на активном ACXSTREAM. Свойство будет включать индекс пакета (на основе 0) и, если это возможно, флаг EOS со смещением байтов конца потока в текущем пакете. После завершения обработки пакета потоковой схемой, он активирует уведомление о завершении заполнения буфера, которое разблокирует клиентов, ожидающих возможности заполнить следующий пакет данными для отрисовки аудио.
Режим потоковой передачи с управляемым таймером поддерживается и указывается с помощью значения PacketCount 1 при вызове обратного вызова EvtAcxStreamAllocateRtPackets драйвера.
Запись звуковых данных
После создания потока и выделения ресурсов приложение вызовет Start в потоке, чтобы начать воспроизведение.
При запуске потока исходный канал заполняет пакет записи звуковыми данными. После заполнения первого пакета исходный канал освобождает пакет в платформу ACX. На этом этапе фреймворк ACX сигнализирует о событии уведомления потока.
После сигнала уведомления о потоке клиент может отправить KSPROPERTY_RTAUDIO_GETREADPACKET, чтобы получить индекс (начиная с нуля) пакета, завершившего захват. Когда клиент отправил GETCAPTUREPACKET, драйвер может предположить, что все предыдущие пакеты обработаны и доступны для заполнения.
Для серийной съемки исходная схема может отправить новый пакет в среду ACX сразу после вызова GETREADPACKET.
Клиент также может использовать KSPROPERTY_RTAUDIO_PACKETVREGISTER для получения указателя на структуру RTAUDIO_PACKETVREGISTER для потока. Эта структура будет обновлена фреймворком ACX перед завершением передачи сигнального пакета.
Устаревшее поведение потоковой передачи ядра KS
Могут возникнуть ситуации, например, когда драйвер реализует серийную съемку (как в реализации функции обнаружения ключевых слов), где нужно использовать старую обработку потоковых пакетов ядра вместо PacketVRegister. Чтобы использовать предыдущее поведение на основе пакетов, драйвер должен вернуть STATUS_NOT_SUPPORTED для KSPROPERTY_RTAUDIO_PACKETVREGISTER.
В следующем примере показано, как это сделать в AcxStreamInitAssignAcxRequestPreprocessCallback для ACXSTREAM. Дополнительные сведения см. в статье AcxStreamDispatchAcxRequest.
Circuit_EvtStreamRequestPreprocess(
_In_ ACXOBJECT Object,
_In_ ACXCONTEXT DriverContext,
_In_ WDFREQUEST Request)
{
ACX_REQUEST_PARAMETERS params;
PCIRCUIT_STREAM_CONTEXT streamCtx;
streamCtx = GetCircuitStreamContext(Object);
// The driver would define the pin type to track which pin is the keyword pin.
// The driver would add this to the driver-defined context when the stream is created.
// The driver would use AcxStreamInitAssignAcxRequestPreprocessCallback to set
// the Circuit_EvtStreamRequestPreprocess callback for the stream.
if (streamCtx && streamCtx->PinType == CapturePinTypeKeyword)
{
if (IsEqualGUID(params.Parameters.Property.Set, KSPROPSETID_RtAudio) &&
params.Parameters.Property.Id == KSPROPERTY_RTAUDIO_PACKETVREGISTER)
{
status = STATUS_NOT_SUPPORTED;
outDataCb = 0;
WdfRequestCompleteWithInformation(Request, status, outDataCb);
return;
}
}
(VOID)AcxStreamDispatchAcxRequest((ACXSTREAM)Object, Request);
}
Положение потока
Платформа ACX вызовет обратный вызов EvtAcxStreamGetPresentationPosition , чтобы получить текущую позицию потока. Текущая позиция потока будет включать PlayOffset и WriteOffset.
Модель потоковой передачи WaveRT позволяет звуковому драйверу предоставлять клиенту регистр положения HW. Модель потоковой передачи ACX не поддерживает предоставление регистров HW, так как они препятствуют перебалансировки.
Каждый раз, когда канал потоковой передачи завершает пакет, он вызывает AcxRtStreamNotifyPacketComplete с 0-м индексом пакетов и значением QPC, принятым как можно ближе к завершению пакета (например, значение QPC можно вычислить подпрограммой службы прерываний). Эта информация доступна клиентам через KSPROPERTY_RTAUDIO_PACKETVREGISTER, которая возвращает указатель на структуру, содержащую CompletedPacketCount, CompletedPacketQPC и значение, объединяющее два (что позволяет клиенту гарантировать, что CompletedPacketCount и CompletedPacketQPC находятся в одном пакете).
Переходы состояния потока
После создания потока ACX передаст поток в разные состояния с помощью следующих обратных вызовов:
- EvtAcxStreamPrepareHardware передаст поток из состояния AcxStreamStateStop в состояние AcxStreamStatePause. Драйвер должен зарезервировать необходимое оборудование, такое как двигатели DMA, когда он получает EvtAcxStreamPrepareHardware.
- EvtAcxStreamRun передаст поток из состояния AcxStreamStatePause в состояние AcxStreamStateRun.
- EvtAcxStreamPause передаст поток из состояния AcxStreamStateRun в состояние AcxStreamStatePause.
- EvtAcxStreamReleaseHardware передаст поток из состояния AcxStreamStatePause в состояние AcxStreamStateStop. Драйвер должен освободить необходимые аппаратные ресурсы, например, движки DMA, при получении EvtAcxStreamReleaseHardware.
После получения обратного вызова EvtAcxStreamReleaseHardware поток может получить обратный вызов EvtAcxStreamPrepareHardware. Это передаст поток обратно в состояние AcxStreamStatePause.
Выделение пакетов с помощью EvtAcxStreamAllocateRtPackets обычно происходит до первого вызова EvtAcxStreamPrepareHardware. Выделенные пакеты обычно освобождаются с помощью EvtAcxStreamFreeRtPackets после последнего вызова EvtAcxStreamReleaseHardware. Это упорядочение не гарантируется.
Состояние AcxStreamStateAcquire не используется. ACX устраняет необходимость для драйвера находиться в состоянии принудительного получения, поскольку это состояние подразумевается в функциях подготовки оборудования (EvtAcxStreamPrepareHardware) и освобождения оборудования (EvtAcxStreamReleaseHardware).
Поддержка больших буферных потоков и механизма разгрузки
ACX использует элемент ACXAUDIOENGINE для обозначения ACXPIN, который будет обрабатывать создание разгрузочного потока и различные элементы, необходимые для регулировки громкости разгрузочного потока, отключения звука и состояния пикового индикатора. Это аналогично существующему узлу звукового модуля в драйверах WaveRT.
Процесс закрытия потока
Когда клиент закрывает поток, драйвер получит EvtAcxStreamPause и EvtAcxStreamReleaseHardware перед тем, как объект ACXSTREAM будет удален платформой ACX Framework. Драйвер может предоставить стандартную запись WDF EvtCleanupCallback в структуре WDF_OBJECT_ATTRIBUTES при вызове AcxStreamCreate для выполнения окончательной очистки для ACXSTREAM. WDF вызывает EvtCleanupCallback, когда платформа пытается удалить объект. Не используйте EvtDestroyCallback, который вызывается только после выпуска всех ссылок на объект, что является неопределенным.
Драйвер должен очистить ресурсы системной памяти, связанные с объектом ACXSTREAM в EvtCleanupCallback, если ресурсы еще не были удалены в EvtAcxStreamReleaseHardware.
Важно, чтобы драйвер не освобождал ресурсы, поддерживающие поток, до тех пор, пока клиент не запросит это.
Состояние AcxStreamStateAcquire не используется. ACX устраняет необходимость наличия состояния захвата у драйвера, так как это состояние является неявным благодаря функциям подготовки оборудования (EvtAcxStreamPrepareHardware) и вызовами освобождения оборудования (EvtAcxStreamReleaseHardware).
Удаление и аннулирование потоков
Если драйвер определяет, что поток стал недействительным (например, джек отключается), схема завершит работу всех потоков.
Очистка памяти потоковой передачи
Удаление ресурсов потока можно сделать в очистке контекста потока драйвера (а не уничтожено). Никогда не помещайте в контекст объекта все, что совместно используется в контексте объекта, уничтожать обратный вызов. Это руководство относится ко всем объектам ACX.
Обратный вызов уничтожения вызывается после того, как последний ссылочный объект исчезает, когда он неизвестен.
Как правило, обратный вызов очистки потока вызывается при закрытии дескриптора. Одно из исключений заключается в том, что драйвер создал поток в обратном вызове. Если acX не удалось добавить этот поток в его поток-мост незадолго до возвращения из операции создания потока, поток отменяется асинхронный, а текущий поток возвращает ошибку клиенту create-stream. На данный момент поток не должен выделять выделения mem. Дополнительные сведения см. в EVT_ACX_STREAM_RELEASE_HARDWARE обратном вызове.
Последовательность очистки памяти потока
Буфер потока — это системный ресурс, и он должен быть выпущен только в том случае, если клиент пользовательского режима закрывает дескриптор потока. Буфер (который отличается от аппаратных ресурсов устройства) имеет такую же продолжительность существования, как и обработчик потока. Когда клиент закрывает дескриптор, ACX вызывает обратный вызов очистки объектов потока, а затем обратный вызов удаления объекта потока, когда ссылка на объект становится равной нулю.
Для ACX можно отложить удаление объекта STREAM на рабочий элемент, если драйвер создал объект STREAM, но затем произошло сбой в обратном вызове create-stream. Чтобы предотвратить взаимоблокировку с завершающимся потоком WDF, ACX отложит удаление на другой поток. Чтобы избежать возможных побочных эффектов этого поведения (отложенный выпуск ресурсов), драйвер может освободить выделенные для потока ресурсы перед возвращением ошибки во время создания потока.
Драйвер должен освободить звуковые буферы, когда ACX вызывает обратный вызов EVT_ACX_STREAM_FREE_RTPACKETS. Этот callback вызывается, когда пользователь закрывает дескрипторы потоков.
Поскольку буферы RT сопоставляются в пользовательском режиме, срок их существования равен сроку существования дескриптора. Драйвер не должен пытаться освободить или освободить звуковые буферы, прежде чем ACX вызывает этот обратный вызов.
Обратный вызов EVT_ACX_STREAM_FREE_RTPACKETS должен быть вызван после обратного вызова EVT_ACX_STREAM_RELEASE_HARDWARE и завершиться до EvtDeviceReleaseHardware.
Этот обратный вызов может произойти после того, как драйвер обработал аппаратный обратный вызов WDF, так как клиент пользовательского режима может держаться на его дескрипторах в течение длительного времени. Драйвер не должен пытаться ждать, пока дескрипторы не исчезнут; это просто приведёт к появлению ошибки проверки 0x9f DRIVER_POWER_STATE_FAILURE. Дополнительные сведения см. в функции обратного вызова EVT_WDF_DEVICE_RELEASE_HARDWARE.
В этом коде EvtDeviceReleaseHardware из примера драйвера ACX показан пример вызова AcxDeviceRemoveCircuit и последующего выпуска потоковой памяти h/w.
RETURN_NTSTATUS_IF_FAILED(AcxDeviceRemoveCircuit(Device, devCtx->Render));
RETURN_NTSTATUS_IF_FAILED(AcxDeviceRemoveCircuit(Device, devCtx->Capture));
// NOTE: Release streaming h/w resources here.
CSaveData::DestroyWorkItems();
CWaveReader::DestroyWorkItems();
Сводка:
Освобождение аппаратных ресурсов WDF устройства -> освобождение аппаратных ресурсов устройства
AcxStreamFreeRtPackets —> освободить звуковой буфер, связанный с дескриптором
Дополнительные сведения об управлении объектами WDF и контурами см. в статье ACX WDF Driver Lifetime Management.
DDIs потоковой передачи
Структуры потоковой передачи
структура ACX_RTPACKET
Эта структура представляет один выделенный пакет. PacketBuffer может быть дескриптором WDFMEMORY, MDL или буфером. Она имеет связанную функцию инициализации, ACX_RTPACKET_INIT.
ACX_STREAM_CALLBACKS
Эта структура определяет обратные вызовы драйвера для потоковой передачи в платформу ACX. Эта структура является частью ACX_PIN_CONFIG структуры.
Обратные вызовы потоковой передачи
EvtAcxStreamAllocateRtPackets
Событие EvtAcxStreamAllocateRtPackets сообщает драйверу выделить RtPackets для потоковой передачи. AcxRtStream получит PacketCount = 2 для потоковой передачи на основе событий или PacketCount = 1 для потоковой передачи на основе таймера. Если драйвер использует один буфер для обоих пакетов, второй rtPacketBuffer должен иметь WDF_MEMORY_DESCRIPTOR с типом WdfMemoryDescriptorTypeInvalid и значением rtPacketOffset, соответствующим концу первого пакета (packet[2].RtPacketOffset = packet[1].RtPacketOffset + packet[1].RtPacketSize).
EvtAcxStreamFreeRtPackets
Событие EvtAcxStreamFreeRtPackets сообщает драйверу освободить RtPackets, которые были выделены в предыдущем вызове EvtAcxStreamAllocateRtPackets. Те же пакеты из этого вызова включены.
EvtAcxStreamGetHwLatency
Событие EvtAcxStreamGetHwLatency сообщает драйверу обеспечить задержку потока для конкретного канала данного потока (общая задержка будет суммой задержки разных каналов). FifoSize указан в байтах, а задержка измеряется в единицах по 100 наносекунд.
EvtAcxStreamSetRenderPacket
Событие EvtAcxStreamSetRenderPacket сообщает драйверу, какой пакет был выпущен клиентом. Если сбои отсутствуют, этот пакет должен быть (CurrentRenderPacket + 1), где CurrentRenderPacket является пакетом, из которого драйвер в настоящее время выполняет потоковую передачу.
Флаги могут быть 0 или KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM = 0x200, указывающие, что пакет является последним пакетом в потоке, и EosPacketLength является допустимой длиной в байтах для пакета. Дополнительные сведения см. в разделе OptionsFlags в KSSTREAM_HEADER структуре (ks.h).
Драйвер должен продолжать увеличивать CurrentRenderPacket, так как пакеты отрисовываются вместо изменения currentRenderPacket в соответствии с этим значением.
EvtAcxStreamGetCurrentPacket
EvtAcxStreamGetCurrentPacket указывает драйверу, какой пакет (с индексом, начинающимся с 0) в настоящее время отрисовывается на аппаратном обеспечении или заполняется в данный момент оборудованием для захвата.
EvtAcxStreamGetCapturePacket
EvtAcxStreamGetCapturePacket сообщает драйверу указать, какой из пакетов, отсчёт которых начинается с нуля, был полностью заполнен самым последним, включая значение QPC в момент начала его заполнения драйвером.
EvtAcxStreamGetPresentationPosition
EvtAcxStreamGetPresentationPosition сообщает драйверу указывать текущую позицию вместе со значением QPC во время вычисления текущей позиции.
СОБЫТИЯ СОСТОЯНИЯ ПОТОКА
Состояние потоковой передачи для ACXSTREAM управляется следующими API.
EVT_ACX_STREAM_PREPARE_HARDWARE
EVT_ACX_STREAM_RELEASE_HARDWARE
API потоковой передачи ACX
AcxStreamCreate
AcxStreamCreate создает поток ACX, который можно использовать для управления поведением потоковой передачи.
AcxRtStreamCreate
AcxRtStreamCreate создает поток ACX, который можно использовать для управления поведением потоковой передачи и обработки выделения пакетов и обмена данными о состоянии потоковой передачи.
AcxRtStreamNotifyPacketComplete
Драйвер вызывает этот API ACX при завершении пакета. Время завершения пакета и индекс пакета на основе 0 включены для повышения производительности клиента. Платформа ACX устанавливает любые события уведомлений, связанные с потоком.
См. также
Общие сведения о расширениях аудиоклассов ACX