Share via


ACX 스트리밍

이 항목에서는 결함이 없는 오디오 환경에 중요한 ACX 스트리밍 및 관련 버퍼링에 대해 설명합니다. 드라이버가 스트림 상태에 대해 통신하고 스트림에 대한 버퍼를 관리하는 데 사용하는 메커니즘을 설명합니다. 일반적인 ACX 오디오 용어 목록 및 ACX 소개는 ACX 오디오 클래스 확장 개요를 참조하세요.

참고 항목

ACX 헤더 및 라이브러리는 WDK 10.0.22621.2428(2023년 10월 24일 릴리스)에 포함되지 않지만 이전 버전과 WDK의 최신(25000 시리즈 빌드) Insider Preview에서 사용할 수 있습니다. WDK의 미리 보기 버전에 대한 자세한 내용은 WDK (Windows 드라이버 키트)의 미리 보기 버전 설치를 참조하세요.

ACX 스트리밍 유형

AcxStream은 특정 회로의 하드웨어에서 오디오 스트림을 나타냅니다. AcxStream은 하나 이상의 AcxElements와 유사한 개체를 집계할 수 있습니다.

ACX 프레임워크는 두 가지 스트림 형식을 지원합니다. 첫 번째 스트림 유형인 RT 패킷 스트림은 RT 패킷을 할당하고 RT 패킷을 사용하여 스트림 상태 전환과 함께 디바이스 하드웨어로 또는 디바이스 하드웨어에서 오디오 데이터를 전송할 수 있도록 지원합니다. 두 번째 스트림 형식인 기본 스트림은 스트림 상태 전환에 대한 지원만 제공합니다.

단일 회로 엔드포인트에서 회로는 RT 패킷 스트림을 만드는 스트리밍 회로여야 합니다. 둘 이상의 회로가 연결되어 엔드포인트를 만드는 경우 엔드포인트의 첫 번째 회로는 스트리밍 회로이며 RT 패킷 스트림을 만듭니다. 연결된 회로는 스트림 상태 전환과 관련된 이벤트를 수신하는 기본 스트림 만듭니다.

자세한 내용은 ACX 개체 요약의 ACX 스트림을 참조하세요. 스트림에 대한 DDI는 acxstreams.h 헤더에 정의됩니다.

ACX 스트리밍 통신 스택

ACX 스트리밍에는 두 가지 유형의 통신이 있습니다. 하나의 통신 경로는 표준 ACX 통신을 사용하는 시작, 만들기 및 할당과 같은 명령의 스트리밍 동작을 제어하는 데 사용됩니다. ACX 프레임워크는 IO 큐를 사용하고 큐를 사용하여 WDF 요청을 전달합니다. 큐 동작은 Evt 콜백 및 ACX 함수를 사용하여 실제 드라이버 코드에서 숨겨지지만 드라이버에는 모든 WDF 요청을 미리 처리할 수 있는 기회도 제공됩니다.

두 번째 및 더 흥미로운 통신 경로는 오디오 스트리밍 신호에 사용됩니다. 여기에는 패킷이 준비되면 드라이버에 알리고 드라이버가 패킷 처리를 완료한 시점의 데이터를 수신하는 작업이 포함됩니다. 

스트리밍 신호에 대한 기본 요구 사항:

  • Glitch 무료 재생 지원
    • 짧은 대기 시간
    • 필요한 잠금은 문제의 스트림으로 제한됩니다.
  • 드라이버 개발자를 위한 사용 편의성

드라이버와 통신하여 스트리밍 상태를 알리기 위해 ACX는 공유 버퍼와 직접 IRP 호출이 있는 이벤트를 사용합니다. 다음에 설명합니다.

공유 버퍼

드라이버에서 클라이언트로 통신하기 위해 공유 버퍼 및 이벤트가 사용됩니다. 이렇게 하면 클라이언트가 대기하거나 폴링할 필요가 없으며, 클라이언트는 직접 IRP 호출의 필요성을 줄이거나 제거하면서 스트리밍을 계속하는 데 필요한 모든 것을 결정할 수 있습니다.

디바이스 드라이버는 공유 버퍼를 사용하여 패킷이 렌더링되거나 캡처되는 클라이언트와 통신합니다. 이 공유 버퍼에는 완료 시간의 QPC(QueryPerformanceCounter) 값과 함께 마지막으로 완료된 패킷의 패킷 수(1부터)가 포함됩니다. 디바이스 드라이버의 경우 AcxRtStreamNotifyPacketComplete를 호출하여 이 정보를 표시해야 합니다. 디바이스 드라이버가 AcxRtStreamNotifyPacketComplete를 호출하면 ACX 프레임워크는 공유 버퍼를 새 패킷 수 및 QPC로 업데이트하고 클라이언트가 새 패킷 수를 읽을 수 있음을 나타내기 위해 클라이언트와 공유된 이벤트를 알릴 것입니다.

직접 IRP 호출

클라이언트에서 드라이버로 통신하기 위해 직접 IRP 호출이 사용됩니다. 이렇게 하면 WDF 요청이 적시에 처리되고 기존 아키텍처에서 잘 작동하는 것으로 입증되도록 하는 복잡성이 줄어듭니다.

