mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 07:17:28 +00:00
[idd] audio: add the mediated PCM data plane
Expose cancel-safe direct-I/O queues for reading rendered frames and writing captured frames through the private broker endpoint. Separate render and capture storage, publish format and state generations, and preserve local loopback while no broker is bound.
This commit is contained in:
@@ -112,33 +112,140 @@ NTSTATUS InstallWaveSubdevice(
|
||||
}
|
||||
|
||||
CAudioAdapter::CAudioAdapter() :
|
||||
m_refCount(1)
|
||||
m_refCount(1),
|
||||
m_brokerActive(0),
|
||||
m_renderDiscontinuity(0),
|
||||
m_renderState{},
|
||||
m_captureState{}
|
||||
{
|
||||
KeInitializeSpinLock(&m_transportLock);
|
||||
KeInitializeSpinLock(&m_streamStateLock);
|
||||
m_renderState.state = KSSTATE_STOP;
|
||||
m_captureState.state = KSSTATE_STOP;
|
||||
}
|
||||
|
||||
CAudioAdapter::~CAudioAdapter()
|
||||
{
|
||||
m_ring.Destroy();
|
||||
m_captureRing.Destroy();
|
||||
m_renderRing.Destroy();
|
||||
}
|
||||
|
||||
NTSTATUS CAudioAdapter::Initialize()
|
||||
{
|
||||
NTSTATUS status = m_clock.Initialize();
|
||||
if (NT_SUCCESS(status))
|
||||
status = m_ring.Init();
|
||||
status = m_renderRing.Init();
|
||||
if (NT_SUCCESS(status))
|
||||
status = m_captureRing.Init();
|
||||
return status;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS CAudioAdapter::ConfigureRender(
|
||||
NTSTATUS CAudioAdapter::ConfigureStream(
|
||||
_In_ EndpointKind kind,
|
||||
_In_ const FormatFingerprint &format)
|
||||
{
|
||||
return m_ring.Configure(format);
|
||||
CAudioRingBuffer &ring = kind == EndpointKind::Render ?
|
||||
m_renderRing : m_captureRing;
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
const NTSTATUS status = ring.Configure(format);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return status;
|
||||
}
|
||||
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
|
||||
AudioStreamSnapshot &stream = kind == EndpointKind::Render ?
|
||||
m_renderState : m_captureState;
|
||||
stream.format = format;
|
||||
stream.formatValid = true;
|
||||
++stream.generation;
|
||||
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
|
||||
BrokerDeviceNotifyStreamChanged(this, kind);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::SetStreamState(
|
||||
_In_ EndpointKind kind,
|
||||
_In_ KSSTATE state)
|
||||
{
|
||||
bool changed;
|
||||
bool reset;
|
||||
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
|
||||
AudioStreamSnapshot &stream = kind == EndpointKind::Render ?
|
||||
m_renderState : m_captureState;
|
||||
changed = stream.state != state;
|
||||
reset = changed && state == KSSTATE_STOP;
|
||||
stream.state = state;
|
||||
if (reset)
|
||||
++stream.generation;
|
||||
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
|
||||
|
||||
if (reset)
|
||||
{
|
||||
if (kind == EndpointKind::Render)
|
||||
m_renderRing.Reset();
|
||||
else
|
||||
m_captureRing.Reset();
|
||||
}
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
if (changed)
|
||||
BrokerDeviceNotifyStreamChanged(this, kind);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::GetStreamSnapshot(
|
||||
_In_ EndpointKind kind,
|
||||
_Out_ AudioStreamSnapshot *snapshot)
|
||||
{
|
||||
if (!snapshot)
|
||||
return;
|
||||
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
|
||||
*snapshot = kind == EndpointKind::Render ?
|
||||
m_renderState : m_captureState;
|
||||
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::SetBrokerActive(_In_ bool active)
|
||||
{
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
const LONG value = active ? 1 : 0;
|
||||
if (InterlockedExchange(&m_brokerActive, value) == value)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
return;
|
||||
}
|
||||
|
||||
m_captureRing.Reset();
|
||||
m_renderRing.Reset();
|
||||
InterlockedExchange(&m_renderDiscontinuity, active ? 1 : 0);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::ResetCapture()
|
||||
{
|
||||
m_ring.Reset();
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
if (InterlockedCompareExchange(&m_brokerActive, 0, 0))
|
||||
m_captureRing.Reset();
|
||||
else
|
||||
m_renderRing.Reset();
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@@ -146,7 +253,14 @@ void CAudioAdapter::WriteRenderFrames(
|
||||
_In_reads_bytes_(bytes) const BYTE *data,
|
||||
_In_ ULONG bytes)
|
||||
{
|
||||
m_ring.Push(data, bytes);
|
||||
ULONG droppedBytes = 0;
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
m_renderRing.Push(data, bytes, &droppedBytes);
|
||||
if (droppedBytes)
|
||||
InterlockedExchange(&m_renderDiscontinuity, 1);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
BrokerDeviceNotifyRenderAvailable(this, bytes, droppedBytes);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@@ -155,7 +269,16 @@ void CAudioAdapter::ReadCaptureFrames(
|
||||
_Out_writes_bytes_(bytes) BYTE *data,
|
||||
_In_ ULONG bytes)
|
||||
{
|
||||
m_ring.Pop(format, data, bytes);
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
const bool brokerActive =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0;
|
||||
CAudioRingBuffer &ring = brokerActive ?
|
||||
m_captureRing : m_renderRing;
|
||||
const ULONG copied = ring.Pop(format, data, bytes);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
if (brokerActive && copied < bytes)
|
||||
BrokerDeviceRecordCaptureUnderrun(this, bytes - copied);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@@ -163,7 +286,107 @@ void CAudioAdapter::DiscardCaptureFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG bytes)
|
||||
{
|
||||
m_ring.Discard(format, bytes);
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
const bool brokerActive =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0;
|
||||
CAudioRingBuffer &ring = brokerActive ?
|
||||
m_captureRing : m_renderRing;
|
||||
const ULONG discarded = ring.Discard(format, bytes);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
if (brokerActive && bytes > discarded)
|
||||
BrokerDeviceRecordCaptureUnderrun(this, bytes - discarded);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS CAudioAdapter::ReadRenderFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG streamGeneration,
|
||||
_Out_writes_bytes_(capacity) BYTE *data,
|
||||
_In_ ULONG capacity,
|
||||
_Out_ PULONG bytesRead,
|
||||
_Out_ bool *discontinuity)
|
||||
{
|
||||
if (!data || !bytesRead || !discontinuity || !format.blockAlign)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
*bytesRead = 0;
|
||||
*discontinuity = false;
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
|
||||
KIRQL stateIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &stateIrql);
|
||||
const bool active =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0 &&
|
||||
m_renderState.state == KSSTATE_RUN &&
|
||||
m_renderState.formatValid &&
|
||||
m_renderState.generation == streamGeneration &&
|
||||
EqualFormatFingerprint(m_renderState.format, format);
|
||||
KeReleaseSpinLock(&m_streamStateLock, stateIrql);
|
||||
if (!active)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
ULONG bytes = m_renderRing.Available();
|
||||
if (bytes > capacity)
|
||||
bytes = capacity;
|
||||
bytes -= bytes % format.blockAlign;
|
||||
if (!bytes)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const ULONG copied = m_renderRing.Pop(format, data, bytes);
|
||||
if (copied)
|
||||
{
|
||||
*discontinuity = InterlockedExchange(
|
||||
&m_renderDiscontinuity, 0) != 0;
|
||||
}
|
||||
*bytesRead = copied;
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS CAudioAdapter::WriteCaptureFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG streamGeneration,
|
||||
_In_ bool discontinuity,
|
||||
_In_reads_bytes_(bytes) const BYTE *data,
|
||||
_In_ ULONG bytes,
|
||||
_Out_ PULONG droppedBytes)
|
||||
{
|
||||
if (!data || !droppedBytes)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
*droppedBytes = 0;
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
|
||||
KIRQL stateIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &stateIrql);
|
||||
const bool active =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0 &&
|
||||
m_captureState.state == KSSTATE_RUN &&
|
||||
m_captureState.formatValid &&
|
||||
m_captureState.generation == streamGeneration &&
|
||||
EqualFormatFingerprint(m_captureState.format, format);
|
||||
KeReleaseSpinLock(&m_streamStateLock, stateIrql);
|
||||
if (!active)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
if (discontinuity)
|
||||
m_captureRing.Reset();
|
||||
const ULONG written = m_captureRing.Push(data, bytes, droppedBytes);
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return written == bytes ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
|
||||
@@ -24,6 +24,15 @@
|
||||
#include "Common.h"
|
||||
#include "RingBuffer.h"
|
||||
|
||||
struct AudioStreamSnapshot
|
||||
{
|
||||
KSSTATE state;
|
||||
bool formatValid;
|
||||
UCHAR reserved[3];
|
||||
ULONGLONG generation;
|
||||
FormatFingerprint format;
|
||||
};
|
||||
|
||||
class CAudioAdapter final : public IUnknown
|
||||
{
|
||||
public:
|
||||
@@ -33,9 +42,23 @@ public:
|
||||
NTSTATUS Initialize();
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS ConfigureRender(
|
||||
NTSTATUS ConfigureStream(
|
||||
_In_ EndpointKind kind,
|
||||
_In_ const FormatFingerprint &format);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void SetStreamState(
|
||||
_In_ EndpointKind kind,
|
||||
_In_ KSSTATE state);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void GetStreamSnapshot(
|
||||
_In_ EndpointKind kind,
|
||||
_Out_ AudioStreamSnapshot *snapshot);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void SetBrokerActive(_In_ bool active);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void ResetCapture();
|
||||
|
||||
@@ -55,6 +78,24 @@ public:
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG bytes);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS ReadRenderFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG streamGeneration,
|
||||
_Out_writes_bytes_(capacity) BYTE *data,
|
||||
_In_ ULONG capacity,
|
||||
_Out_ PULONG bytesRead,
|
||||
_Out_ bool *discontinuity);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS WriteCaptureFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG streamGeneration,
|
||||
_In_ bool discontinuity,
|
||||
_In_reads_bytes_(bytes) const BYTE *data,
|
||||
_In_ ULONG bytes,
|
||||
_Out_ PULONG droppedBytes);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
AudioClockSnapshot GetClockSnapshot() const;
|
||||
|
||||
@@ -66,8 +107,15 @@ public:
|
||||
|
||||
private:
|
||||
volatile LONG m_refCount;
|
||||
volatile LONG m_brokerActive;
|
||||
volatile LONG m_renderDiscontinuity;
|
||||
KSPIN_LOCK m_transportLock;
|
||||
KSPIN_LOCK m_streamStateLock;
|
||||
AudioStreamSnapshot m_renderState;
|
||||
AudioStreamSnapshot m_captureState;
|
||||
CAudioClock m_clock;
|
||||
CAudioRingBuffer m_ring;
|
||||
CAudioRingBuffer m_renderRing;
|
||||
CAudioRingBuffer m_captureRing;
|
||||
};
|
||||
|
||||
NTSTATUS StartAudioDevice(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Formats.h"
|
||||
|
||||
class CAudioAdapter;
|
||||
|
||||
@@ -75,3 +76,19 @@ CAudioAdapter *BrokerDeviceAcquireAdapter(
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void BrokerDeviceReleaseAdapter();
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void BrokerDeviceNotifyStreamChanged(
|
||||
_In_ CAudioAdapter *adapter,
|
||||
_In_ EndpointKind kind);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void BrokerDeviceNotifyRenderAvailable(
|
||||
_In_ CAudioAdapter *adapter,
|
||||
_In_ ULONG bytes,
|
||||
_In_ ULONG droppedBytes);
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void BrokerDeviceRecordCaptureUnderrun(
|
||||
_In_ CAudioAdapter *adapter,
|
||||
_In_ ULONGLONG bytes);
|
||||
|
||||
@@ -114,8 +114,7 @@ NTSTATUS CMiniportWaveRTStream::Initialize(
|
||||
m_format = *format;
|
||||
m_fingerprint = fingerprint;
|
||||
|
||||
if (m_kind == EndpointKind::Render)
|
||||
status = m_adapter->ConfigureRender(m_fingerprint);
|
||||
status = m_adapter->ConfigureStream(m_kind, m_fingerprint);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -195,8 +194,7 @@ NTSTATUS CMiniportWaveRTStream::SetFormat(
|
||||
m_fingerprint = fingerprint;
|
||||
KeReleaseSpinLock(&m_positionLock, oldIrql);
|
||||
|
||||
if (m_kind == EndpointKind::Render)
|
||||
status = m_adapter->ConfigureRender(m_fingerprint);
|
||||
status = m_adapter->ConfigureStream(m_kind, m_fingerprint);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -244,6 +242,8 @@ NTSTATUS CMiniportWaveRTStream::SetState(_In_ KSSTATE state)
|
||||
if (resetCapture)
|
||||
m_adapter->ResetCapture();
|
||||
|
||||
m_adapter->SetStreamState(m_kind, KSSTATE_RUN);
|
||||
|
||||
KeAcquireSpinLock(&m_positionLock, &oldIrql);
|
||||
if (m_state == KSSTATE_RUN)
|
||||
ArmTimerLocked();
|
||||
@@ -280,6 +280,7 @@ NTSTATUS CMiniportWaveRTStream::SetState(_In_ KSSTATE state)
|
||||
KeReleaseSpinLock(&m_positionLock, oldIrql);
|
||||
|
||||
StopTimer();
|
||||
m_adapter->SetStreamState(m_kind, state);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -901,6 +902,7 @@ void CMiniportWaveRTStream::AdvanceEventPacketsLocked(
|
||||
{
|
||||
m_eosNotificationSent = true;
|
||||
m_state = KSSTATE_PAUSE;
|
||||
m_adapter->SetStreamState(m_kind, KSSTATE_PAUSE);
|
||||
m_pendingBytes = 0;
|
||||
m_frameRemainder = 0;
|
||||
break;
|
||||
@@ -944,6 +946,7 @@ void CMiniportWaveRTStream::TransferFramesLocked(_In_ ULONGLONG bytes)
|
||||
if (m_linearPosition >= m_eosLinearPosition)
|
||||
{
|
||||
m_state = KSSTATE_PAUSE;
|
||||
m_adapter->SetStreamState(m_kind, KSSTATE_PAUSE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -997,7 +1000,10 @@ void CMiniportWaveRTStream::TransferFramesLocked(_In_ ULONGLONG bytes)
|
||||
|
||||
if (m_kind == EndpointKind::Render && m_eosReceived &&
|
||||
m_linearPosition >= m_eosLinearPosition)
|
||||
{
|
||||
m_state = KSSTATE_PAUSE;
|
||||
m_adapter->SetStreamState(m_kind, KSSTATE_PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
void CMiniportWaveRTStream::SignalNotificationsLocked()
|
||||
|
||||
Reference in New Issue
Block a user