ACX-Streaming

In diesem Thema werden ACX-Streaming und die zugeordnete Pufferung erläutert, was für ein Glitch-kostenloses Audioerlebnis von entscheidender Bedeutung ist. Er beschreibt die Mechanismen, die vom Treiber verwendet werden, um über den Datenstromzustand zu kommunizieren und den Puffer für den Datenstrom zu verwalten. Eine Liste allgemeiner ACX-Audiobegriffe und eine Einführung in ACX finden Sie in der Übersicht über ACX-Audioklassenerweiterungen.

Hinweis

Die ACX-Header und -Bibliotheken sind nicht in den WDK 10.0.22621.2428 (veröffentlicht am 24. Oktober 2023) enthalten, sind aber in früheren Versionen sowie in den neuesten (25000-Serienbuilds) Insider Preview des WDK verfügbar. Weitere Informationen zu Vorschauversionen des WDK finden Sie unter Installieren von Vorschauversionen des Windows Driver Kit (WDK).

ACX-Streamingtypen

Ein AcxStream stellt einen Audiodatenstrom auf der Hardware eines bestimmten Schaltkreises dar. Ein AcxStream kann ein oder mehrere AcxElements-ähnliche Objekte aggregieren.

Das ACX-Framework unterstützt zwei Datenstromtypen. Der erste Streamtyp, der RT-Paketstream, bietet Unterstützung für die Zuordnung von RT-Paketen und die Verwendung von RT-Paketen zum Übertragen von Audiodaten an oder von der Gerätehardware zusammen mit Streamstatusübergängen. Der zweite Streamtyp, der Basic Stream, bietet nur Unterstützung für Datenstromstatusübergänge.

Bei einem einzelnen Schaltkreisendpunkt muss es sich bei dem Schaltkreis um einen Streaming-Schaltkreis handeln, der einen RT-Paketstream erstellt. Wenn zwei oder mehr Schaltkreise verbunden sind, um einen Endpunkt zu erstellen, ist der erste Schaltkreis im Endpunkt der Streaming-Schaltkreis und erstellt einen RT-Paketstream; Verbundene Schaltkreise erstellen grundlegende Datenströme, um Ereignisse im Zusammenhang mit Streamstatusübergängen zu empfangen.

Weitere Informationen finden Sie unter ACX Stream in der Zusammenfassung von ACX-Objekten. Die DDIs für Datenstrom werden im acxstreams.h-Header definiert.

ACX-Streamingkommunikationsstapel

Es gibt zwei Arten von Kommunikationen für ACX Streaming. Ein Kommunikationspfad wird zum Steuern des Streamingverhaltens verwendet, für Befehle wie Start, Erstellen und Zuordnen, die die standardmäßige ACX-Kommunikation verwenden. Das ACX-Framework verwendet E/A-Warteschlangen und übergibt WDF-Anforderungen mithilfe der Warteschlangen. Das Warteschlangenverhalten wird durch die Verwendung von Evt-Rückrufen und ACX-Funktionen vom tatsächlichen Treibercode ausgeblendet, obwohl dem Treiber auch die Möglichkeit gegeben wird, alle WDF-Anforderungen vorab zu verarbeiten.

Der zweite und interessantere Kommunikationspfad wird für die Audiostreaming-Signalisierung verwendet. Dies umfasst die Meldung des Treibers, wann ein Paket bereit ist und Daten empfängt, wenn der Treiber die Verarbeitung eines Pakets abgeschlossen hat. 

Die Standard Anforderungen für streaming-Signalisierung:

  • Unterstützung der Glitch-kostenlosen Wiedergabe
    • Niedrige Latenz
    • Alle erforderlichen Sperren sind auf den betreffenden Datenstrom beschränkt.
  • Benutzerfreundlichkeit für Treiberentwickler

Um mit dem Treiber zu kommunizieren, um den Streamingstatus zu signalisieren, verwendet ACX Ereignisse mit einem freigegebenen Puffer und direkten IRP-Aufrufen. Diese werden als Nächstes beschrieben.

Freigegebener Puffer

Für die Kommunikation vom Treiber an den Client werden ein gemeinsam genutzter Puffer und ein Ereignis verwendet. Dadurch wird sichergestellt, dass der Client nicht warten oder abfragen muss, und dass der Client feststellen kann, was es zum Fortsetzen des Streamings benötigt, während der Bedarf an direkten IRP-Aufrufen reduziert oder eliminiert wird.

Der Gerätetreiber verwendet einen freigegebenen Puffer, um mit dem Client zu kommunizieren, von dem das Paket gerendert oder erfasst wird. Dieser freigegebene Puffer enthält die Paketanzahl (1-basiert) des letzten abgeschlossenen Pakets zusammen mit dem QPC-Wert (QueryPerformanceCounter) der Abschlusszeit. Für den Gerätetreiber muss diese Informationen durch Aufrufen von AcxRtStreamNotifyPacketComplete angegeben werden. Wenn der Gerätetreiber AcxRtStreamNotifyPacketComplete aufruft, aktualisiert das ACX-Framework den freigegebenen Puffer mit der neuen Paketanzahl und QPC und signalisiert ein Ereignis, das für den Client freigegeben wurde, um anzugeben, dass der Client die neue Paketanzahl lesen kann.

Direkte IRP-Anrufe

Für die Kommunikation vom Client an den Treiber werden direkte IRP-Aufrufe verwendet. Dadurch werden die Komplexitäten reduziert, um sicherzustellen, dass WDF-Anforderungen zeitnah behandelt werden und sich als gut in der vorhandenen Architektur erwiesen haben.

Der Client kann jederzeit die aktuelle Paketanzahl anfordern oder die aktuelle Paketanzahl an den Gerätetreiber angeben. Diese Anforderungen rufen die Ereignishandler des Gerätetreibers EvtAcxStreamGetCurrentPacket und EvtAcxStreamSetRenderPacket auf. Der Client kann auch das aktuelle Aufnahmepaket anfordern, das den Ereignishandler des Gerätetreibers EvtAcxStreamGetCapturePacket aufruft.

Ähnlichkeiten mit PortCls