클라이언트는 언제든지 현재 패킷 수를 요청하거나 디바이스 드라이버에 대한 현재 패킷 수를 나타낼 수 있습니다. 이러한 요청은 EvtAcxStreamGetCurrentPacket 및 EvtAcxStreamSetRenderPacket 디바이스 드라이버 이벤트 처리기를 호출합니다. 클라이언트는 EvtAcxStreamGetCapturePacket 디바이스 드라이버 이벤트 처리기를 호출하는 현재 캡처 패킷을 요청할 수도 있습니다.

PortCls와 유사점

ACX에서 사용되는 직접 IRP 호출 및 공유 버퍼의 조합은 PortCls에서 버퍼 완성 처리를 전달하는 방법과 유사합니다. IRP는 매우 유사하며 공유 버퍼는 드라이버가 IRP에 의존하지 않고 패킷 수와 타이밍을 직접 통신하는 기능을 도입합니다.   드라이버는 스트림 제어 경로에도 사용되는 잠금에 액세스해야 하는 작업을 수행하지 않도록 해야 합니다. 이 작업은 결함을 방지하기 위해 필요합니다. 

저전력 재생을 위한 대규모 버퍼 지원

미디어 콘텐츠를 재생할 때 소비되는 전력량을 줄이려면 APU가 고강도 상태에서 소비하는 시간을 줄이는 것이 중요합니다. 일반 오디오 재생은 10ms 버퍼를 사용하므로 APU는 항상 활성 상태여야 합니다. 상태를 줄이는 데 필요한 시간을 APU에 제공하기 위해 ACX 드라이버는 1-2초 크기 범위에서 훨씬 더 큰 버퍼에 대한 지원을 보급할 수 있습니다. 즉, APU는 1-2초마다 한 번씩 절전 모드를 해제하고, 다음 1-2초 버퍼를 준비하기 위해 최대 속도로 필요한 작업을 수행하고, 다음 버퍼가 필요할 때까지 가능한 가장 낮은 전원 상태로 전환할 수 있습니다. 

기존 스트리밍 모델에서는 오프로드 재생을 통해 저전력 재생이 지원됩니다. 오디오 드라이버는 엔드포인트에 대한 웨이브 필터에 AudioEngine 노드를 노출하여 오프로드 재생에 대한 지원을 보급합니다. AudioEngine 노드는 드라이버가 원하는 처리로 큰 버퍼에서 오디오를 렌더링하는 데 사용하는 DSP 엔진을 제어하는 수단을 제공합니다.

AudioEngine 노드는 다음과 같은 기능을 제공합니다.

  • 오디오 엔진 설명은 오디오 스택에 웨이브 필터의 핀이 오프로드 및 루프백 지원(및 호스트 재생 지원)을 제공하는지 알려줍니다. 
  • 오디오 스택에 오프로드에 대해 지원될 수 있는 최소 및 최대 버퍼 크기를 알려주는 버퍼 크기 범위입니다. 재생. 버퍼 크기 범위는 시스템 활동에 따라 동적으로 변경됩니다. 
  • 지원되는 형식, 현재 디바이스 조합 형식 및 디바이스 형식을 비롯한 형식 지원 
  • 더 큰 버퍼를 사용하는 경우 소프트웨어 볼륨은 응답성이 없으므로 증가 지원을 포함한 볼륨입니다.
  • 하나 이상의 오프로드된 스트림에 보호된 콘텐츠가 포함된 경우 드라이버에 AudioEngine 루프백 핀을 음소거하도록 지시하는 루프백 보호입니다. 
  • AudioEngine에서 GFX를 사용하거나 사용하지 않도록 설정하려면 글로벌 FX 상태입니다. 

스트림이 오프로드 핀에 만들어지면 스트림은 볼륨, 로컬 FX 및 루프백 보호를 지원합니다. 

ACX를 사용한 저전력 재생

ACX 프레임워크는 저전력 재생에 동일한 모델을 사용합니다. 드라이버는 호스트, 오프로드 및 루프백 스트리밍을 위한 세 개의 개별 ACXPIN 개체와 호스트, 오프로드 및 루프백에 사용되는 핀을 설명하는 ACXAUDIOENGINE 요소를 만듭니다. 드라이버는 회로를 만드는 동안 ACXCIRCUIT에 핀과 ACXAUDIOENGINE 요소를 추가합니다.

오프로드된 스트림 만들기

또한 드라이버는 볼륨, 음소거 및 피크 미터를 제어할 수 있도록 오프로드용으로 만든 스트림에 ACXAUDIOENGINE 요소를 추가합니다.

스트리밍 다이어그램

이 다이어그램은 다중 스택 ACX 드라이버를 보여줍니다.

커널 스트리밍 인터페이스가 맨 위에 있는 DSP, CODEC 및 AMP 상자를 보여 주는 다이어그램

각 ACX 드라이버는 오디오 하드웨어의 별도 부분을 제어하며 다른 공급업체에서 제공할 수 있습니다. ACX는 애플리케이션을 있는 그대로 실행할 수 있도록 호환되는 커널 스트리밍 인터페이스를 제공합니다.

스트림 핀

