Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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.
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-Streaming-Kommunikations-Stack
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 IO-Warteschlangen und leitet WDF-Anfragen über diese Warteschlangen weiter. 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 Benachrichtigung des Treibers, wann ein Paket bereit ist, und den Erhalt von Informationen darüber, wann der Treiber die Verarbeitung eines Pakets abgeschlossen hat.
Die wichtigsten Anforderungen für streaming-Signalisierung:
- Unterstützung für Glitch-Free-Wiedergabe
- Niedrige Latenz
- Alle erforderlichen Sperren sind auf den betreffenden Datenstrom beschränkt.
- Benutzerfreundlichkeit für Treiberentwickler
Um mit dem Treiber zu kommunizieren und 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 sowohl die (1-basierte) Paketanzahl des letzten abgeschlossenen Pakets als auch den 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 Art und Weise, wie die Verarbeitung des Pufferabschlusses in PortCls gehandhabt wird. Die IRPs sind sehr ähnlich, und der freigegebene Puffer ermöglicht es dem Treiber, die Paketanzahl und das Timing direkt zu kommunizieren, ohne sich auf IRPs verlassen zu müssen. Treiber müssen sicherstellen, dass sie nichts tun, das Zugriff auf Sperren erfordert, die auch in den Stream-Kontrollpfaden verwendet werden. Dies ist erforderlich, um Fehlfunktionen 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:
- Audio-Engine-Beschreibung, die dem Audiostapel mitteilt, welche Pins am Wellenfilter Offload- und Loopback-Unterstützung sowie Unterstützung für die Hostwiedergabe 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.
- Lautstärke, einschließlich Unterstützung beim Hochskalieren, da mit den größeren Puffern das Softwarevolumen nicht reagiert.
- 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 in der AudioEngine zu aktivieren oder zu deaktivieren.
Wenn ein Datenstrom auf dem Offload-Pin erstellt wird, unterstützt er Volume, Local FX und Loopback-Schutz.
Niedrigenergie-Wiedergabe 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 das ACXAUDIOENGINE-Element während der Erstellung des ACXCIRCUIT dem Schaltkreis hinzu.
Ausgelagerte Streamerstellung
Der Treiber fügt außerdem ein ACXAUDIOENGINE-Element zu Streams hinzu, die für das Offloading erstellt wurden, um die Kontrolle über Lautstärke, Stummschaltung und Pegelanzeige zu ermöglichen.
Streamingdiagramm
Dieses Diagramm zeigt einen ACX-Treiber mit mehreren Stapeln.
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 Senkenpin und einen Quellpin. Diese Pins werden vom ACX-Framework verwendet, um die Verbindungen des Schaltkreises für den Audiostapel zugänglich 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 aus der Schaltung erstellten Datenstroms zu steuern. 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 zum Zeitpunkt der Schaltkreiserstellung und zur 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 Anfragen zum Erstellen eines Datenstroms entgegennimmt. 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 (auf die gleiche Weise wie die ACXCIRCUIT-Objekte verknüpft sind).
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ückenpins für einen Schaltkreis, wenn der Treiber sie nicht während der Schaltkreiserstellung hinzufü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 gibt die unterstützten Formate für die Stream-Erstellung bekannt, indem er die unterstützten Formate für jeden Modus zur ACXPIN-Erstellung mithilfe von AcxPinAssignModeDataFormatList und AcxPinGetRawDataFormatList hinzufügt. 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 Stream-Formate für den Endpunkt werden durch die 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 Stromerstellungsanforderung an den Downstream-Schaltkreis weitergeleitet wird. Wenn der Bridge-Pin des Upstream-Kreises keine Formate aufweist, wird das ursprüngliche Stream-Format verwendet. Wenn der verbundene Pin des nachgeschalteten Schaltkreises das verwendete Format nicht unterstützt, schlägt die Datenstromerstellung fehl.
Wenn ein Gerätekreis eine Datenstromformatänderung durchführt, sollte der Gerätetreiber dem nachgeschalteten Überbrückungsstift 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, übernehmen die Stream-Objekte die Streaming-Logik.
Die Stream-Erstellungsanforderung wird an die entsprechende PIN gesendet, die als Teil der Topologiegenerierung des Hauptkreises generiert wird, indem der EvtAcxCircuitCreateStream aufgerufen wird, der während der Erstellung des Hauptkreises spezifiziert wurde.
Der Streaming-Schaltkreis ist der Upstream-Schaltkreis, der zunächst die Datenstromerstellungsanforderung verarbeitet.
- Es aktualisiert die ACXSTREAM_INIT-Struktur und weist AcxStreamCallbacks und AcxRtStreamCallbacks zu.
- 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 Stream-Erstellung 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 Stream-spezifischen 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 durch das 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.
Beispiel für einen Render-Stream
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 Steckerverbindungserkennung oder Details der Endpunkthardware übernehmen.
- AudioKSE ruft NtCreateFile auf, um einen Datenstrom zu erstellen.
- Dieser Prozess wird über ACX gefiltert und endet mit dem Aufrufen der EvtAcxPinCreateStream-Funktion des DSP-Schaltkreises, wobei der Pin, das Datenformat (einschließlich Modus) und die Geräteinformationen übergeben werden.
- Der DSP-Schaltkreis überprüft die Datenformatinformationen, um sicherzustellen, dass er den erstellten Datenstrom verarbeiten kann.
- Der DSP-Schaltkreis erstellt das ACXSTREAM-Objekt, um den Datenstrom darzustellen.
- Der DSP-Schaltkreis weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu.
- Der DSP-Schaltkreis gibt den Ausführungsfluss an das ACX-Framework zurück, welches dann den nächsten Schaltkreis im Endpunkt-Audiopfad, den CODEC-Schaltkreis, aufruft.
- Der CODEC-Schaltkreis überprüft die Datenformatinformationen, um zu bestätigen, dass es das Rendern der Daten verarbeiten kann.
- Der CODEC-Schaltkreis weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu.
- Der CODEC-Schaltkreis fügt sich als Stromsenke zum ACXSTREAM hinzu.
- Der CODEC-Schaltkreis gibt den Ausführungsfluss an das ACX-Framework zurück, das dann den nächsten Schaltkreis im Endpunktaudiopfad, den AMP-Schaltkreis, aufruft.
- Der AMP-Schaltkreis weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu.
- Der AMP-Schaltkreis gibt den Ausführungsfluss an das ACX-Framework zurück. Zu diesem Zeitpunkt ist die Stream-Erstellung fertig.
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 Offload-Streams sollte der Gerätetreiber während der Erstellung des Streaming-Zyklus Folgendes tun:
- Erstellen Sie die Host-, Offload- und Loopback-ACXPIN-Objekte, und fügen Sie sie dem ACXCIRCUIT hinzu.
- Erstellen Sie ACXVOLUME-, ACXMUTE- und ACXPEAKMETER-Elemente. Diese werden dem ACXCIRCUIT nicht direkt hinzugefügt.
- Initialisieren Sie eine ACX_AUDIOENGINE_CONFIG Struktur, wobei Die Objekte HostPin, OffloadPin, LoopbackPin, VolumeElement, MuteElement und PeakMeterElement zugewiesen werden.
- 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.
Streamressourcenzuordnung
Das Streamingmodell für ACX ist paketbasiert und unterstützt ein oder zwei Pakete für einen Datenstrom. Das Rendern oder Erfassen des 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 Allokation durchzuführen, und ein Array von Zeigern auf die Pufferallokationen zurückgeben. 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 sich möglicherweise vom Ende des Puffers bis zum Anfang erstreckt, ohne dass die Anwendung die Verwaltung des Speicherzugriffs übernehmen muss.
Wenn zwei Pakete zugewiesen werden, werden sie dem Benutzermodus zugeordnet.
| Paket 0 | Paket 1 |
Mit dem anfänglichen ACX-Paketstreaming sind zu Beginn nur zwei Pakete zugeordnet. Die virtuelle Speicherzuordnung des Clients bleibt über die gesamte Lebensdauer des Datenstroms hinweg unverändert gültig, sobald die Allokation und Abbildung durchgeführt worden ist. 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 Unterstützung für große Puffer bereitgestellt wird, stellt der Treiber auch einen Rückruf bereit, der verwendet wird, um die minimalen und maximalen Paketgrößen für die Wiedergabe großer Puffer zu ermitteln. Die Paketgröße für die Stream-Pufferzuordnung 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. Nachdem das Ereignis verarbeitet wurde, 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 der Treiber AcxStreamBridgeInvertChangeStateSequence (definiert in ACX_STREAM_BRIDGE_CONFIG_FLAGS) festlegt, wenn das ACXSTREAMBRIDGE erstellt wird, das der Treiber zur Streamingschaltung 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 Streaming-Schaltung mit einem Paket abgeschlossen ist, löst sie die Puffer-vollständig-Benachrichtigung aus, mit der Clients freigegeben 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 läuft, füllt die Quellschaltung das Aufnahmepaket mit Audiodaten auf. 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 Signalisierungspaket vervollständigt wird.
Älteres KS-Kernelstreamingverhalten
Es kann Situationen geben, z. B. wenn ein Treiber die Burst-Aufnahme implementiert (wie in einer Keyword-Spotter-Implementierung), in denen das Legacy-Kernel-Streaming-Paketbehandlungsverhalten 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 die Offenlegung von HW-Registern nicht, da dies eine Neuausrichtung verhindern würde.
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 den Erfassungszustand haben muss, da dieser Zustand im Zusammenhang mit den Rückrufen zum Vorbereiten der Hardware (EvtAcxStreamPrepareHardware) und zum Freigeben der Hardware (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-Schließvorgang
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 die endgültige Bereinigung für ACXSTREAM durchzuführen. WDF ruft EvtCleanupCallback auf, wenn das Framework versucht, das Objekt zu löschen. Verwenden Sie EvtDestroyCallback nicht, da es nur aufgerufen wird, wenn alle Verweise auf das Objekt freigegeben wurden, was unbestimmbar ist.
Der Treiber sollte Systemspeicherressourcen bereinigen, die dem ACXSTREAM-Objekt in EvtCleanupCallback zugeordnet sind, wenn die Ressourcen in EvtAcxStreamReleaseHardware noch nicht bereinigt wurden.
Es ist wichtig, dass der Treiber keine Ressourcen bereinigt, die den Stream unterstützen, bis der Client dies anfordert.
Der AcxStreamStateAcquire-Zustand wird nicht verwendet. ACX entfernt die Notwendigkeit, dass der Treiber über den Erwerbsstatus verfügt, da dieser Zustand mit den Rückrufen für das Vorbereiten der Hardware (EvtAcxStreamPrepareHardware) und das Freigeben der Hardware (EvtAcxStreamReleaseHardware) implizit ist.
Entfernen und Ungültigstellen von Stream-Überraschungen
Wenn der Treiber feststellt, dass der Stream ungültig geworden ist (z. B. der Stecker wird herausgezogen), schaltet der Schaltkreis alle Streams ab.
Bereinigung des Streamspeichers
Die Entsorgung der Ressourcen des Datenstroms kann in der Datenstromkontextbereinigung (nicht zerstört) des Treibers 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 die Bereinigungsrückruffunktion 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 Callback.
Stream-Speicherbereinigungssequenz
Der Datenstrompuffer ist eine Systemressource und sollte nur freigegeben werden, wenn der Benutzermodus-Client das Handle des Datenstroms schließt. Der Puffer (der sich von den Hardwareressourcen unterscheidet) hat dieselbe Lebensdauer wie das Stream-Handle. Wenn der Client das Handle schließt, ruft ACX den Bereinigungsrückruf für das Stream-Objekt auf, und anschließend den Löschrückruf des Stream-Objekts, wenn der Verweis auf das Objekt null wird.
Es ist möglich, dass ACX das Löschen eines STREAM-Objekts auf ein Arbeitsobjekt verschieben kann, wenn der Treiber ein Stream-Objekt erstellt hat und dann der Aufruf zur Erzeugung des Streams fehlgeschlagen ist. Um einen Deadlock mit einem herunterfahrenden WDF-Thread zu verhindern, verschiebt 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 Stream-Ressourcen freigeben, bevor beim Erstellen des Streams ein Fehler zurückgegeben wird.
Der Treiber muss die Audiopuffer freigeben, wenn ACX den EVT_ACX_STREAM_FREE_RTPACKETS Rückruf aufruft. Dieser Rückruf wird ausgeführt, wenn der Benutzer die Stream-Handles schließt.
Da RT-Puffer im Benutzermodus zugeordnet sind, entspricht die Pufferlebensdauer der Handle-Lebensdauer. Der Treiber sollte nicht versuchen, die Audiopuffer freizugeben, bevor ACX diesen Callback aufruft.
EVT_ACX_STREAM_FREE_RTPACKETS Rückruf sollte nach dem EVT_ACX_STREAM_RELEASE_HARDWARE Rückruf aufgerufen und vor EvtDeviceReleaseHardware abgeschlossen werden.
Dieser Rückruf kann auftreten, nachdem der Treiber den WDF-Freigabe-Hardware-Rückruf verarbeitet hat, weil der Benutzermodus-Client seine Handles lange Zeit behalten 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 der 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 -> gibt den mit dem Handle verbundenen Audiopuffer frei.
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 (paket[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 ist eine Summe der Latenz der verschiedenen Schaltkreise). Die FifoSize ist in Bytes und die Verzögerung in 100-Nanosekundeneinheiten angegeben.
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 KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM = 0x200sein, was angibt, dass das Paket das letzte Paket im Datenstrom ist, und EosPacketLength ist eine gültige Länge in Bytes für das Paket. Weitere Informationen finden Sie unter OptionsFlags- in KSSTREAM_HEADER Struktur (ks.h).
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
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.