Die Kombination von direkten IRP-Aufrufen und freigegebenen Puffern, die von ACX verwendet werden, ähnelt der Kommunikation der Verarbeitung des Pufferabschlusses in PortCls. Die IRPs sind sehr ähnlich, und der freigegebene Puffer ermöglicht es dem Treiber, die Paketanzahl und -anzeige direkt zu kommunizieren, ohne sich auf IRPs zu verlassen.   Treiber müssen sicherstellen, dass sie nichts tun, das Zugriff auf Sperren erfordert, die auch in den Datenstromsteuerungspfaden verwendet werden. Dies ist erforderlich, um Störungen zu verhindern. 

Unterstützung für große Puffer für die Wiedergabe mit geringer Leistung

Um die Energie zu reduzieren, die beim Wiedergeben von Medieninhalten verbraucht wird, ist es wichtig, die Zeit zu reduzieren, die die APU in einem Hochleistungszustand verbringt. Da die normale Audiowiedergabe 10 ms Puffer verwendet, muss die APU immer aktiv sein. Um der APU die Zeit zu geben, die er benötigt, um den Zustand zu reduzieren, können ACX-Treiber die Unterstützung für deutlich größere Puffer im Bereich der Größe von 1 bis 2 Sekunden ankündigen. Dies bedeutet, dass die APU einmal alle 1 bis 2 Sekunden aufwachen kann, die erforderlichen Vorgänge mit maximaler Geschwindigkeit ausführen, um den nächsten 1-2-Sekunden-Puffer vorzubereiten, und dann zum niedrigsten möglichen Energiezustand zu wechseln, bis der nächste Puffer benötigt wird. 

In vorhandenen Streamingmodellen wird die Wiedergabe mit geringer Leistung über die Offload-Wiedergabe unterstützt. Ein Audiotreiber kündigt Unterstützung für die Offload-Wiedergabe an, indem ein AudioEngine-Knoten auf dem Wellenfilter für einen Endpunkt verfügbar gemacht wird. Der Knoten "AudioEngine" bietet eine Möglichkeit, das DSP-Modul zu steuern, das der Treiber zum Rendern der Audiodaten aus den großen Puffern mit der gewünschten Verarbeitung verwendet.

Der Knoten "AudioEngine" bietet folgende Möglichkeiten:

  • Audiomodulbeschreibung, die den Audiostapel angibt, welche Pins auf dem Wellenfilter Offload- und Loopbackunterstützung (und Hostwiedergabeunterstützung) bieten. 
  • Puffergrößenbereich, der dem Audiostapel die mindesten und maximalen Puffergrößen angibt, die für den Offload unterstützt werden können. Wiedergabe. Der Puffergrößenbereich kann sich basierend auf der Systemaktivität dynamisch ändern. 
  • Formatunterstützung, einschließlich unterstützter Formate, des aktuellen Gerätemixformats und des Geräteformats. 
  • Volume, einschließlich Ramping-Unterstützung, da mit dem größeren Puffer-Softwarevolume nicht reaktionsfähig ist.
  • Loopback-Schutz, der dem Treiber angibt, die AudioEngine Loopback-Pin stummzuschalten, wenn mindestens ein ausgeladener Datenstrom geschützte Inhalte enthält. 
  • Globaler FX-Zustand, um GFX für audioEngine zu aktivieren oder zu deaktivieren. 

Wenn ein Datenstrom auf dem Offload-Pin erstellt wird, unterstützt der Datenstrom volume, Local FX und Loopback Protection. 

Geringe Energiewiedergabe mit ACX

Das ACX-Framework verwendet das gleiche Modell für die Wiedergabe mit geringer Leistung. Der Treiber erstellt drei separate ACXPIN-Objekte für Host-, Offload- und Loopbackstreaming sowie ein ACXAUDIOENGINE-Element, das beschreibt, welche dieser Pins für Host, Offload und Loopback verwendet werden. Der Treiber fügt die Pins und ACXAUDIOENGINE-Element während der Schaltkreiserstellung dem ACXCIRCUIT hinzu.

Geladene Datenstromerstellung

Der Treiber fügt außerdem ein ACXAUDIOENGINE-Element zu Datenströmen hinzu, die für den Offload erstellt wurden, um die Kontrolle über Lautstärke, Stummschaltung und Spitzenzähler zu ermöglichen.

Streamingdiagramm

Dieses Diagramm zeigt einen ACX-Treiber mit mehreren Stapeln.

Diagramm zur Veranschaulichung von DSP-, CODEC- und AMP-Feldern mit einer Kernelstreamingschnittstelle oben.

Jeder ACX-Treiber steuert einen separaten Teil der Audiohardware und kann von einem anderen Anbieter bereitgestellt werden. ACX bietet eine kompatible Kernelstreamingschnittstelle, mit der Anwendungen wie folgt ausgeführt werden können.

Stream-Pins

Jeder ACXCIRCUIT verfügt über mindestens einen Senken-Pin und einen Quell-Pin. Diese Pins werden vom ACX-Framework verwendet, um die Verbindungen des Schaltkreises mit dem Audiostapel verfügbar zu machen. Bei einer Renderschaltung wird die Quellnadel verwendet, um das Renderverhalten eines datenstroms zu steuern, der aus dem Schaltkreis erstellt wurde. Bei einer Erfassungsschaltung wird der Sink-Pin verwendet, um das Aufnahmeverhalten eines datenstroms zu steuern, der aus dem Schaltkreis erstellt wurde.   ACXPIN ist das Objekt, das zum Steuern des Streamings im Audiopfad verwendet wird. Das Streaming ACXCIRCUIT ist für die Erstellung der entsprechenden ACXPIN-Objekte für den Endpunkt-Audiopfad zur Schaltkreiserstellung und für die Registrierung der ACXPINs bei ACX verantwortlich. Der ACXCIRCUIT muss nur den Render- oder Aufnahme-Pins für den Schaltkreis erstellen; Das ACX-Framework erstellt den anderen Pin, der zum Herstellen einer Verbindung mit und zur Kommunikation mit dem Schaltkreis benötigt wird.   

Streamingschaltung

Wenn ein Endpunkt aus einem einzelnen Schaltkreis besteht, handelt es sich bei diesem Schaltkreis um den Streaming-Schaltkreis.

Wenn ein Endpunkt aus mehr als einem Schaltkreis besteht, der von einem oder mehreren Gerätetreibern erstellt wird, werden die Schaltkreise in der spezifischen Reihenfolge verbunden, die von der ACXCOMPOSITETEMPLATE bestimmt wird, die den zusammengesetzten Endpunkt beschreibt. Der erste Schaltkreis im Endpunkt ist der Streaming-Schaltkreis für den Endpunkt.