각 ACXCIRCUIT에는 하나 이상의 싱크 핀과 하나의 원본 핀이 있습니다. 이러한 핀은 ACX 프레임워크에서 회로의 연결을 오디오 스택에 노출하는 데 사용됩니다. 렌더링 회로의 경우 원본 핀을 사용하여 회로에서 만든 스트림의 렌더링 동작을 제어합니다. 캡처 회로의 경우 싱크 핀은 회로에서 만든 스트림의 캡처 동작을 제어하는 데 사용됩니다.   ACXPIN은 오디오 경로에서 스트리밍을 제어하는 데 사용되는 개체입니다. 스트리밍 ACXCIRCUIT은 회로 생성 시 엔드포인트 오디오 경로에 대한 적절한 ACXPIN 개체를 만들고 ACX에 ACXPIN을 등록하는 역할을 담당합니다. ACXCIRCUIT은 회로에 대한 렌더링 또는 캡처 핀 또는 핀만 만들어야 합니다. ACX 프레임워크는 회로에 연결하고 통신하는 데 필요한 다른 핀을 만듭니다.   

스트리밍 회로

엔드포인트가 단일 회로로 구성된 경우 해당 회로는 스트리밍 회로입니다.

엔드포인트가 하나 이상의 디바이스 드라이버에서 만든 둘 이상의 회로로 구성된 경우 회로는 구성된 엔드포인트를 설명하는 ACXCOMPOSITETEMPLATE에 의해 결정되는 특정 순서로 연결됩니다. 엔드포인트의 첫 번째 회로는 엔드포인트에 대한 스트리밍 회로입니다.

스트리밍 회로는 AcxRtStreamCreate를 사용하여 EvtAcxCircuitCreateStream에 대한 응답으로 RT 패킷 스트림을 만들어야 합니다. AcxRtStreamCreate를 사용하여 만든 ACXSTREAM을 사용하면 스트리밍 회로 드라이버가 스트리밍에 사용되는 버퍼를 할당하고 클라이언트 및 하드웨어 요구 사항에 따라 스트리밍 흐름을 제어할 수 있습니다.

엔드포인트의 다음 회로는 AcxStreamCreate를 사용하여 EvtAcxCircuitCreateStream에 대한 응답으로 기본 스트림을 만들어야 합니다. 다음 회로에서 AcxStreamCreate를 사용하여 만든 ACXSTREAM 개체를 사용하면 드라이버가 일시 중지 또는 실행과 같은 스트림 상태 변경에 대응하여 하드웨어를 구성할 수 있습니다.

스트리밍 ACXCIRCUIT은 스트림을 만들기 위한 요청을 수신하는 첫 번째 회로입니다. 요청에는 디바이스, 핀 및 데이터 형식(모드 포함)이 포함됩니다.

오디오 경로의 각 ACXCIRCUIT은 회로의 스트림 인스턴스를 나타내는 ACXSTREAM 개체를 만듭니다. ACX 프레임워크는 ACXSTREAM 개체를 함께 연결합니다(ACXCIRCUIT 개체가 연결된 것과 거의 동일한 방식). 

업스트림 및 다운스트림 회로

스트림 생성은 스트리밍 회로에서 시작되며 회로가 연결된 순서대로 각 다운스트림 회로로 전달됩니다. AcxPinCommunicationNone과 동일한 Communication을 사용하여 만든 브리지 핀 간에 연결됩니다. ACX 프레임워크는 드라이버가 회로를 만들 때 추가하지 않는 경우 회로에 대해 하나 이상의 브리지 핀을 만듭니다.

스트리밍 회로로 시작하는 각 회로에 대해 AcxPinTypeSource 브리지 핀은 다음 다운스트림 회로에 연결됩니다. 최종 회로에는 오디오 엔드포인트 하드웨어를 설명하는 엔드포인트 핀이 있습니다(예: 엔드포인트가 마이크인지 스피커인지, 잭이 연결되어 있는지 여부).

스트리밍 회로를 따르는 각 회로에 대해 AcxPinTypeSink 브리지 핀은 다음 업스트림 회로에 연결됩니다.

스트림 형식 협상

드라이버는 AcxPinAssignModeDataFormatList 및 AcxPinGetRawDataFormatList사용하여 스트림을 만드는 데 사용되는 ACXPIN에 모드당 지원되는 형식을 추가하여 스트림 생성에 지원되는 형식을 보급합니다. 다중 회로 엔드포인트의 경우 ACXSTREAMBRIDGE를 사용하여 ACX 회로 간의 모드 및 서식 지원을 조정할 수 있습니다. 엔드포인트에 대해 지원되는 스트림 형식은 스트리밍 회로에서 만든 스트리밍 ACXPIN에 의해 결정됩니다. 다음 회로에서 사용하는 형식은 엔드포인트에서 이전 회로의 브리지 핀에 의해 결정됩니다.

기본적으로 ACX 프레임워크는 다중 회로 엔드포인트의 각 회로 사이에 ACXSTREAMBRIDGE를 만듭니다. 기본 ACXSTREAMBRIDGE는 스트림 생성 요청을 다운스트림 회로로 전달할 때 RAW 모드의 기본 형식인 업스트림 회로의 브리지 핀을 사용합니다. 업스트림 회로의 브리지 핀에 형식이 없으면 원래 스트림 형식이 사용됩니다. 다운스트림 회로의 연결된 핀이 사용되는 형식을 지원하지 않으면 스트림 생성이 실패합니다.

