ACX-Streaming
In diesem Thema werden ACX-Streaming und die zugeordnete Pufferung erläutert, was für eine störungsfreie Audioerfahrung 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 Audiostram auf der Hardware einer bestimmten Verbindung dar. Ein AcxStream kann ein oder mehrere AcxElements-ähnliche Objekte aggregieren.
Das ACX-Framework unterstützt zwei Streamtypen. 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 Basis-Stream, bietet nur Unterstützung für Streamstatusübergänge.
Bei einem einzelnen Verbindungsendpunkt muss es sich bei der Verbindung um eine Streaming-Verbindung handeln, die einen RT-Paketstream erstellt. Wenn zwei oder mehr Verbindungen verbunden sind, um einen Endpunkt zu erstellen, ist die erste Verbindung im Endpunkt die Streaming-Verbindung und erstellt einen RT-Paketstream. Verbundene Verbindungen erstellen Basis-Streams, um Ereignisse im Zusammenhang mit Streamstatusübergängen zu empfangen.
Weitere Informationen finden Sie unter ACX-Stream in Zusammenfassung von ACX-Objekten. Die DDIs für den Stream werden im acxstreams.h-Header definiert.
ACX-Streaming-Kommunikationsstapel
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. Dazu gehört, dem Treiber mitzuteilen, wann ein Paket bereit ist, und Daten darüber zu empfangen, wann der Treiber die Verarbeitung eines Pakets abgeschlossen hat.
Die Hauptanforderungen für die Streaming-Signalisierung:
- Unterstützung der störungsfreien Wiedergabe
- Geringe Latenz
- Alle erforderlichen Sperren sind auf den betreffenden Stream beschränkt.
- Benutzerfreundlichkeit für Treiberentwickler
Um mit dem Treiber zu kommunizieren und den Streaming-Status zu signalisieren, verwendet ACX Ereignisse mit einem gemeinsam genutzten Puffer und direkte IRP-Aufrufe. Diese werden als Nächstes beschrieben.
Geteilter 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 alles bestimmen kann, was er zum Fortsetzen des Streamings benötigt, während gleichzeitig die Notwendigkeit direkter IRP-Aufrufe 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 müssen 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 mit dem Client geteiltes Ereignis, um anzuzeigen, dass der Client die neue Paketanzahl lesen darf.
Direkte IRP-Aufrufe
Für die Kommunikation vom Client an den Treiber werden direkte IRP-Aufrufe verwendet. Dies reduziert die Komplexität bei der Sicherstellung, dass WDF-Anfragen zeitnah bearbeitet werden, und funktioniert nachweislich gut in der vorhandenen Architektur.
Der Client kann jederzeit die aktuelle Paketanzahl anfordern oder die aktuelle Paketanzahl an den Gerätetreiber angeben. Diese Anforderungen rufen die EvtAcxStreamGetCurrentPacket- und EvtAcxStreamSetRenderPacket-Ereignishandler des Gerätetreibers auf. Der Client kann auch das aktuelle Erfassungspaket anfordern, das den EvtAcxStreamGetCapturePacket-Ereignishandler des Gerätetreibers 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, was Zugriff auf Sperren erfordert, die auch in den Stream-Kontrollpfaden verwendet werden – dies ist notwendig, 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 sie 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 aktiviert werden kann und die erforderlichen Vorgänge mit maximaler Geschwindigkeit ausführt, 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 bestehenden Streaming-Modellen wird die Wiedergabe mit geringem Stromverbrauch durch die Auslagerungswiedergabe unterstützt. Ein Audiotreiber kündigt Unterstützung für die Auslagerungswiedergabe an, indem er einen AudioEngine-Knoten im Wave-Filter für einen Endpunkt bereitstellt. 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 dem Audio-Stack mitteilt, welche Pins am Wave-Filter Auslagerungs- und Loopback-Unterstützung (und Host-Wiedergabeunterstützung) bieten.
- Puffergrößenbereich, der dem Audio-Stack die minimale und maximale Puffergröße mitteilt, die für die Auslagerungswiedergabe unterstützt werden kann. 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 das Software-Volume bei größeren Puffern nicht reagiert.
- Loopback-Schutz, der dem Treiber angibt, die AudioEngine-Loopback-Pin stummzuschalten, wenn mindestens ein ausgelagerter Stream geschützte Inhalte enthält.
- Globaler FX-Zustand, um GFX für AudioEngine zu aktivieren oder zu deaktivieren.
Wenn ein Stream auf dem Auslagerungs-Pin erstellt wird, unterstützt der Stream Volume, Local FX und Loopback Protection.
Wiedergabe mit niedriger Energie 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-, Auslagerungs- und Loopback-Streaming sowie ein ACXAUDIOENGINE-Element, das beschreibt, welche dieser Pins für Host, Auslagerung und Loopback verwendet werden. Der Treiber fügt die Pins und das ACXAUDIOENGINE-Element während der Verbindungserstellung dem ACXCIRCUIT hinzu.
Erstellung des Auslagerungsstreams
Der Treiber fügt außerdem ein ACXAUDIOENGINE-Element zu den Streams hinzu, die für die Auslagerung erstellt wurden, um die Kontrolle über Lautstärke, Stummschaltung und Spitzenzähler zu ermöglichen.
Streamingdiagramm
Dieses Diagramm zeigt einen ACX-Treiber mit mehreren Stacks.
Jeder ACX-Treiber steuert einen separaten Teil der Audiohardware und kann von einem anderen Anbieter bereitgestellt werden. ACX bietet eine kompatible Kernel-Streamingschnittstelle, 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 der Schaltung mit dem Audiostapel bereitzustellen. Bei einer Renderverbindung wird der Quell-Pin verwendet, um das Renderverhalten eines Streams zu steuern, der aus der Verbindung erstellt wurde. Bei einer Erfassungsverbindung wird der Senken-Pin verwendet, um das Erfassungsverhalten eines Streams zu steuern, der aus der Verbindung 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 Verbindungserstellung und für die Registrierung der ACXPINs bei ACX verantwortlich. Der ACXCIRCUIT muss nur die Render- oder Erfassungs-Pins für die Verbindung erstellen. Das ACX-Framework erstellt den anderen Pin, der für die Verbindung und Kommunikation mit der Schaltung benötigt wird.
Streamingverbindung
Wenn ein Endpunkt aus einer einzelnen Verbindung besteht, handelt es sich bei dieser Verbindung um die Streaming-Verbindung.
Wenn ein Endpunkt aus mehr als einer Verbindung besteht, die von einem oder mehreren Gerätetreibern erstellt wird, werden die Verbindungen in der spezifischen Reihenfolge verbunden, die von der ACXCOMPOSITETEMPLATE bestimmt wird, die den zusammengesetzten Endpunkt beschreibt. Die erste Verbindung im Endpunkt ist die Streaming-Verbindung für den Endpunkt.
Die Streaming-Verbindung sollte AcxRtStreamCreate verwenden, um einen RT-Paketstream als Reaktion auf EvtAcxCircuitCreateStream zu erstellen. Der mit AcxRtStreamCreate erstellte ACXSTREAM ermöglicht es dem Streaming-Verbindungstreiber, den Puffer für das Streaming zuzuweisen und den Streaming-Flow als Reaktion auf die Client- und Hardwareanforderungen zu steuern.
Die folgenden Verbindungen im Endpunkt sollten AcxStreamCreate verwenden, um einen Basis-Stream als Reaktion auf EvtAcxCircuitCreateStream zu erstellen. Die mit AcxStreamCreate von den folgenden Verbindungen erstellten ACXSTREAM-Objekte ermöglichen es den Treibern, Hardware als Reaktion auf Stream-Statusänderungen wie Pause oder Run zu konfigurieren.
Das Streaming ACXCIRCUIT ist die erste Verbindung, die die Anforderungen zum Erstellen eines Streams empfängt. Die Anforderung enthält das Gerät, den Pin und das Datenformat (einschließlich Modus).
Jede ACXCIRCUIT im Audiopfad erstellt ein ACXSTREAM-Objekt, das die Streaminstanz der Verbindung darstellt. Das ACX-Framework verknüpft die ACXSTREAM-Objekte miteinander (ähnlich wie die ACXCIRCUIT-Objekte verknüpft sind).
Vor- und nachgeschaltete Verbindungen
Die Stream-Erstellung beginnt an der Streaming-Verbindung und wird in der Reihenfolge, in der die Verbindungen verbunden sind, an jede nachgelagerte Verbindung weitergeleitet. Die Verbindungen werden zwischen Bridge-Pins hergestellt, die mit Communication gleich AcxPinCommunicationNone erstellt wurden. Das ACX-Framework erstellt einen oder mehrere Bride-Pins für eine Verbindung, wenn der Treiber sie nicht zum Zeitpunkt der Verbindungserstellung hinzufügt.
Für jede Verbindung, beginnend mit der Streaming-Verbindung, wird der AcxPinTypeSource-Bridge-Pin mit der nächsten nachgelagerten Verbindung verbunden. Der letzte Verbindung verfügt über einen Endpunkt-Pin, der die Audio-Endpunkt-Hardware beschreibt (z. B. ob es sich bei dem Endpunkt um ein Mikrofon oder einen Lautsprecher handelt und ob die Buchse eingesteckt ist).
Für jede Verbindung, die der Streaming-Verbindung folgt, wird der AcxPinTypeSink-Bridge-Pin mit der nächsten vorgeschalteten Verbindung verbunden.
Streamformat-Aushandlung
Der Treiber kündigt die unterstützten Formate für die Streamerstellung an, indem die unterstützten Formate pro Modus zur ACXPIN-Erstellung mit AcxPinAssignModeDataFormatList und AcxPinGetRawDataFormatList hinzugefügt werden. Bei Endpunkten mit mehreren Verbindungen kann ein ACXSTREAMBRIDGE verwendet werden, um den Modus und die Formatunterstützung zwischen ACX-Schaltkreise zu koordinieren. Die unterstützten Streamformate für den Endpunkt werden von den Streaming-ACXPINs bestimmt, die von der Streaming-Verbindung erstellt wurden. Die von den folgenden Verbindungen verwendeten Formate werden durch den Bridge-Pin der vorherigen Verbindung im Endpunkt bestimmt.
Standardmäßig erstellt das ACX-Framework eine ACXSTREAMBRIDGE zwischen jeder Verbindung in einem Endpunkt mit mehreren Verbindungen. Die Standard-ACXSTREAMBRIDGE verwendet das Standardformat des RAW-Modus des Bridge-Pins der vorgeschalteten Verbindung, wenn sie die Stream-Erstellungsanforderung an die nachgeschaltete Verbindung weiterleitet. Wenn der Bridge-Pin der vorgeschalteten Verbindung keine Formate hat, wird das ursprüngliche Stream-Format verwendet. Wenn der verbundene Pin der nachgeschalteten Verbindung das verwendete Format nicht unterstützt, schlägt die Streamerstellung fehl.
Wenn eine Geräteverbindung eine Änderung des Stream-Formats durchführt, sollte der Gerätetreiber das nachgelagerte Format zum nachgelagerten Bridge-Pin hinzufügen.
Streamerstellung
Der erste Schritt bei der Streamerstellung besteht darin, die ACXSTREAM-Instanz für jeden ACXCIRCUIT im Endpunkt-Audiopfad zu erstellen. ACX rufte den EvtAcxCircuitCreateStream jeder Verbindung auf. ACX beginnt mit der Hauptverbindung und ruft den EvtAcxCircuitCreateStream jeder Verbindung in der Reihenfolge auf, die mit der Endverbindung endet. Die Reihenfolge kann umgekehrt werden, indem das AcxStreamBridgeInvertChangeStateSequence-Flag (definiert in ACX_STREAM_BRIDGE_CONFIG_FLAGS) für die Stream-Bridge angegeben wird. Nachdem alle Verbindungen ein Streamobjekt erstellt haben, verarbeiten die Streamobjekte die Streaminglogik.
Die Stream-Erstellungsanforderung wird an die entsprechende PIN gesendet, die im Rahmen der Topologiegenerierung der Hauptverbindung durch Aufrufen des bei der Erstellung der Hauptverbindung angegebenen EvtAcxCircuitCreateStream generiert wurde.
Die Streaming-Verbindung ist die vorgelagerte Verbindung, die zunächst die Streamerstellungsanforderung verarbeitet.
- Sie aktualisiert die ACXSTREAM_INIT-Struktur, indem AcxStreamCallbacks und AcxRtStreamCallbacks zugewiesen werden
- Sie erstellt das ACXSTREAM-Objekt mit AcxRtStreamCreate.
- Sie erstellt streamspezifische Elemente (z. B. ACXVOLUME oder ACXAUDIOENGINE)
- Sie fügt die Elemente zum ACXSTREAM-Objekt hinzu.
- Sie gibt das ACXSTREAM-Objekt zurück, das für das ACX-Framework erstellt wurde.
ACX leitet dann die Streamerstellung an die nächste nachgeschaltete Verbindung weiter.
- Sie aktualisiert die ACXSTREAM_INIT-Struktur, wobei AcxStreamCallbacks zugewiesen werden.
- Sie erstellt das ACXSTREAM-Objekt mit AcxStreamCreate.
- Sie erstellt alle streamspezifischen Elemente.
- Sie fügt die Elemente zum ACXSTREAM-Objekt hinzu.
- Sie gibt das ACXSTREAM-Objekt zurück, das für das ACX-Framework erstellt wurde.
Der Kommunikationskanal zwischen Verbindungen in einem Audiopfad verwendet ACXTARGETSTREAM-Objekte. In diesem Beispiel hat jede Verbindung Zugriff auf eine E/A-Warteschlange für die Verbindung davor und die Verbindung 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 jede Verbindung dem ACXSTREAM-Objekt Kontextinformationen hinzufügen, um private Daten für den Stream zu speichern und nachzuverfolgen.
Renderstream-Beispiel
Erstellen eines Renderstreams auf einem Endpunktaudiopfad, bestehend aus drei Schaltkreisen: DSP, CODEC und AMP. Die DSP-Verbindung fungiert als Streamingverbindung und hat einen EvtAcxPinCreateStream-Handler bereitgestellt. Die DSP-Verbindung funktioniert auch als Filterverbindung: Je nach Streammodus und Konfiguration kann die Signalverarbeitung auf die Audiodaten angewendet werden. Die CODEC-Schaltung stellt den DAC dar und stellt die Audio-Senke-Funktionalität bereit. Die AMP-Verbindung stellt die analoge Hardware zwischen dem DAC und dem Lautsprecher dar. Die AMP-Verbindung übernimmt möglicherweise die Buchsenerkennung oder andere Endpunkt-Hardwaredetails.
- AudioKSE ruft NtCreateFile auf, um einen Stream zu erstellen.
- Dies wird durch ACX gefiltert und endet mit dem Aufruf des EvtAcxPinCreateStream der DSP-Verbindung mit dem Pin, dem Datenformat (einschließlich Modus) und den Geräteinformationen.
- Die DSP-Verbindung überprüft die Datenformatinformationen, um sicherzustellen, dass sie den erstellten Stream verarbeiten kann.
- Die DSP-Verbindung erstellt das ACXSTREAM-Objekt, um den Stream darzustellen.
- Die DSP-Verbindung weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu.
- Die DSP-Verbindung gibt den Ausführungsflow an das ACX-Framework zurück, das dann die nächste Verbindung im Endpunktaudiopfad, die CODEC-Verbindung, aufruft.
- Die CODEC-Verbindung überprüft die Datenformatinformationen, um zu bestätigen, dass sie das Rendern der Daten verarbeiten kann.
- Die CODEC-Verbindung weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu.
- Die CODEC-Verbindung fügt sich selbst als Stream-Senke zum ACXSTREAM hinzu.
- Die CODEX-Verbindung gibt den Ausführungsflow an das ACX-Framework zurück, das dann die nächste Verbindung im Endpunktaudiopfad, die AMP-Verbindung, aufruft.
- Die AMP-Verbindung weist eine private Kontextstruktur zu und ordnet sie dem ACXSTREAM zu.
- Die AMP-Verbindung gibt den Ausführungsflow an das ACX-Framework zurück. Zu diesem Zeitpunkt ist die Streamerstellung abgeschlossen.
Große Puffer-Streams
Große Puffer-Streams werden auf dem ACXPIN erstellt, der für die Auslagerung durch das ACXCIRCUIT ACXAUDIOENGINE-Element festgelegt ist.
Zur Unterstützung von Auslagerungsstreams sollte der Gerätetreiber während der Erstellung der Streaming-Verbindung Folgendes ausführen:
- Erstellen Sie die Host-, Auslagerungs- 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, und weisen Sie die Objekte HostPin, OffloadPin, LoopbackPin, VolumeElement, MuteElement und PeakMeterElement zu.
- Erstellen Sie das ACXAUDIOENGINE-Element.
Treiber müssen ähnliche Schritte ausführen, um ein ACXSTREAMAUDIOENGINE-Element beim Erstellen eines Streams auf dem Auslagerungs-Pin hinzuzufügen.
Stream-Ressourcenzuweisung
Das Streamingmodell für ACX ist paketbasiert und unterstützt ein oder zwei Pakete für einen Stream. Der Render- oder Capture-ACXPIN für die Streaming-Verbindung erhält eine Anfrage zur Zuweisung der im Stream verwendeten Speicherpakete. Um den Ausgleich zu unterstützen, muss der zugewiesene Speicher ein Systemspeicher und kein Gerätespeicher sein, der 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 einen einzigen Puffer zuordnen und als zweites Paket einen Zeiger an eine Auslagerung des Puffers zurückgeben.
Wenn ein einzelnes Paket zugewiesen wird, muss das Paket seitenbündig ausgerichtet sein und zweimal dem 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 das Umschließen 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 Zuordnung des virtuellen Clientspeichers bleibt für die gesamte Lebensdauer des Streams unverändert gültig, sobald die Zuweisung und Zuordnung durchgeführt wurde. Dem Stream ist ein Ereignis zugeordnet, das den Paketabschluss für beide Pakete anzeigt. Es wird auch ein freigegebener Puffer vorhanden sein, den das ACX-Framework verwendet, um zu kommunizieren, welches Paket mit dem Ereignis abgeschlossen wurde.
Große Pufferströme mit Paketgrößen
Wenn die Unterstützung für große Puffer bereitgestellt wird, stellt der Treiber auch einen Rückruf bereit, der verwendet wird, um die minimale und maximale Paketgröße für die Wiedergabe großer Puffer zu bestimmen. Die Paketgröße für die Stream-Pufferzuweisung wird anhand des Minimums und Maximums bestimmt.
Da die minimale und maximale Puffergröße veränderlich sein kann, kann der Treiber den Paketzuteilungsaufruf als fehlschlagen melden, wenn sich die minimale und maximale Größe geändert hat.
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 zur Verbindung hinzuzufügen.
// Set RT buffer constraints.
//
status = AddPropertyToCircuitInterface(Circuit, ARRAYSIZE(DspC_InterfaceProperties), DspC_InterfaceProperties);
Die Hilfsfunktion „AddPropertyToCircuitInterface“ verwendet den AcxCircuitGetSymbolicLinkName für die Verbindung und ruft dann IoGetDeviceInterfaceAlias auf, um die von der Verbindung verwendete Audioschnittstelle zu finden.
Anschließend ruft die SetDeviceInterfacePropertyDataMultiple-Funktion die IoSetDeviceInterfacePropertyData-Funktion auf, um den aktuellen Wert der Geräteschnittstelleneigenschaft, die KS-Audioeigenschaftenwerte auf der Audioschnittstelle für ACXCIRCUIT, zu ändern.
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 Stream-Statusänderung auftritt, erhält jedes Stream-Objekt im Endpunkt-Audiopfad für den Stream ein Benachrichtigungsereignis vom ACX-Framework. Die Reihenfolge, in der dies geschieht, hängt von der Zustandsänderung und dem Stream-Flow ab.
Bei Render-Streams, die von einem weniger aktiven Zustand in einen aktiveren Zustand wechseln, empfängt die Streaming-Verbindung (die den SINK registriert hat) das Ereignis zuerst. Sobald das Ereignis verarbeitet wurde, empfängt die nächste Verbindung im Endpunkt-Audiopfad das Ereignis.
Bei Render-Streams, die von einem aktiveren Zustand in einen weniger aktiven Zustand wechseln, empfängt die Streaming-Verbindung das Ereignis zuletzt.
Bei Erfassungs-Streams, die von einem wenigern aktiven Zustand in einen aktiveren Zustand wechseln, empfängt die Streaming-Verbindung das Ereignis zuletzt.
Bei Erfassungs-Streams, die von einem aktiveren Zustand in einen weniger aktiven Zustand wechseln, empfängt die Streaming-Verbindung das Ereignis zuerst.
Die obige Sortierung ist die Standardeinstellung, die vom ACX-Framework bereitgestellt wird. Ein Treiber kann das gegenteilige Verhalten anfordern, indem acxStreamBridgeInvertChangeStateSequence (in ACX_STREAM_BRIDGE_CONFIG_FLAGS definiert) festgelegt wird, wenn die ACXSTREAMBRIDGE erstellt wird, die der Treiber zur Streaming-Verbindung hinzufügt.
Streamen von Audiodaten
Sobald der Stream erstellt und die entsprechenden Puffer zugewiesen wurden, befindet sich der Stream im Pause-Zustand und wartet auf den Start des Streams. Wenn der Client den Stream in den Play-Status versetzt, ruft das ACX-Framework alle mit dem Stream verknüpften ACXSTREAM-Objekte auf, um anzugeben, dass sich der Stream-Status im Play-Status befindet. Der ACXPIN wird dann in den Play-Status versetzt, woraufhin der Datenfluss beginnt.
Rendern von Audiodaten
Sobald der Stream erstellt und die Ressourcen zugewiesen sind, ruft die Anwendung „Start“ für den Stream auf, um die Wiedergabe zu starten. Beachten Sie, dass eine Anwendung vor dem Starten des Streams GetBuffer/ReleaseBuffer aufrufen sollte, um sicherzustellen, dass das erste Paket, das sofort mit der Wiedergabe beginnt, gültige Audiodaten enthält.
Der Client beginnt mit dem Vorabrollen eines Puffers. Wenn der Client ReleaseBuffer aufruft, wird dies in einen Aufruf in AudioKSE übersetzt, der die ACX-Ebene aufruft, die dann EvtAcxStreamSetRenderPacket im aktiven ACXSTREAM aufruft. Die Eigenschaft enthält den Paketindex (0-basiert) und gegebenenfalls ein EOS-Flag mit dem Byte-Offset des Endes des Streams im aktuellen Paket. Nachdem die Streaming-Verbindung mit einem Paket abgeschlossen ist, löst sie die Benachrichtigung über den vollständigen Puffer aus, die Clients freigibt, die darauf warten, das nächste Paket mit Render-Audiodaten zu füllen.
Der Timer-gesteuerte Streamingmodus wird unterstützt und wird durch die Verwendung eines PacketCount-Werts von 1 beim Aufrufen des EvtAcxStreamAllocateRtPackets-Rückrufs des Treibers angezeigt.
Erfassen von Audiodaten
Sobald der Stream erstellt und die Ressourcen zugewiesen sind, ruft die Anwendung „Start“ für den Stream auf, um die Wiedergabe zu starten.
Wenn der Stream ausgeführt wird, füllt die Quellverbindung das Erfassungspaket mit Audiodaten aus. Sobald das erste Paket gefüllt ist, gibt die Quellverbindung das Paket an das ACX-Framework frei. An diesem Punkt signalisiert das ACX-Framework das Streambenachrichtigungsereignis.
Nachdem die Streambenachrichtigung 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 Füllen verfügbar sind.
Bei der Burst-Erfassung kann die Quellverbindung 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 Stream abzurufen. Diese Struktur wird vom ACX-Framework aktualisiert, bevor das Signalisierungspaket abgeschlossen ist.
Älteres KS-Kernelstreamingverhalten
Es kann Situationen geben, wenn beispielsweise ein Treiber die Burst-Erfassung implementiert (wie in einer Schlüsselworterkennungsimplementierung), in denen anstelle des PacketVRegisters das ältere Kernel-Streaming-Paketverarbeitungsverhalten 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 erfolgt. 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 Stream-Position umfasst den PlayOffset und den WriteOffset.
Mit dem WaveRT-Streamingmodell kann der Audiotreiber ein HW-Positionsregister für den Client verfügbar machen. Das ACX-Streamingmodell unterstützt das Bereitstellen von HW-Registern nicht, da dadurch ein Ausgleich verhindert wird.
Jedes Mal, wenn die Streaming-Verbindung ein Paket abgeschlossen hat, ruft es AcxRtStreamNotifyPacketComplete mit dem 0-basierten Paketindex und dem QPC-Wert auf, der so nah am Paketabschluss wie möglich liegt (z. B. kann der QPC-Wert von der Interrupt Service Routine berechnet werden). Diese Informationen stehen Clients über KSPROPERTY_RTAUDIO_PACKETVREGISTER zur Verfügung, das 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 Stream erstellt wurde, übergibt ACX den Stream mithilfe der folgenden Rückrufe in verschiedene Zustände:
- EvtAcxStreamPrepareHardware übergibt den Stream vom AcxStreamStateStop-Zustand in den AcxStreamStatePause-Zustand. Der Treiber sollte erforderliche Hardware wie DMA-Module reservieren, wenn er EvtAcxStreamPrepareHardware empfängt.
- EvtAcxStreamRun übergibt den Stream vom AcxStreamStatePause-Zustand in den AcxStreamStateRun-Zustand.
- EvtAcxStreamPause übergibt den Stream vom AcxStreamStateRun-Zustand in den AcxStreamStatePause-Zustand.
- EvtAcxStreamReleaseHardware übergibt den Stream vom AcxStreamStatePause-Zustand in den AcxStreamStateStop-Zustand. Der Treiber sollte erforderliche Hardware wie DMA-Module 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 Stream wieder in den AcxStreamStatePause-Zustand übergeben.
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 Sortierung ist dabei nicht garantiert.
Der AcxStreamStateAcquire-Zustand wird nicht verwendet. Durch ACX ist es nicht mehr erforderlich, dass der Treiber den Erfassungsstatus hat, da dieser Status mit der 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 eine ACXPIN zu bestimmen, die die Offload-Streamerstellung und die verschiedenen Elemente übernimmt, die für die Offload-Streamlautstärke, die Stummschaltung und den Spitzenzähler-Status erforderlich sind. Dies ähnelt dem vorhandenen Audiomodulknoten in WaveRT-Treibern.
Stream-Abschlussprozess
Wenn der Client den Stream 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 eine endgültige Bereinigung für den ACXSTREAM auszuführen. WDF ruft EvtCleanupCallback auf, wenn das Framework versucht, das Objekt zu löschen. Verwenden Sie nicht EvtDestroyCallback, das erst aufgerufen wird, wenn alle Verweise auf das Objekt freigegeben wurden, was unbestimmt ist.
Der Treiber sollte die mit dem ACXSTREAM-Objekt verknüpften Systemspeicherressourcen in EvtCleanupCallback bereinigen, sofern die Ressourcen nicht bereits in EvtAcxStreamReleaseHardware bereinigt wurden.
Es ist wichtig, dass der Treiber die Ressourcen, die den Stream unterstützen, erst dann bereinigt, wenn der Client dies anfordert.
Der AcxStreamStateAcquire-Zustand wird nicht verwendet. Durch ACX ist es nicht mehr erforderlich, dass der Treiber den Erfassungsstatus hat, da dieser Status mit der Vorbereitungshardware (EvtAcxStreamPrepareHardware) und die Freigabehardware (EvtAcxStreamReleaseHardware) implizit ist.
Entfernen und Invalidieren von Stream-Überraschungen
Wenn der Treiber feststellt, dass der Stream ungültig geworden ist (z. B. wenn die Buchse nicht mehr angeschlossen ist), schaltet die Verbindung alle Streams ab.
Stream-Speicherbereinigung
Die Beseitigung der Ressourcen des Streams kann durch Bereinigung (nicht Zerstörung) des Stream-Kontexts des Treibers erfolgen. Löschen Sie niemals etwas, das im Destroy-Rückruf eines Objektkontexts freigegeben wird. Diese Anleitung gilt für alle ACX-Objekte.
Der Destroy-Rückruf wird aufgerufen, nachdem der letzte Verweis nicht mehr vorhanden ist, wenn er unbekannt ist.
Im Allgemeinen wird der Bereinigungsrückruf des Streams aufgerufen, wenn das Handle geschlossen wird. Eine Ausnahme davon ist, wenn der Treiber den Stream in seinem Rückruf erstellt hat. Wenn ACX diesen Stream nicht unmittelbar vor der Rückkehr vom Stream-Create-Vorgang zu seiner Stream-Bridge hinzufügen konnte, wird der Stream asynchron abgebrochen und der aktuelle Thread gibt einen Fehler an den Create-Stream-Client zurück. Zu diesem Zeitpunkt sollten dem Stream keine Speicherzuweisungen zugewiesen sein. Weitere Informationen finden Sie unter EVT_ACX_STREAM_RELEASE_HARDWARE-Rückruf.
Stream-Speicherbereinigungssequenz
Der Stream-Puffer ist eine Systemressource und sollte nur freigegeben werden, wenn der Benutzermodus-Client das Handle des Streams schließt. Der Puffer (der sich von den Hardwareressourcen des Geräts unterscheidet) hat dieselbe Lebensdauer wie das Handle des Streams. Wenn der Client das Handle schließt, ruft ACX den Rückruf zum Bereinigen der Stream-Objekte und dann den Löschrückruf des Stream-Objekts auf, wenn die Referenz für das Objekt zu Null wechselt.
Es ist möglich, dass ACX die Löschung eines STREAM-Objekts auf ein Arbeitselement verschiebt, wenn der Treiber ein Stream-Objekt erstellt hat und dann der Rückruf zum Erstellen eines Streams fehlgeschlagen ist. Um einen Deadlock bei einem heruntergefahrenen 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 zugewiesenen Stream-Ressourcen freigeben, bevor er einen Fehler von der Stream-Erstellung zurückgibt.
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 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 Rückruf aufruft.
Der EVT_ACX_STREAM_FREE_RTPACKETS-Rückruf sollte nach dem EVT_ACX_STREAM_RELEASE_HARDWARE-Rückruf aufgerufen werden und vor EvtDeviceReleaseHardware enden.
Dieser Rückruf kann auftreten, nachdem der Treiber den WDF-Release-Hardware-Rückruf verarbeitet hat, da der Benutzermodus-Client seine Handles lange behalten kann. Der Treiber sollte nicht warten, bis diese Handles entfernt werden, da dies nur zu einer 0x9f DRIVER_POWER_STATE_FAILURE-Fehlerprüfung führt. Siehe EVT_WDF_DEVICE_RELEASE_HARDWARE-Rückruffunktion für weitere Informationen.
Dieser EvtDeviceReleaseHardware-Code aus dem ACX-Beispieltreiber zeigt ein Beispiel für den Aufruf von AcxDeviceRemoveCircuit und anschließendes Freigeben des Streaming-Hardwarespeichers.
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 –> Freigeben der Hardwareressourcen des Geräts
AcxStreamFreeRtPackets –> Freigeben von Audiopuffer, der dem Handle zugeordnet ist
Weitere Informationen zum Verwalten von WDF- und Verbindungsobjekten 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 das 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 einen 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 freizugeben, die in einem vorherigen Aufruf von EvtAcxStreamAllocateRtPackets zugeordnet wurden. Die gleichen Pakete aus diesem Aufruf sind enthalten.
EvtAcxStreamGetHwLatency
Das EvtAcxStreamGetHwLatency-Ereignis weist den Treiber an, die Streamlatenz für die spezifische Verbindung dieses Streams bereitzustellen (die Gesamtlatenz ist eine Summe der Latenz der verschiedenen Verbindungen). Die FifoSize wird in Bytes und die Verzögerung in 100-Nanosekunden-Einheiten angegeben.
EvtAcxStreamSetRenderPacket
Das EvtAcxStreamSetRenderPacket-Ereignis teilt dem Treiber mit, welches Paket gerade vom Client freigegeben wurde. Wenn keine Störungen vorliegen, sollte dieses Paket (CurrentRenderPacket + 1) sein, wobei CurrentRenderPacket das Paket ist, aus dem der Treiber gerade streamt.
Flags können 0 oder KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM = 0x200
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. Weitere Informationen finden Sie unter OptionsFlags in KSSTREAM_HEADER-Struktur (ks.h).
Der Treiber sollte CurrentRenderPacket beim Rendern von Paketen weiter erhöhen, anstatt sein CurrentRenderPacket so zu ändern, dass es diesem Wert entspricht.
EvtAcxStreamGetCurrentPacket
Das EvtAcxStreamGetCurrentPacket weist den Treiber an, anzugeben, welches Paket (0-basiert) gerade an die Hardware gerendert oder gerade von der Erfassungshardware gefüllt wird.
EvtAcxStreamGetCapturePacket
Das EvtAcxStreamGetCapturePacket weist den Treiber an, anzugeben, welches Paket (0-basiert) zuletzt vollständig gefüllt wurde, einschließlich des QPC-Werts zu dem Zeitpunkt, als der Treiber mit dem Füllen des Pakets begann.
EvtAcxStreamGetPresentationPosition
Die EvtAcxStreamGetPresentationPosition teilt dem Treiber mit, die aktuelle Position zusammen mit dem QPC-Wert zum Zeitpunkt der Berechnung der aktuellen Position anzugeben.
STREAM-STATUSEREIGNISSE
Der Streamingstatus für einen ACXSTREAM wird von den folgenden APIs verwaltet.
EVT_ACX_STREAM_PREPARE_HARDWARE
EVT_ACX_STREAM_RELEASE_HARDWARE
Streaming-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 Stream zugeordneten Benachrichtigungsereignisse fest.