Der Streaming-Schaltkreis sollte AcxRtStreamCreate verwenden, um einen RT-Paketstream als Reaktion auf EvtAcxCircuitCreateStream zu erstellen. Der mit AcxRtStreamCreate erstellte ACXSTREAM ermöglicht es dem Streaming-Schaltkreistreiber, den puffer für Streaming zuzuweisen und den Streamingfluss als Reaktion auf die Client- und Hardwareanforderungen zu steuern.

Die folgenden Schaltkreise im Endpunkt sollten AcxStreamCreate verwenden, um einen Standardstream als Reaktion auf EvtAcxCircuitCreateStream zu erstellen. Die ACXSTREAM-Objekte, die mit AcxStreamCreate durch die folgenden Schaltkreise erstellt wurden, ermöglichen es den Treibern, Hardware als Reaktion auf Datenstromzustandsänderungen wie "Anhalten" oder "Ausführen" zu konfigurieren.

Das Streaming ACXCIRCUIT ist der erste Schaltkreis, der die Anforderungen zum Erstellen eines Datenstroms empfängt. Die Anforderung enthält das Gerät, den Pin und das Datenformat (einschließlich Modus).

Jeder ACXCIRCUIT im Audiopfad erstellt ein ACXSTREAM-Objekt, das die Streaminstanz des Schaltkreises darstellt. Das ACX-Framework verknüpft die ACXSTREAM-Objekte miteinander (ähnlich wie die ACXCIRCUIT-Objekte sind verknüpft). 

Vor- und Nachgeschaltete Schaltkreise

Die Datenstromerstellung beginnt am Streaming-Schaltkreis und wird an jeden nachgeschalteten Schaltkreis in der Reihenfolge weitergeleitet, in der die Schaltkreise verbunden sind. Die Verbindungen werden zwischen Brückennadeln hergestellt, die mit "Communication equal to AcxPinCommunicationNone" erstellt wurden. Das ACX-Framework erstellt einen oder mehrere Brückenstifte für einen Schaltkreis, wenn der Treiber sie nicht zur Schaltkreiserstellungszeit hinzu fügt.

Für jeden Schaltkreis, der mit dem Streaming-Schaltkreis beginnt, wird der AcxPinTypeSource-Brückenstift mit dem nächsten Downstream-Schaltkreis verbunden. Der letzte Schaltkreis verfügt über einen Endpunkt-Pin, der die Audioendpunkthardware beschreibt (z. B. ob der Endpunkt ein Mikrofon oder Lautsprecher ist und ob die Buchse angeschlossen ist).

Für jeden Schaltkreis nach dem Streaming-Schaltkreis wird der AcxPinTypeSink-Brückenstift mit dem nächsten Upstream-Schaltkreis verbunden.

Streamformat-Aushandlung

Der Treiber kündigt die unterstützten Formate für die Datenstromerstellung an, indem die unterstützten Formate pro Modus zur ACXPIN-Erstellung mit AcxPinAssignModeDataFormatList und AcxPinGetRawDataFormatList hinzugefügt werden. Für Multi-Circuit-Endpunkte kann eine ACXSTREAMBRIDGE verwendet werden, um den Modus und die Formatunterstützung zwischen ACX-Schaltkreisen zu koordinieren. Die unterstützten Streamformate für den Endpunkt werden von den streaming-ACXPINs bestimmt, die vom Streaming-Schaltkreis erstellt wurden. Die von den folgenden Schaltkreisen verwendeten Formate werden durch den Brückenstift des vorherigen Schaltkreises im Endpunkt bestimmt.

Standardmäßig erstellt das ACX-Framework eine ACXSTREAMBRIDGE zwischen jedem Schaltkreis in einem Multi-Circuit-Endpunkt. Die Standardeinstellung ACXSTREAMBRIDGE verwendet das Standardformat des RAW-Modus des Brückenstifts des Upstream-Schaltkreises, wenn die Datenstromerstellungsanforderung an den downstream-Schaltkreis weitergeleitet wird. Wenn der Brücken-Pin des Upstream-Schaltkreises keine Formate aufweist, wird das ursprüngliche Datenstromformat verwendet. Wenn der verbundene Pin des nachgeschalteten Schaltkreises das verwendete Format nicht unterstützt, schlägt die Datenstromerstellung fehl.

Wenn eine Geräteschaltung eine Datenstromformatänderung durchführt, sollte der Gerätetreiber dem nachgeschalteten Brückenstift das nachgeschaltete Format hinzufügen.  

Streamerstellung

Der erste Schritt bei der Streamerstellung besteht darin, die ACXSTREAM-Instanz für jeden ACXCIRCUIT im Endpunkt-Audiopfad zu erstellen. ACX ruft den EvtAcxCircuitCreateStream jedes Schaltkreises auf. ACX beginnt mit dem Leiterkreis und ruft den EvtAcxCircuitCreateStream jedes Schaltkreises in der Reihenfolge auf, die mit dem Tail-Schaltkreis endet. Die Reihenfolge kann umgekehrt werden, indem das AcxStreamBridgeInvertChangeStateSequence-Flag (definiert in ACX_STREAM_BRIDGE_CONFIG_FLAGS) für die Stream-Brücke angegeben wird. Nachdem alle Schaltkreise ein Datenstromobjekt erstellt haben, verarbeiten die Streamobjekte streaminglogik.

Die Stream Creation Request wird an die entsprechende PIN gesendet, die als Teil der Topologiegenerierung des Leiterkreises generiert wird, indem der evtAcxCircuitCreateStream aufgerufen wird, der während der Erstellung des Leiterkreises angegeben wurde. 

Der Streaming-Schaltkreis ist der Upstream-Schaltkreis, der zunächst die Datenstromerstellungsanforderung verarbeitet.

  • Sie aktualisiert die ACXSTREAM_INIT Struktur, zuweisen von AcxStreamCallbacks und AcxRtStreamCallbacks
  • Es erstellt das ACXSTREAM-Objekt mit AcxRtStreamCreate
  • Es erstellt streamspezifische Elemente (z. B. ACXVOLUME oder ACXAUDIOENGINE)
  • Es fügt die Elemente zum ACXSTREAM-Objekt hinzu.
  • Es gibt das ACXSTREAM-Objekt zurück, das für das ACX-Framework erstellt wurde.