디바이스 회로가 스트림 형식 변경을 수행하는 경우 디바이스 드라이버는 다운스트림 브리지 핀에 다운스트림 형식을 추가해야 합니다.  

스트림 만들기

스트림 만들기의 첫 번째 단계는 엔드포인트 오디오 경로의 각 ACXCIRCUIT에 대한 ACXSTREAM 인스턴스를 만드는 것입니다. ACX는 각 회로의 EvtAcxCircuitCreateStream을 호출합니다. ACX는 헤드 회로로 시작하고 각 회로의 EvtAcxCircuitCreateStream을 순서대로 호출하여 테일 회로로 끝납니다. 순서는 Stream Bridge에 대한 AcxStreamBridgeInvertChangeStateSequence 플래그(ACX_STREAM_BRIDGE_CONFIG_FLAGS 정의됨)를 지정하여 되돌릴 수 있습니다. 모든 회로가 스트림 개체를 만든 후 스트림 개체는 스트리밍 논리를 처리합니다.

스트림 생성 요청은 헤드 회로 생성 중에 지정된 EvtAcxCircuitCreateStream을 호출하여 헤드 회로 토폴로지 생성의 일부로 생성된 적절한 PIN으로 전송됩니다. 

스트리밍 회로는 처음에 스트림 만들기 요청을 처리하는 업스트림 회로입니다.

  • AcxStreamCallbacks 및 AcxRtStreamCallbacks를 할당하여 ACXSTREAM_INIT 구조를 업데이트합니다.
  • AcxRtStreamCreate를 사용하여 ACXSTREAM 개체를 만듭니다.
  • 스트림별 요소(예: ACXVOLUME 또는 ACXAUDIOENGINE)를 만듭니다.
  • ACXSTREAM 개체에 요소를 추가합니다.
  • ACX 프레임워크에 만들어진 ACXSTREAM 개체를 반환합니다.

그런 다음, ACX는 스트림 생성을 다음 다운스트림 회로로 전달합니다.

  • AcxStreamCallbacks를 할당하여 ACXSTREAM_INIT 구조를 업데이트합니다.
  • AcxStreamCreate를 사용하여 ACXSTREAM 개체를 만듭니다.
  • 스트림별 요소를 만듭니다.
  • ACXSTREAM 개체에 요소를 추가합니다.
  • ACX 프레임워크에 만들어진 ACXSTREAM 개체를 반환합니다.

오디오 경로의 회로 간 통신 채널은 ACXTARGETSTREAM 개체를 사용합니다. 이 예제에서 각 회로는 앞에 있는 회로의 IO 큐와 엔드포인트 오디오 경로의 회로 뒤에 있는 회로에 액세스할 수 있습니다. 또한 엔드포인트 오디오 경로는 선형 및 양방향입니다. 실제 IO 큐 처리는 ACX 프레임워크에서 수행됩니다.    ACXSTREAM 개체를 만드는 동안 각 회로는 ACXSTREAM 개체에 컨텍스트 정보를 추가하여 스트림에 대한 프라이빗 데이터를 저장하고 추적할 수 있습니다.

렌더링 스트림 예제

DSP, CODEC 및 AMP의 세 가지 회로로 구성된 엔드포인트 오디오 경로에 렌더링 스트림을 만듭니다. DSP 회로는 스트리밍 회로로 작동하며 EvtAcxPinCreateStream 처리기를 제공했습니다. DSP 회로는 필터 회로로도 작동합니다. 스트림 모드 및 구성에 따라 오디오 데이터에 신호 처리를 적용할 수 있습니다. CODEC 회로는 오디오 싱크 기능을 제공하는 DAC를 나타냅니다. AMP 회로는 DAC와 스피커 사이의 아날로그 하드웨어를 나타냅니다. AMP 회로는 잭 검색 또는 기타 엔드포인트 하드웨어 세부 정보를 처리할 수 있습니다.

  1. AudioKSE는 NtCreateFile을 호출하여 스트림을 만듭니다.
  2. 이는 ACX를 통해 필터링되고 핀, 데이터 폼(모드 포함) 및 디바이스 정보를 사용하여 DSP 회로의 EvtAcxPinCreateStream을 호출하는 것으로 끝납니다. 
  3. DSP 회로는 데이터 형식 정보의 유효성을 검사하여 생성된 스트림을 처리할 수 있는지 확인합니다. 
  4. DSP 회로는 스트림을 나타내는 ACXSTREAM 개체를 만듭니다. 
  5. DSP 회로는 프라이빗 컨텍스트 구조를 할당하고 ACXSTREAM과 연결합니다. 
  6. DSP 회로는 실행 흐름을 ACX 프레임워크로 반환한 다음, ENDpoint Audio Path, CODEC 회로의 다음 회로로 호출합니다. 
  7. CODEC 회로는 데이터 형식 정보의 유효성을 검사하여 데이터 렌더링을 처리할 수 있도록 합니다. 
  8. CODEC 회로는 프라이빗 컨텍스트 구조를 할당하고 ACXSTREAM과 연결합니다. 
  9. CODEC 회로는 ACXSTREAM에 스트림 싱크로 자신을 추가합니다.
  10. CODEC 회로는 실행 흐름을 ACX 프레임워크로 반환한 다음, AMP 회로인 엔드포인트 오디오 경로의 다음 회로로 호출합니다. 
  11. AMP 회로는 프라이빗 컨텍스트 구조를 할당하고 ACXSTREAM과 연결합니다. 
  12. AMP 회로는 실행 흐름을 ACX 프레임워크로 반환합니다. 이 시점에서 스트림 만들기가 완료됩니다. 