ACX leitet dann die Datenstromerstellung an den nächsten nachgeschalteten Schaltkreis weiter.

  • Sie aktualisiert die ACXSTREAM_INIT-Struktur, wobei AcxStreamCallbacks zugewiesen werden
  • Es erstellt das ACXSTREAM-Objekt mithilfe von AcxStreamCreate
  • Es erstellt alle streamspezifischen Elemente.
  • Es fügt die Elemente zum ACXSTREAM-Objekt hinzu.
  • Es gibt das ACXSTREAM-Objekt zurück, das für das ACX-Framework erstellt wurde.

Der Kommunikationskanal zwischen Schaltkreisen in einem Audiopfad verwendet ACXTARGETSTREAM-Objekte. In diesem Beispiel hat jeder Schaltkreis Zugriff auf eine E/A-Warteschlange für den Schaltkreis davor und den Schaltkreis dahinter im Endpunkt-Audiopfad. Darüber hinaus ist ein Endpunktaudiopfad linear und bidirektional. Die tatsächliche Verarbeitung der E/A-Warteschlange wird vom ACX-Framework ausgeführt.    Beim Erstellen des ACXSTREAM-Objekts kann jeder Schaltkreis dem ACXSTREAM-Objekt Kontextinformationen hinzufügen, um private Daten für den Datenstrom zu speichern und nachzuverfolgen.

Renderdatenstrombeispiel

Erstellen eines Renderdatenstroms auf einem Endpunktaudiopfad, bestehend aus drei Schaltkreisen: DSP, CODEC und AMP. Der DSP-Schaltkreis fungiert als Streamingschaltung und hat einen EvtAcxPinCreateStream-Handler bereitgestellt. Der DSP-Schaltkreis funktioniert auch als Filterschaltung: Je nach Datenstrommodus und Konfiguration kann die Signalverarbeitung auf die Audiodaten angewendet werden. Der CODEC-Schaltkreis stellt den DAC dar und stellt die Audio-Sink-Funktionalität bereit. Der AMP-Schaltkreis stellt die analoge Hardware zwischen dem DAC und dem Lautsprecher dar. Der AMP-Schaltkreis kann die Erkennung der Buchse oder andere Endpunkthardwaredetails verarbeiten.

  1. AudioKSE ruft NtCreateFile auf, um einen Datenstrom zu erstellen.
  2. Dies filtert über ACX und endet mit dem Aufrufen des EvtAcxPinCreateStream des DSP-Schaltkreises mit den Pin-, Datenformaten (einschließlich Modus) und Geräteinformationen. 
  3. Der DSP-Schaltkreis überprüft die Datenformatinformationen, um sicherzustellen, dass er den erstellten Datenstrom verarbeiten kann. 
  4. Der DSP-Schaltkreis erstellt das ACXSTREAM-Objekt, um den Datenstrom darzustellen. 
  5. Der DSP-Schaltkreis weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu. 
  6. Der DSP-Schaltkreis gibt den Ausführungsfluss an das ACX-Framework zurück, der dann in den nächsten Schaltkreis im Endpunktaudiopfad, dem CODEC-Schaltkreis, aufruft. 
  7. Der CODEC-Schaltkreis überprüft die Datenformatinformationen, um zu bestätigen, dass es das Rendern der Daten verarbeiten kann. 
  8. Der CODEC-Schaltkreis weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu. 
  9. Der CODEC-Schaltkreis fügt sich als Stromsenke zum ACXSTREAM hinzu.
  10. Der CODEC-Schaltkreis gibt den Ausführungsfluss an das ACX-Framework zurück, der dann in den nächsten Schaltkreis im Endpunktaudiopfad, dem AMP-Schaltkreis, aufruft. 
  11. Der AMP-Schaltkreis weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu. 
  12. Der AMP-Schaltkreis gibt den Ausführungsfluss an das ACX-Framework zurück. Zu diesem Zeitpunkt ist die Datenstromerstellung abgeschlossen. 

Große Pufferströme

Große Pufferströme werden auf dem ACXPIN erstellt, der für den Offload durch das ACXCIRCUIT ACXAUDIOENGINE-Element festgelegt ist.

Zur Unterstützung von Auslagerungsstreams sollte der Gerätetreiber während der Erstellung des Streaming-Schaltkreises Folgendes ausführen:

  1. Erstellen Sie die Host-, Offload- und Loopback-ACXPIN-Objekte, und fügen Sie sie dem ACXCIRCUIT hinzu.
  2. Erstellen Sie ACXVOLUME-, ACXMUTE- und ACXPEAKMETER-Elemente. Diese werden dem ACXCIRCUIT nicht direkt hinzugefügt.
  3. Initialisieren Sie eine ACX_AUDIOENGINE_CONFIG Struktur, und weisen Sie die Objekte HostPin, OffloadPin, LoopbackPin, VolumeElement, MuteElement und PeakMeterElement zu.
  4. Erstellen Sie das ACXAUDIOENGINE-Element.

Treiber müssen ähnliche Schritte ausführen, um ein ACXSTREAMAUDIOENGINE-Element beim Erstellen eines Datenstroms auf dem Offload-Pin hinzuzufügen.

Stream-Ressourcenzuordnung

Das Streamingmodell für ACX ist paketbasiert und unterstützt ein oder zwei Pakete für einen Datenstrom. Das Rendern oder Erfassen von ACXPIN für den Streaming-Schaltkreis erhält eine Anforderung, die speicherpakete zuzuweisen, die im Datenstrom verwendet werden. Um rebalance zu unterstützen, muss der zugeordnete Arbeitsspeicher systemspeicher sein, anstatt dass der Gerätespeicher dem System zugeordnet ist. Der Treiber kann vorhandene WDF-Funktionen verwenden, um die Zuordnung durchzuführen, und gibt ein Array von Zeigern an die Pufferzuweisungen zurück. Wenn der Treiber einen einzelnen zusammenhängenden Block benötigt, kann er beide Pakete als einzelner Puffer zuweisen und einen Zeiger auf einen Offset des Puffers als zweites Paket zurückgeben.

Wenn ein einzelnes Paket zugewiesen wird, muss das Paket seitenbündig ausgerichtet sein und zweimal im Benutzermodus zugeordnet werden:

| Paket 0 | Paket 0 |

Dadurch kann GetBuffer einen Zeiger auf einen einzelnen zusammenhängenden Speicherpuffer zurückgeben, der vom Ende des Puffers bis zum Anfang reichen kann, ohne dass die Anwendung den Umbruch des Speicherzugriffs verarbeiten muss. 

Wenn zwei Pakete zugeordnet sind, werden sie im Benutzermodus zugeordnet:

| Paket 0 | Paket 1 |

Mit dem anfänglichen ACX-Paketstreaming sind zu Beginn nur zwei Pakete zugeordnet. Die virtuelle Speicherzuordnung des Clients wird neu Standard gültig sein, ohne sich für die Lebensdauer des Datenstroms zu ändern, sobald die Zuordnung und Zuordnung durchgeführt wurde. Es gibt ein Ereignis, das dem Datenstrom zugeordnet ist, um den Paketabschluss für beide Pakete anzugeben. Es wird auch ein freigegebener Puffer vorhanden sein, den das ACX-Framework verwendet, um zu kommunizieren, welches Paket mit dem Ereignis fertig ist.  

Große Pufferdatenströme Paketgrößen

Wenn Die Unterstützung für große Puffer verfügbar macht, stellt der Treiber auch einen Rückruf bereit, der verwendet wird, um die mindesten und maximalen Paketgrößen für die Wiedergabe großer Puffer zu ermitteln.   Die Paketgröße für die Datenstrompufferzuordnung wird basierend auf dem Minimum und maximum bestimmt.

Da die minimalen und maximalen Puffergrößen möglicherweise veränderlich sind, kann der Treiber den Paketzuweisungsaufruf fehlschlagen, wenn sich das Minimum und das Maximum geändert haben.

Angeben von ACX-Puffereinschränkungen

Um ACX-Puffereinschränkungen anzugeben, können ACX-Treiber die Einstellung der KS/PortCls-Eigenschaften verwenden – KSAUDIO_PACKETSIZE_CONSTRAINTS2 und die KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT Struktur.

Das folgende Codebeispiel zeigt, wie Puffergrößeneinschränkungen für WaveRT-Puffer für verschiedene Signalverarbeitungsmodi festgelegt werden.

//
// 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
        },
    }
};

Eine DSP_DEVPROPERTY Struktur wird verwendet, um die Einschränkungen zu speichern.

typedef struct _DSP_DEVPROPERTY {
    const DEVPROPKEY   *PropertyKey;
    DEVPROPTYPE Type;
    ULONG BufferSize;
    __field_bcount_opt(BufferSize) PVOID Buffer;
} DSP_DEVPROPERTY, PDSP_DEVPROPERTY;

Und ein Array dieser Strukturen wird erstellt.

const DSP_DEVPROPERTY DspR_InterfaceProperties[] =
{
    {
        &DEVPKEY_KsAudio_PacketSize_Constraints2,       // Key
        DEVPROP_TYPE_BINARY,                            // Type
        sizeof(DspR_RtPacketSizeConstraints),           // BufferSize
        &DspR_RtPacketSizeConstraints,                  // Buffer
    },
};

Später in der Funktion EvtCircuitCompositeCircuitInitialize wird die Hilfsfunktion AddPropertyToCircuitInterface verwendet, um das Array der Schnittstelleneigenschaften zum Schaltkreis hinzuzufügen.

   // Set RT buffer constraints.
    //
    status = AddPropertyToCircuitInterface(Circuit, ARRAYSIZE(DspC_InterfaceProperties), DspC_InterfaceProperties);

Die Hilfsfunktion "AddPropertyToCircuitInterface" verwendet den AcxCircuitGetSymbolicLinkName für den Schaltkreis und ruft dann IoGetDeviceInterfaceAlias auf, um die vom Schaltkreis verwendete Audioschnittstelle zu finden.

Anschließend ruft die SetDeviceInterfacePropertyDataMultiple-Funktion IoSetDeviceInterfacePropertyData auf, um den aktuellen Wert der Geräteschnittstelleneigenschaft zu ändern – die Werte der KS-Audioeigenschaft auf der Audioschnittstelle für die 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;
}

Streamstatusänderungen

Wenn eine Datenstromstatusänderung auftritt, empfängt jedes Datenstromobjekt im Endpunktaudiopfad für den Datenstrom ein Benachrichtigungsereignis vom ACX-Framework. Die Reihenfolge, in der dies geschieht, hängt von der Zustandsänderung und dem Fluss des Datenstroms ab.

  • Für Renderdatenströme, die von einem weniger aktiven Zustand zu einem aktiveren Zustand gehen, empfängt der Streaming-Schaltkreis (der das SINK registriert hat) das Ereignis zuerst. Nach der Behandlung des Ereignisses empfängt der nächste Schaltkreis im Endpunkt-Audiopfad das Ereignis.

  • Für Renderdatenströme, die von einem aktiveren Zustand zu einem weniger aktiven Zustand wechseln, empfängt der Streaming-Schaltkreis das Ereignis zuletzt. 

  • Für Aufnahmedatenströme, die von einem weniger aktiven Zustand zu einem aktiveren Zustand wechseln, empfängt die Streamingschaltung das Ereignis zuletzt. 

  • Für Aufnahmedatenströme, die von einem aktiveren Zustand zu einem weniger aktiven Zustand wechseln, empfängt die Streamingschaltung zuerst das Ereignis. 

Die obige Sortierung ist die Standardeinstellung, die vom ACX-Framework bereitgestellt wird. Ein Treiber kann das gegenteilige Verhalten anfordern, indem acxStreamBridgeInvertChangeStateSequence (definiert in ACX_STREAM_BRIDGE_CONFIG_FLAGS) festgelegt wird, wenn die ACXSTREAMBRIDGE erstellt wird, die der Treiber zum Streaming-Schaltkreis hinzufügt.  

Streamen von Audiodaten

Nachdem der Datenstrom erstellt und die entsprechenden Puffer zugewiesen wurden, befindet sich der Datenstrom im Status "Pause", auf den der Datenstrom gestartet wird. Wenn der Client den Datenstrom in den Play-Zustand versetzt, ruft das ACX-Framework alle ACXSTREAM-Objekte auf, die dem Datenstrom zugeordnet sind, um anzugeben, dass sich der Streamstatus in "Play" befindet. Die ACXPIN wird dann im Play-Zustand platziert, an dem Daten fließen. 

Rendern von Audiodaten