큰 버퍼 스트림

ACXCIRCUIT의 ACXAUDIOENGINE 요소에 의해 오프로드로 지정된 ACXPIN에 큰 버퍼 스트림이 생성됩니다.

오프로드 스트림을 지원하려면 디바이스 드라이버는 스트리밍 회로를 만드는 동안 다음을 수행해야 합니다.

  1. 호스트, 오프로드 및 루프백 ACXPIN 개체를 만들고 ACXCIRCUIT에 추가합니다.
  2. ACXVOLUME, ACXMUTE 및 ACXPEAKMETER 요소를 만듭니다. ACXCIRCUIT에 직접 추가되지 않습니다.
  3. hostPin, OffloadPin, LoopbackPin, VolumeElement, MuteElement 및 PeakMeterElement 개체를 할당하여 ACX_AUDIOENGINE_CONFIG 구조를 초기화합니다.
  4. 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 함수를 호출하여 ACXCIRCUIT에 대한 오디오 인터페이스의 KS 오디오 속성 값인 디바이스 인터페이스 속성의 현재 값을 수정합니다.

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 프레임워크에서 알림 이벤트를 받습니다. 이 순서는 상태 변경 및 스트림의 흐름에 따라 달라집니다.

  • 활성 상태가 낮은 상태에서 더 활성 상태로 가는 렌더링 스트림의 경우 SINK를 등록한 스트리밍 회로가 먼저 이벤트를 받습니다. 이벤트를 처리하면 엔드포인트 오디오 경로의 다음 회로가 이벤트를 받습니다.

  • 더 활성 상태에서 덜 활성 상태로 가는 렌더링 스트림의 경우 스트리밍 회로는 마지막으로 이벤트를 수신합니다. 

  • 활성 상태가 낮은 상태에서 더 활성 상태로 가는 캡처 스트림의 경우 스트리밍 회로는 마지막으로 이벤트를 수신합니다. 

  • 활성 상태가 덜 활성 상태인 캡처 스트림의 경우 스트리밍 회로가 먼저 이벤트를 수신합니다. 

위의 순서 지정은 ACX 프레임워크에서 제공하는 기본값입니다. 드라이버는 드라이버가 스트리밍 회로에 추가하는 ACXSTREAMBRIDGE를 만들 때 AcxStreamBridgeInvertChangeStateSequence(ACX_STREAM_BRIDGE_CONFIG_FLAGS 정의됨)를 설정하여 반대 동작을 요청할 수 있습니다.  

오디오 데이터 스트리밍

스트림이 만들어지고 적절한 버퍼가 할당되면 스트림이 스트림 시작을 기다리는 일시 중지 상태에 있습니다. 클라이언트가 스트림을 재생 상태로 전환하면 ACX 프레임워크는 스트림과 연결된 모든 ACXSTREAM 개체를 호출하여 스트림 상태가 Play에 있음을 나타냅니다. 그런 다음 ACXPIN은 재생 상태로 배치되며, 이 상태에서 데이터가 흐르기 시작합니다. 

오디오 데이터 렌더링

스트림이 만들어지고 리소스가 할당되면 애플리케이션은 스트림에서 Start를 호출하여 재생을 시작합니다. 애플리케이션은 스트림을 시작하기 전에 GetBuffer/ReleaseBuffer를 호출하여 즉시 재생을 시작할 첫 번째 패킷에 유효한 오디오 데이터가 있는지 확인해야 합니다. 

클라이언트는 버퍼를 미리 롤링하여 시작합니다. 클라이언트가 ReleaseBuffer를 호출하면 활성 ACXSTREAM에서 EvtAcxStreamSetRenderPacket을 호출하는 ACX 계층을 호출하는 AudioKSE의 호출로 변환됩니다. 이 속성에는 패킷 인덱스(0부터 시작) 및 적절한 경우 현재 패킷에서 스트림 끝의 바이트 오프셋이 있는 EOS 플래그가 포함됩니다.    스트리밍 회로가 패킷으로 완료되면 다음 패킷을 렌더링 오디오 데이터로 채우기 위해 대기하는 클라이언트를 해제하는 버퍼 완료 알림을 트리거합니다. 

타이머 기반 스트리밍 모드는 지원되며 드라이버의 EvtAcxStreamAllocateRtPackets 콜백을 호출할 때 PacketCount 값 1을 사용하여 표시됩니다.

오디오 데이터 캡처

스트림이 만들어지고 리소스가 할당되면 애플리케이션은 스트림에서 Start를 호출하여 재생을 시작합니다. 

스트림이 실행 중이면 원본 회로가 캡처 패킷을 오디오 데이터로 채웁니다. 첫 번째 패킷이 채워지면 원본 회로는 패킷을 ACX 프레임워크로 해제합니다. 이 시점에서 ACX 프레임워크는 스트림 알림 이벤트를 신호로 표시합니다. 

스트림 알림이 신호를 받으면 클라이언트는 KSPROPERTY_RTAUDIO_GETREADPACKET 전송하여 캡처가 완료된 패킷의 인덱스(0부터 시작)를 가져올 수 있습니다. 클라이언트가 GETCAPTUREPACKET을 보낸 경우 드라이버는 이전 패킷이 모두 처리되었으며 채울 수 있다고 가정할 수 있습니다. 

버스트 캡처의 경우 원본 회로는 GETREADPACKET이 호출되는 즉시 ACX 프레임워크에 새 패킷을 해제할 수 있습니다.

클라이언트는 KSPROPERTY_RTAUDIO_PACKETVREGISTER 사용하여 스트림의 RTAUDIO_PACKETVREGISTER 구조에 대한 포인터를 가져올 수도 있습니다. 이 구조는 패킷이 완료되었음을 알리기 전에 ACX 프레임워크에 의해 업데이트됩니다.

레거시 KS 커널 스트리밍 동작

드라이버가 버스트 캡처를 구현하는 경우(키 단어 스포터 구현에서와 같이), PacketVRegister 대신 레거시 커널 스트리밍 패킷 처리 동작을 사용해야 하는 경우가 있을 수 있습니다. 이전 패킷 기반 동작을 사용하려면 드라이버는 KSPROPERTY_RTAUDIO_PACKETVREGISTER 대한 STATUS_NOT_SUPPORTED 반환해야 합니다.

다음 샘플에서는 ACXSTREAM에 대한 AcxStreamInitAssignAcxRequestPreprocessCallback에서 이 작업을 수행하는 방법을 보여 줍니다. 자세한 내용은 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 레지스터를 노출하는 것을 지원하지 않습니다. 이 경우 균형 조정이 발생하지 않기 때문에 지원되지 않습니다. 

스트리밍 회로가 패킷을 완료할 때마다 0부터 시작하는 패킷 인덱스와 가능한 한 패킷 완료에 근접한 QPC 값을 사용하여 AcxRtStreamNotifyPacketComplete를 호출합니다(예: QPC 값은 인터럽트 서비스 루틴에 의해 계산될 수 있음). 이 정보는 completedPacketCount, CompletedPacketQPC를 포함하는 구조체에 대한 포인터를 반환하는 KSPROPERTY_RTAUDIO_PACKETVREGISTER 통해 클라이언트에서 사용할 수 있습니다. 이 두 가지를 결합하는 값(클라이언트가 CompletedPacketCount 및 CompletedPacketQPC가 동일한 패킷에서 온 것을 확인할 수 있음).  

스트림 상태 전환

스트림을 만든 후 ACX는 다음 콜백을 사용하여 스트림을 다른 상태로 전환합니다.

  • EvtAcxStreamPrepareHardware 는 AcxStreamStateStop 상태에서 AcxStreamStatePause 상태로 스트림을 전환합니다. 드라이버는 EvtAcxStreamPrepareHardware를 받을 때 DMA 엔진과 같은 필수 하드웨어를 예약해야 합니다.
  • EvtAcxStreamRun 은 스트림을 AcxStreamStatePause 상태에서 AcxStreamStateRun 상태로 전환합니다.
  • EvtAcxStreamPause 는 스트림을 AcxStreamStateRun 상태에서 AcxStreamStatePause 상태로 전환합니다.
  • EvtAcxStreamReleaseHardware 는 스트림을 AcxStreamStatePause 상태에서 AcxStreamStateStop 상태로 전환합니다. 드라이버는 EvtAcxStreamReleaseHardware를 받을 때 DMA 엔진과 같은 필수 하드웨어를 해제해야 합니다.

스트림은 EvtAcxStreamReleaseHardware 콜백을 받은 후 EvtAcxStreamPrepareHardware 콜백을 받을 수 있습니다. 그러면 스트림이 AcxStreamStatePause 상태로 다시 전환됩니다.

EvtAcxStreamAllocateRtPackets를 사용한 패킷 할당은 일반적으로 EvtAcxStreamPrepareHardware에 대한 첫 번째 호출 전에 발생합니다. 할당된 패킷은 일반적으로 EvtAcxStreamReleaseHardware에 대한 마지막 호출 후에 EvtAcxStreamFreeRtPackets로 해제됩니다. 이 순서는 보장되지 않습니다.

AcxStreamStateAcquire 상태는 사용되지 않습니다. ACX는 준비 하드웨어(EvtAcxStreamPrepareHardware) 및 릴리스 하드웨어(EvtAcxStreamReleaseHardware) 콜백과 함께 암시적 상태이므로 드라이버가 획득 상태를 가질 필요가 없습니다.

큰 버퍼 스트림 및 오프로드 엔진 지원

ACX는 ACXAUDIOENGINE 요소를 사용하여 오프로드 스트림 생성을 처리하는 ACXPIN 및 스트림 볼륨 오프로드, 음소거 및 피크 미터 상태에 필요한 다양한 요소를 지정합니다. 이는 WaveRT 드라이버의 기존 오디오 엔진 노드와 유사합니다.

스트림 닫기 프로세스