Sobald der Datenstrom erstellt wurde und die Ressourcen zugeordnet sind, ruft die Anwendung "Start" im Datenstrom auf, um die Wiedergabe zu starten. Beachten Sie, dass eine Anwendung GetBuffer/ReleaseBuffer aufrufen sollte, bevor Sie den Datenstrom starten, um sicherzustellen, dass das erste Paket, das sofort wiedergegeben wird, gültige Audiodaten aufweist. 

Der Client beginnt mit dem Vorabrollen eines Puffers. Wenn der Client ReleaseBuffer aufruft, wird dies in einen Anruf in AudioKSE übersetzt, der in die ACX-Ebene aufruft, wodurch EvtAcxStreamSetRenderPacket für den aktiven ACXSTREAM aufgerufen wird. Die Eigenschaft enthält den Paketindex (0-basiert) und gegebenenfalls ein EOS-Flag mit dem Byte-Offset des Endes des Datenstroms im aktuellen Paket.    Nachdem die Streamingschaltung mit einem Paket abgeschlossen ist, löst sie die puffervervollständigen Benachrichtigung aus, mit der Clients losgelassen werden, die darauf warten, das nächste Paket mit Renderaudiodaten auszufüllen. 

Der Zeitgebergesteuerte Streamingmodus wird unterstützt und wird mithilfe eines PacketCount-Werts von 1 angegeben, wenn der EvtAcxStreamAllocateRtPackets-Rückruf des Treibers aufgerufen wird.

Aufzeichnen von Audiodaten

Sobald der Datenstrom erstellt wurde und die Ressourcen zugeordnet sind, ruft die Anwendung "Start" im Datenstrom auf, um die Wiedergabe zu starten. 

Wenn der Datenstrom ausgeführt wird, füllt der Quellkreis das Aufnahmepaket mit Audiodaten aus. Sobald das erste Paket gefüllt ist, gibt der Quellkreis das Paket an das ACX-Framework frei. An diesem Punkt signalisiert das ACX-Framework das Streambenachrichtigungsereignis. 

Nachdem die Datenstrombenachrichtigung signalisiert wurde, kann der Client KSPROPERTY_RTAUDIO_GETREADPACKET senden, um den Index (0-basiert) des Pakets abzurufen, das die Erfassung abgeschlossen hat. Wenn der Client GETCAPTUREPACKET gesendet hat, kann der Treiber davon ausgehen, dass alle vorherigen Pakete verarbeitet wurden und zum Ausfüllen verfügbar sind. 

Bei der Burst-Erfassung kann der Quellkreis ein neues Paket in das ACX-Framework freigeben, sobald GETREADPACKET aufgerufen wurde.

Der Client kann auch KSPROPERTY_RTAUDIO_PACKETVREGISTER verwenden, um einen Zeiger auf die RTAUDIO_PACKETVREGISTER Struktur für den Datenstrom abzurufen. Diese Struktur wird vom ACX-Framework aktualisiert, bevor das Paket abgeschlossen ist.

Älteres KS-Kernelstreamingverhalten

Es kann Situationen geben, z. B. wenn ein Treiber die Burst-Erfassung implementiert (wie in einer Key word-Spotter-Implementierung), in der das legacy-Kernelstreamingpaketbehandlungsverhalten anstelle des PacketVRegisters verwendet werden muss. Um das vorherige paketbasierte Verhalten zu verwenden, sollte der Treiber STATUS_NOT_SUPPORTED für KSPROPERTY_RTAUDIO_PACKETVREGISTER zurückgeben.

Im folgenden Beispiel wird gezeigt, wie Dies im AcxStreamInitAssignAcxRequestPreprocessCallback für einen ACXSTREAM geschieht. Weitere Informationen finden Sie unter 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);
}

Streamposition

Das ACX-Framework ruft den EvtAcxStreamGetPresentationPosition-Rückruf auf, um die aktuelle Streamposition abzurufen. Die aktuelle Streamposition enthält playOffset und WriteOffset. 

Mit dem WaveRT-Streamingmodell kann der Audiotreiber ein HW-Positionsregister für den Client verfügbar machen. Das ACX-Streamingmodell unterstützt das Verfügbarmachen von HW-Registern nicht, da dadurch eine Neuausgewogenkeit verhindert wird. 

Jedes Mal, wenn der Streaming-Schaltkreis ein Paket abgeschlossen hat, ruft er AcxRtStreamNotifyPacketComplete mit dem 0-basierten Paketindex und dem QPC-Wert auf, der so nah am Paketabschluss wie möglich genommen wird (z. B. kann der QPC-Wert von der Interrupt Service Routine berechnet werden). Diese Informationen stehen Clients über KSPROPERTY_RTAUDIO_PACKETVREGISTER zur Verfügung, die einen Zeiger auf eine Struktur zurückgibt, die den CompletedPacketCount, den CompletedPacketQPC und einen Wert enthält, der die beiden kombiniert (wodurch der Client sicherstellen kann, dass "CompletedPacketCount" und "CompletedPacketQPC" aus demselben Paket stammen).  

Streamstatusübergänge

Nachdem ein Datenstrom erstellt wurde, übergibt ACX den Datenstrom mithilfe der folgenden Rückrufe in verschiedene Zustände:

  • EvtAcxStreamPrepareHardware übergibt den Datenstrom vom AcxStreamStateStop-Zustand in den AcxStreamStatePause-Zustand. Der Treiber sollte erforderliche Hardware wie DMA-Engines reservieren, wenn er EvtAcxStreamPrepareHardware empfängt.
  • EvtAcxStreamRun übergibt den Datenstrom vom AcxStreamStatePause-Zustand in den AcxStreamStateRun-Zustand.
  • EvtAcxStreamPause übergibt den Datenstrom vom AcxStreamStateRun-Zustand in den AcxStreamStatePause-Zustand.
  • EvtAcxStreamReleaseHardware übergibt den Datenstrom vom AcxStreamStatePause-Zustand in den AcxStreamStateStop-Zustand. Der Treiber sollte erforderliche Hardware wie DMA-Engines freigeben, wenn er EvtAcxStreamReleaseHardware empfängt.

Der Datenstrom kann den EvtAcxStreamPrepareHardware-Rückruf empfangen, nachdem er den EvtAcxStreamReleaseHardware-Rückruf empfangen hat. Dadurch wird der Datenstrom wieder in den AcxStreamStatePause-Zustand umgestellt.