클라이언트가 스트림을 닫으면 ACX Framework에서 ACXSTREAM 개체를 삭제하기 전에 드라이버가 EvtAcxStreamPause 및 EvtAcxStreamReleaseHardware를 받게 됩니다. AcxStreamCreate를 호출하여 ACXSTREAM에 대한 최종 클린 수행할 때 드라이버는 WDF_OBJECT_ATTRIBUTES 구조에서 표준 WDF EvtCleanupCallback 항목을 제공할 수 있습니다. WDF는 프레임워크가 개체를 삭제하려고 할 때 EvtCleanupCallback을 호출합니다. 확정되지 않은 개체에 대한 모든 참조가 해제된 후에만 호출되는 EvtDestroyCallback을 사용하지 마세요.

EvtAcxStreamReleaseHardware에서 리소스가 아직 클린 않은 경우 드라이버는 EvtCleanupCallback의 ACXSTREAM 개체와 연결된 시스템 메모리 리소스를 클린 합니다.

클라이언트가 요청할 때까지 드라이버가 스트림을 지원하는 리소스를 클린 않는 것이 중요합니다.

AcxStreamStateAcquire 상태는 사용되지 않습니다. ACX는 준비 하드웨어(EvtAcxStreamPrepareHardware) 및 릴리스 하드웨어(EvtAcxStreamReleaseHardware) 콜백과 함께 암시적 상태이므로 드라이버가 획득 상태를 가질 필요가 없습니다.

스트림 깜짝 제거 및 무효화

드라이버가 스트림이 잘못되었다고 판단하면(예: 잭이 분리됨) 회로가 모든 스트림을 종료합니다. 

스트림 메모리 클린up

스트림 리소스의 삭제는 드라이버의 스트림 컨텍스트 클린(삭제 안 됨)에서 수행할 수 있습니다. 개체의 컨텍스트에서 공유되는 모든 항목을 삭제하지 마세요. 이 지침은 모든 ACX 개체에 적용됩니다.

삭제 콜백은 알 수 없는 마지막 참조가 사라진 후에 호출됩니다.

일반적으로 스트림의 클린up 콜백은 핸들이 닫혀 있을 때 호출됩니다. 한 가지 예외는 드라이버가 콜백에서 스트림을 만든 경우입니다. ACX가 스트림 만들기 작업에서 반환하기 직전에 스트림 브리지에 이 스트림을 추가하지 못한 경우 스트림은 비동기적으로 취소되고 현재 스레드는 create-stream 클라이언트에 오류를 반환합니다. 스트림에는 이 시점에서 할당된 mem 할당이 없어야 합니다. 자세한 내용은 EVT_ACX_STREAM_RELEASE_HARDWARE 콜백을 참조하세요.

스트림 메모리 클린 시퀀스

스트림 버퍼는 시스템 리소스이며 사용자 모드 클라이언트가 스트림의 핸들을 닫을 때만 해제되어야 합니다. 버퍼(디바이스의 하드웨어 리소스와 다름)는 스트림의 핸들과 동일한 수명을 줍니다. 클라이언트가 핸들을 닫으면 ACX는 콜백을 클린 스트림 개체를 호출한 다음 개체의 ref가 0으로 이동하면 스트림 obj의 삭제 콜백을 호출합니다.

드라이버가 stream-obj를 만든 다음 만들기 스트림 콜백에 실패했을 때 ACX에서 작업 항목에 대한 STREAM obj 삭제를 연기할 수 있습니다. 종료 WDF 스레드로 교착 상태를 방지하기 위해 ACX는 삭제를 다른 스레드로 연기합니다. 이 동작의 가능한 부작용(리소스의 지연된 릴리스)을 방지하기 위해 드라이버는 stream-create에서 오류를 반환하기 전에 할당된 스트림 리소스를 해제할 수 있습니다.

ACX가 EVT_ACX_STREAM_FREE_RTPACKETS 콜백을 호출할 때 드라이버는 오디오 버퍼를 해제해야 합니다. 이 콜백은 사용자가 스트림 핸들을 닫을 때 호출됩니다.

RT 버퍼는 사용자 모드로 매핑되므로 버퍼 수명은 핸들 수명과 동일합니다. ACX가 이 콜백을 호출하기 전에 드라이버는 오디오 버퍼를 해제/해제하려고 시도해서는 안 됩니다.

EVT_ACX_STREAM_FREE_RTPACKETS 콜백은 콜백을 EVT_ACX_STREAM_RELEASE_HARDWARE 후 EvtDeviceReleaseHardware 전에 종료되어야 합니다.

사용자 모드 클라이언트가 오랫동안 핸들을 유지할 수 있으므로 드라이버가 WDF 릴리스 하드웨어 콜백을 처리한 후에 이 콜백이 발생할 수 있습니다. 드라이버는 이러한 핸들이 사라질 때까지 기다리지 않아야 합니다. 이렇게 하면 0x9f DRIVER_POWER_STATE_FAILURE 버그 검사 생성됩니다. 자세한 내용은 EVT_WDF_DEVICE_RELEASE_HARDWARE 콜백 함수를 참조하세요.

샘플 ACX 드라이버의 이 EvtDeviceReleaseHardware 코드는 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 디바이스 릴리스 하드웨어 -> 릴리스 디바이스의 h/w 리소스

AcxStreamFreeRtPackets -> 핸들과 연결된 릴리스/무료 오디오 버퍼

WDF 및 회로 개체 관리에 대한 자세한 내용은 ACX WDF 드라이버 수명 관리를 참조 하세요.

스트리밍 DDI

스트리밍 구조

ACX_RTPACKET 구조체

이 구조는 할당된 단일 패킷을 나타냅니다. PacketBuffer는 WDFMEMORY 핸들, MDL 또는 버퍼일 수 있습니다. ACX_RTPACKET_INIT 연결된 초기화 함수가 있습니다.

ACX_STREAM_CALLBACKS

이 구조는 ACX 프레임워크로 스트리밍하기 위한 드라이버 콜백을 식별합니다. 이 구조체는 ACX_PIN_CONFIG 구조체의 일부입니다.

스트리밍 콜백

EvtAcxStreamAllocateRtPackets

EvtAcxStreamAllocateRtPackets 이벤트는 스트리밍을 위해 RtPackets를 할당하도록 드라이버에 지시합니다. AcxRtStream은 이벤트 기반 스트리밍의 경우 PacketCount = 2, 타이머 기반 스트리밍의 경우 PacketCount = 1을 받습니다. 드라이버가 두 패킷 모두에 단일 버퍼를 사용하는 경우 두 번째 RtPacketBuffer에는 첫 번째 패킷의 끝(패킷[2])과 일치하는 RtPacketOffset이 있는 Type = WdfMemoryDescriptorTypeInvalid가 있는 WDF_MEMORY_DESCRIPTOR 있어야 합니다. RtPacketOffset = packet[1]. RtPacketOffset+packet[1]. RtPacketSize).

EvtAcxStreamFreeRtPackets

EvtAcxStreamFreeRtPackets 이벤트는 EvtAcxStreamAllocateRtPackets에 대한 이전 호출에서 할당된 RtPackets를 해제하도록 드라이버에 지시합니다. 해당 호출에서 동일한 패킷이 포함됩니다.

EvtAcxStreamGetHwLatency

EvtAcxStreamGetHwLatency 이벤트는 드라이버에게 이 스트림의 특정 회로에 대한 스트림 대기 시간을 제공하도록 지시합니다(전체 대기 시간은 다른 회로의 대기 시간의 합계). FifoSize는 바이트 단위이고 지연은 100나노초 단위입니다.

EvtAcxStreamSetRenderPacket

EvtAcxStreamSetRenderPacket 이벤트는 클라이언트에서 방금 릴리스된 패킷을 드라이버에 알려줍니다. 결함이 없는 경우 이 패킷은 (CurrentRenderPacket + 1)이어야 합니다. 여기서 CurrentRenderPacket은 드라이버가 현재 스트리밍 중인 패킷입니다.

플래그는 0 또는 AcxStreamSetRenderPacketEndOfStream일 수 있으며, 이는 패킷이 스트림의 마지막 패킷이고 EosPacketLength가 패킷에 유효한 길이(바이트)임을 나타냅니다.

이 값과 일치하도록 CurrentRenderPacket을 변경하는 대신 패킷이 렌더링될 때 드라이버는 CurrentRenderPacket을 계속 늘려야 합니다.

EvtAcxStreamGetCurrentPacket

EvtAcxStreamGetCurrentPacket드라이버에 현재 하드웨어에 렌더링되거나 캡처 하드웨어에 의해 채워지고 있는 패킷(0 기반)을 나타내도록 지시합니다.

EvtAcxStreamGetCapturePacket

EvtAcxStreamGetCapturePacket드라이버가 패킷을 채우기 시작했을 때의 QPC 값을 포함하여 가장 최근에 완전히 채워진 패킷(0부터 시작)을 나타내도록 드라이버에 지시합니다.

EvtAcxStreamGetPresentationPosition

EvtAcxStreamGetPresentationPosition현재 위치를 계산할 때 QPC 값과 함께 현재 위치를 나타내도록 드라이버에 지시합니다.

스트림 상태 이벤트

ACXSTREAM의 스트리밍 상태는 다음 API에 의해 관리됩니다.

EVT_ACX_STREAM_PREPARE_HARDWARE

EVT_ACX_STREAM_RELEASE_HARDWARE

EVT_ACX_STREAM_RUN

EVT_ACX_STREAM_PAUSE

스트리밍 API

AcxStreamCreate

AcxStreamCreate는 스트리밍 동작을 제어하는 데 사용할 수 있는 ACX 스트림을 만듭니다.

AcxRtStreamCreate

AcxRtStreamCreate는 스트리밍 동작을 제어하고 패킷 할당을 처리하고 스트리밍 상태를 통신하는 데 사용할 수 있는 ACX 스트림을 만듭니다.

AcxRtStreamNotifyPacketComplete

패킷이 완료되면 드라이버가 이 ACX API를 호출합니다. 클라이언트 성능을 향상시키기 위해 패킷 완료 시간 및 0부터 시작하는 패킷 인덱스가 포함됩니다. ACX 프레임워크는 스트림과 연결된 모든 알림 이벤트를 설정합니다.

참고 항목

ACX 오디오 클래스 확장 개요

ACX 참조 설명서

ACX 개체 요약