Die Paketzuordnung mit EvtAcxStreamAllocateRtPackets erfolgt normalerweise vor dem ersten Aufruf von EvtAcxStreamPrepareHardware. Die zugewiesenen Pakete werden normalerweise mit EvtAcxStreamFreeRtPackets nach dem letzten Aufruf von EvtAcxStreamReleaseHardware freigegeben. Diese Bestellung ist nicht garantiert.

Der AcxStreamStateAcquire-Zustand wird nicht verwendet. ACX entfernt die Notwendigkeit, dass der Treiber über den Abrufstatus verfügt, da dieser Zustand mit den Rückrufen für die Vorbereitungshardware (EvtAcxStreamPrepareHardware) und die Freigabehardware (EvtAcxStreamReleaseHardware) implizit ist.

Unterstützung für große Pufferströme und Offload-Engine

ACX verwendet das ACXAUDIOENGINE-Element, um einen ACXPIN festzulegen, der die Offload-Datenstromerstellung und die verschiedenen Elemente behandelt, die für offload-Datenstromvolumen, Stummschaltung und Spitzenzählerzustand erforderlich sind. Dies ähnelt dem vorhandenen Audiomodulknoten in WaveRT-Treibern.

Stream close process

Wenn der Client den Datenstrom schließt, empfängt der Treiber EvtAcxStreamPause und EvtAcxStreamReleaseHardware, bevor das ACXSTREAM-Objekt vom ACX-Framework gelöscht wird. Der Treiber kann den standardmäßigen WDF EvtCleanupCallback-Eintrag in der WDF_OBJECT_ATTRIBUTES Struktur bereitstellen, wenn AcxStreamCreate aufgerufen wird, um endgültige sauber up für acXSTREAM auszuführen. WDF ruft EvtCleanupCallback auf, wenn das Framework versucht, das Objekt zu löschen. Verwenden Sie evtDestroyCallback nicht, das nur aufgerufen wird, wenn alle Verweise auf das Objekt freigegeben wurden, die unbestimmt sind.

Der Treiber sollte systemspeicherressourcen, die dem ACXSTREAM-Objekt in EvtCleanupCallback zugeordnet sind, sauber, wenn die Ressourcen noch nicht in EvtAcxStreamReleaseHardware sauber wurden.

Es ist wichtig, dass der Treiber keine Ressourcen sauber, die den Datenstrom unterstützen, bis er vom Client angefordert wurde.

Der AcxStreamStateAcquire-Zustand wird nicht verwendet. ACX entfernt die Notwendigkeit, dass der Treiber über den Abrufstatus verfügt, da dieser Zustand mit den Rückrufen für die Vorbereitungshardware (EvtAcxStreamPrepareHardware) und die Freigabehardware (EvtAcxStreamReleaseHardware) implizit ist.

Entfernen und Ungültigstellen von Stream-Überraschungen

Wenn der Treiber feststellt, dass der Datenstrom ungültig wurde (z. B. der Stecker wird nicht angeschlossen), wird der Schaltkreis alle Datenströme heruntergefahren. 

Streamspeicher sauber up

Die Entsorgung der Ressourcen des Datenstroms kann im Streamkontext des Treibers sauber up (nicht zerstört) erfolgen. Löschen Sie niemals alle Elemente, die im Kontext eines Objekts freigegeben sind, den Rückruf. Diese Anleitung gilt für alle ACX-Objekte.

Der Rückruf wird aufgerufen, nachdem der letzte Verweis nicht mehr vorhanden ist.

Im Allgemeinen wird der sauber uprückruf des Datenstroms aufgerufen, wenn das Handle geschlossen wird. Eine Ausnahme davon ist, wenn der Treiber den Datenstrom in seinem Rückruf erstellt hat. Wenn ACX diesen Datenstrom nicht direkt vor der Rückgabe aus dem Datenstromerstellungsvorgang zu seiner Stream-Brücke hinzufügen konnte, wird der Datenstrom asynchron abgebrochen, und der aktuelle Thread gibt einen Fehler an den Create-Stream-Client zurück. Der Datenstrom sollte zu diesem Zeitpunkt keine Mem-Zuordnungen zugewiesen haben. Weitere Informationen finden Sie unter EVT_ACX_STREAM_RELEASE_HARDWARE Rückruf.

Streamspeicher sauber Up-Sequenz

Der Datenstrompuffer ist eine Systemressource und sollte nur freigegeben werden, wenn der Benutzermodusclient das Handle des Datenstroms schließt. Der Puffer (der sich von den Hardwareressourcen des Geräts unterscheidet) hat dieselbe Lebensdauer wie das Handle des Datenstroms. Wenn der Client das Handle schließt, ruft ACX die Datenstromobjekte sauber up callback und dann den Löschrückruf des Datenstroms auf, wenn der Verweis auf das Objekt zu Null wechselt.

Es ist möglich, dass ACX einen STREAM-Obj-Löschvorgang auf eine Arbeitsaufgabe zurückstellen kann, wenn der Treiber einen Stream-Obj erstellt hat und dann den Create-Stream-Rückruf fehlgeschlagen ist. Um einen Deadlock mit einem WDF-Thread zum Herunterfahren zu verhindern, verschärft ACX den Löschvorgang auf einen anderen Thread. Um mögliche Nebenwirkungen dieses Verhaltens (verzögerte Freigabe von Ressourcen) zu vermeiden, kann der Treiber die zugeordneten Datenstromressourcen freigeben, bevor ein Fehler aus der Datenstromerstellung zurückgegeben wird.

Der Treiber muss die Audiopuffer freigeben, wenn ACX den EVT_ACX_STREAM_FREE_RTPACKETS Rückruf aufruft. Dieser Rückruf wird aufgerufen, wenn der Benutzer die Datenstromhandles schließt.

Da RT-Puffer im Benutzermodus zugeordnet sind, entspricht die Pufferlebensdauer der Handle-Lebensdauer. Der Treiber sollte nicht versuchen, die Audiopuffer freizugeben/freizugeben, bevor ACX diesen Rückruf aufruft.

EVT_ACX_STREAM_FREE_RTPACKETS Rückruf sollte nach EVT_ACX_STREAM_RELEASE_HARDWARE Rückruf und Ende vor EvtDeviceReleaseHardware aufgerufen werden.

Dieser Rückruf kann auftreten, nachdem der Treiber den WDF-Hardwarerückruf verarbeitet hat, da der Benutzermodusclient lange Zeit an seinen Handles halten kann. Der Treiber sollte nicht versuchen, darauf zu warten, dass diese Ziehpunkte entfernt werden. Dadurch wird nur eine 0x9f DRIVER_POWER_STATE_FAILURE Fehlerüberprüfung erstellt. Weitere Informationen finden Sie unter EVT_WDF_DEVICE_RELEASE_HARDWARE Rückruffunktion.

Dieser EvtDeviceReleaseHardware-Code aus dem ACX-Beispieltreiber zeigt ein Beispiel für den Aufruf von AcxDeviceRemoveCircuit und anschließendes Freigeben des Streaming-H/w-Speichers.

    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();

Zusammenfassung:

WDF-Gerätefreigabehardware –> h/w-Ressourcen des Geräts freigeben

AcxStreamFreeRtPackets -> release/free audio buffer associated with handle

Weitere Informationen zum Verwalten von WDF- und Schaltkreisobjekten finden Sie unter ACX WDF Driver Lifetime Management.

Streaming-DDIs

Streamingstrukturen

ACX_RTPACKET Struktur

Diese Struktur stellt ein einzelnes zugeordnetes Paket dar. Der PacketBuffer kann ein WDFMEMORY-Handle, eine MDL oder ein Puffer sein. Sie verfügt über eine zugeordnete Initialisierungsfunktion, ACX_RTPACKET_INIT.

ACX_STREAM_CALLBACKS

Diese Struktur identifiziert die Treiberrückrufe für das Streaming in das ACX-Framework. Diese Struktur ist Teil der ACX_PIN_CONFIG Struktur.

Streamingrückrufe

EvtAcxStreamAllocateRtPackets

Das EvtAcxStreamAllocateRtPackets-Ereignis teilt dem Treiber mit, RtPackets für Streaming zuzuweisen. Ein AcxRtStream empfängt PacketCount = 2 für ereignisgesteuertes Streaming oder PacketCount = 1 für timerbasiertes Streaming. Wenn der Treiber einen einzelnen Puffer für beide Pakete verwendet, sollte der zweite RtPacketBuffer eine WDF_MEMORY_DESCRIPTOR mit Type = WdfMemoryDescriptorTypeInvalid mit einem RtPacketOffset aufweisen, das am Ende des ersten Pakets ausgerichtet ist (packet[2]. RtPacketOffset = packet[1]. RtPacketOffset+packet[1]. RtPacketSize).

EvtAcxStreamFreeRtPackets

Das EvtAcxStreamFreeRtPackets-Ereignis teilt dem Treiber mit, die RtPackets freizuweisen, die in einem vorherigen Aufruf von EvtAcxStreamAllocateRtPackets zugeordnet wurden. Die gleichen Pakete aus diesem Anruf sind enthalten.

EvtAcxStreamGetHwLatency

Das EvtAcxStreamGetHwLatency-Ereignis weist den Treiber an, die Datenstromlatenz für den spezifischen Schaltkreis dieses Datenstroms bereitzustellen (die Gesamtlatenz wird eine Summe der Latenz der verschiedenen Schaltkreise sein). Die FifoSize ist in Byte und die Verzögerung ist in 100-Nanosekundeneinheiten enthalten.

EvtAcxStreamSetRenderPacket

Das EvtAcxStreamSetRenderPacket-Ereignis teilt dem Treiber mit, welches Paket gerade vom Client freigegeben wurde. Wenn keine Störungen vorhanden sind, sollte dieses Paket (CurrentRenderPacket + 1) sein, wobei CurrentRenderPacket das Paket ist, von dem der Treiber derzeit gestreamt wird.

Flags können 0 oder AcxStreamSetRenderPacketEndOfStream sein, was angibt, dass das Paket das letzte Paket im Stream ist, und EosPacketLength ist eine gültige Länge in Bytes für das Paket.

Der Treiber sollte das CurrentRenderPacket weiterhin erhöhen, da Pakete gerendert werden, anstatt das CurrentRenderPacket so zu ändern, dass er mit diesem Wert übereinstimmt.

EvtAcxStreamGetCurrentPacket

Das EvtAcxStreamGetCurrentPacket teilt dem Treiber mit, anzugeben, welches Paket (0-basiert) derzeit auf der Hardware gerendert wird oder derzeit von der Aufnahmehardware gefüllt wird.

EvtAcxStreamGetCapturePacket

Das EvtAcxStreamGetCapturePacket teilt dem Treiber mit, anzugeben, welches Paket (0-basiert) zuletzt vollständig gefüllt wurde, einschließlich des QPC-Werts zum Zeitpunkt, zu dem der Treiber mit dem Ausfüllen des Pakets begonnen hat.

EvtAcxStreamGetPresentationPosition

Die EvtAcxStreamGetPresentationPosition teilt dem Treiber mit, die aktuelle Position zusammen mit dem QPC-Wert zum Zeitpunkt der Berechnung der aktuellen Position anzugeben.

STREAM STATE-EREIGNISSE

Der Streamingstatus für einen ACXSTREAM wird von den folgenden APIs verwaltet.

EVT_ACX_STREAM_PREPARE_HARDWARE

EVT_ACX_STREAM_RELEASE_HARDWARE

EVT_ACX_STREAM_RUN

EVT_ACX_STREAM_PAUSE

Streaming von ACX-APIs

AcxStreamCreate

AcxStreamCreate erstellt einen ACX-Stream, der zum Steuern des Streamingverhaltens verwendet werden kann.

AcxRtStreamCreate

AcxRtStreamCreate erstellt einen ACX-Stream, der verwendet werden kann, um das Streamingverhalten zu steuern und die Paketzuweisung zu verarbeiten und den Streamingstatus zu kommunizieren.

AcxRtStreamNotifyPacketComplete

Der Treiber ruft diese ACX-API auf, wenn ein Paket abgeschlossen ist. Die Paketabschlusszeit und der 0-basierte Paketindex sind enthalten, um die Clientleistung zu verbessern. Das ACX-Framework legt alle dem Datenstrom zugeordneten Benachrichtigungsereignisse fest.

Weitere Informationen

Übersicht über ACX-Audioklassenerweiterungen

ACX-Referenzdokumentation

Zusammenfassung von ACX-Objekten