summaryrefslogtreecommitdiff
path: root/audio/Acx/Samples/Common/StreamEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/Acx/Samples/Common/StreamEngine.cpp')
-rw-r--r--audio/Acx/Samples/Common/StreamEngine.cpp1345
1 files changed, 1345 insertions, 0 deletions
diff --git a/audio/Acx/Samples/Common/StreamEngine.cpp b/audio/Acx/Samples/Common/StreamEngine.cpp
new file mode 100644
index 00000000..be90bac6
--- /dev/null
+++ b/audio/Acx/Samples/Common/StreamEngine.cpp
@@ -0,0 +1,1345 @@
+/*++
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ StreamEngine.cpp
+
+Abstract:
+
+ Virtual Streaming Engine - this module controls streaming logic for
+ the device.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "private.h"
+#include "public.h"
+#include <devguid.h>
+#include <ks.h>
+#include <mmsystem.h>
+#include <ksmedia.h>
+#include "streamengine.h"
+
+#ifndef __INTELLISENSE__
+#include "streamengine.tmh"
+#endif
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CStreamEngine::CStreamEngine(
+ _In_ ACXSTREAM Stream,
+ _In_ ACXDATAFORMAT StreamFormat,
+ _In_ BOOL Offload,
+ _In_opt_ CSimPeakMeter *CircuitPeakmeter
+)
+ : m_PacketsCount(0),
+ m_PacketSize(0),
+ m_FirstPacketOffset(0),
+ m_NotificationTimer(NULL),
+ m_CurrentState(AcxStreamStateStop),
+ m_CurrentPacket(0),
+ m_Position(0),
+ m_Stream(Stream),
+ m_StreamFormat(StreamFormat),
+ m_StartTime(0),
+ m_StartPosition(0),
+ m_GlitchAdjust(0),
+ m_ToneFrequency(DEFAULT_FREQUENCY),
+ m_Offload(Offload),
+ m_pCircuitPeakmeter(CircuitPeakmeter)
+{
+ PAGED_CODE();
+
+ KeQueryPerformanceCounter(&m_PerformanceCounterFrequency);
+ RtlZeroMemory(m_Packets, sizeof(m_Packets));
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CStreamEngine::~CStreamEngine()
+{
+ PAGED_CODE();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::AllocateRtPackets(
+ _In_ ULONG PacketCount,
+ _In_ ULONG PacketSize,
+ _Out_ PACX_RTPACKET* Packets
+)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PACX_RTPACKET packets = NULL;
+ PVOID packetBuffer = NULL;
+ ULONG i;
+ ULONG packetAllocSizeInPages = 0;
+ ULONG packetAllocSizeInBytes = 0;
+ ULONG firstPacketOffset = 0;
+ size_t packetsSize = 0;
+
+ PAGED_CODE();
+
+ if (PacketCount > MAX_PACKET_COUNT)
+ {
+ ASSERT(FALSE);
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ status = RtlSizeTMult(PacketCount, sizeof(ACX_RTPACKET), &packetsSize);
+ if (!NT_SUCCESS(status))
+ {
+ ASSERT(FALSE);
+ goto exit;
+ }
+
+ packets = (PACX_RTPACKET)ExAllocatePool2(POOL_FLAG_NON_PAGED, packetsSize, DeviceDriverTag);
+ if (!packets)
+ {
+ status = STATUS_NO_MEMORY;
+ ASSERT(FALSE);
+ goto exit;
+ }
+
+ //
+ // We need to allocate page-aligned buffers, to ensure no kernel memory leaks
+ // to user space. Round up the packet size to page aligned, then calculate
+ // the first packet's buffer offset so packet 0 ends on a page boundary and
+ // packet 1 begins on a page boundary.
+ //
+ status = RtlULongAdd(PacketSize, PAGE_SIZE - 1, &packetAllocSizeInPages);
+ if (!NT_SUCCESS(status))
+ {
+ ASSERT(FALSE);
+ goto exit;
+ }
+ packetAllocSizeInPages = packetAllocSizeInPages / PAGE_SIZE;
+ packetAllocSizeInBytes = PAGE_SIZE * packetAllocSizeInPages;
+ firstPacketOffset = packetAllocSizeInBytes - PacketSize;
+
+ for (i = 0; i < PacketCount; ++i)
+ {
+ PMDL pMdl = NULL;
+
+ ACX_RTPACKET_INIT(&packets[i]);
+
+ packetBuffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, packetAllocSizeInBytes, DeviceDriverTag);
+ if (packetBuffer == NULL)
+ {
+ status = STATUS_NO_MEMORY;
+ goto exit;
+ }
+
+ pMdl = IoAllocateMdl(packetBuffer, packetAllocSizeInBytes, FALSE, TRUE, NULL);
+ if (pMdl == NULL)
+ {
+ status = STATUS_NO_MEMORY;
+ goto exit;
+ }
+
+ MmBuildMdlForNonPagedPool(pMdl);
+
+ WDF_MEMORY_DESCRIPTOR_INIT_MDL(&((packets)[i].RtPacketBuffer), pMdl, packetAllocSizeInBytes);
+
+ packets[i].RtPacketSize = PacketSize;
+ if (i == 0)
+ {
+ packets[i].RtPacketOffset = firstPacketOffset;
+ }
+ else
+ {
+ packets[i].RtPacketOffset = 0;
+ }
+ m_Packets[i] = packetBuffer;
+
+ packetBuffer = NULL;
+ }
+
+ *Packets = packets;
+ packets = NULL;
+ m_PacketsCount = PacketCount;
+ m_PacketSize = PacketSize;
+ m_FirstPacketOffset = firstPacketOffset;
+
+exit:
+ if (packetBuffer)
+ {
+ ExFreePoolWithTag(packetBuffer, DeviceDriverTag);
+ }
+ if (packets)
+ {
+ FreeRtPackets(packets, PacketCount);
+ }
+ return status;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+VOID
+CStreamEngine::FreeRtPackets(
+ _Frees_ptr_ PACX_RTPACKET Packets,
+ _In_ ULONG PacketCount
+)
+{
+ ULONG i;
+ PVOID buffer;
+
+ PAGED_CODE();
+
+ for (i = 0; i < PacketCount; ++i)
+ {
+ if (Packets[i].RtPacketBuffer.u.MdlType.Mdl)
+ {
+ buffer = MmGetMdlVirtualAddress(Packets[i].RtPacketBuffer.u.MdlType.Mdl);
+ IoFreeMdl(Packets[i].RtPacketBuffer.u.MdlType.Mdl);
+ ExFreePool(buffer);
+ }
+ }
+
+ ExFreePool(Packets);
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::PrepareHardware()
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ WDF_TIMER_CONFIG timerConfig;
+ WDF_OBJECT_ATTRIBUTES timerAttributes;
+ PSTREAM_TIMER_CONTEXT timerCtx;
+
+ PAGED_CODE();
+
+ //
+ // If already in this state, do nothing.
+ //
+ if (m_CurrentState == AcxStreamStatePause)
+ {
+ // Nothing to do.
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (m_CurrentState != AcxStreamStateStop)
+ {
+ // Error out.
+ status = STATUS_INVALID_STATE_TRANSITION;
+ goto exit;
+ }
+
+ //
+ // Stop to Pause.
+ //
+ WDF_TIMER_CONFIG_INIT(&timerConfig, CStreamEngine::s_EvtStreamPassCallback);
+ timerConfig.AutomaticSerialization = TRUE;
+ timerConfig.UseHighResolutionTimer = WdfTrue;
+ timerConfig.Period = 0;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&timerAttributes, STREAM_TIMER_CONTEXT);
+ timerAttributes.ParentObject = m_Stream;
+
+ status = WdfTimerCreate(&timerConfig, &timerAttributes, &m_NotificationTimer);
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ timerCtx = GetStreamTimerContext(m_NotificationTimer);
+ timerCtx->StreamEngine = this;
+
+ m_CurrentState = AcxStreamStatePause;
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::ReleaseHardware()
+{
+ PAGED_CODE();
+
+ //
+ // If already in this state, do nothing.
+ //
+ if (m_CurrentState == AcxStreamStateStop)
+ {
+ // Nothing to do.
+ goto exit;
+ }
+
+ //
+ // Just assert we are in the correct state.
+ // On the way down we always want to succeed.
+ //
+ ASSERT(m_CurrentState == AcxStreamStatePause);
+
+ //
+ // Pause to Stop.
+ //
+ if (m_NotificationTimer)
+ {
+ WdfTimerStop(m_NotificationTimer, TRUE);
+ WdfObjectDelete(m_NotificationTimer);
+ m_NotificationTimer = NULL;
+ }
+
+ KeFlushQueuedDpcs();
+
+ m_Position = 0;
+ m_GlitchAdjust = 0;
+ m_CurrentPacket = 0;
+
+ m_CurrentState = AcxStreamStateStop;
+
+exit:
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::Pause()
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ PAGED_CODE();
+
+ if (m_CurrentState == AcxStreamStatePause)
+ {
+ // Nothing to do.
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (m_CurrentState != AcxStreamStateRun)
+ {
+ // Error out.
+ status = STATUS_INVALID_STATE_TRANSITION;
+ goto exit;
+ }
+
+ m_PeakMeter.StopStream();
+ if (m_pCircuitPeakmeter)
+ {
+ m_pCircuitPeakmeter->StopStream();
+ }
+
+ //
+ // Run to Pause.
+ //
+ WdfTimerStop(m_NotificationTimer, TRUE);
+
+ // Save the position we paused at.
+ UpdatePosition();
+
+ m_CurrentState = AcxStreamStatePause;
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::Run()
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ PAGED_CODE();
+
+ if (m_CurrentState == AcxStreamStateRun)
+ {
+ // Nothing to do.
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (m_CurrentState != AcxStreamStatePause)
+ {
+ status = STATUS_INVALID_STATE_TRANSITION;
+ goto exit;
+ }
+
+ m_PeakMeter.StartStream();
+ if (m_pCircuitPeakmeter)
+ {
+ m_pCircuitPeakmeter->StartStream();
+ }
+
+ //
+ // Pause to Run.
+ //
+ // Save the time and position - if we ran and paused previously, the StartTime and StartPosition will allow
+ // us to continue scheduling packet completions correctly, while still reporting absolute position from the
+ // start of the stream.
+ //
+ m_StartTime = KSCONVERT_PERFORMANCE_TIME(m_PerformanceCounterFrequency.QuadPart, KeQueryPerformanceCounter(NULL));
+ m_StartPosition = m_Position;
+
+ // Reset time we've lost to glitches
+ m_GlitchAdjust = 0;
+
+ ScheduleNextPass();
+
+ m_CurrentState = AcxStreamStateRun;
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::GetPresentationPosition(
+ _Out_ PULONGLONG PositionInBlocks,
+ _Out_ PULONGLONG QPCPosition
+)
+{
+ ULONG blockAlign;
+ LARGE_INTEGER qpc;
+
+ PAGED_CODE();
+
+ blockAlign = AcxDataFormatGetBlockAlign(m_StreamFormat);
+
+ // Update the position based on the current time
+ UpdatePosition();
+ qpc = KeQueryPerformanceCounter(NULL);
+
+ *PositionInBlocks = m_Position / blockAlign;
+ *QPCPosition = (ULONGLONG)qpc.QuadPart;
+
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::GetLinearBufferPosition(
+ _Out_ PULONGLONG Position
+)
+{
+ UNREFERENCED_PARAMETER(Position);
+ PAGED_CODE();
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::SetCurrentWritePosition(
+ _In_ ULONG Position
+)
+{
+ UNREFERENCED_PARAMETER(Position);
+ PAGED_CODE();
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::SetLastBufferPosition(
+ _In_ ULONG Position
+)
+{
+ UNREFERENCED_PARAMETER(Position);
+ PAGED_CODE();
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::AssignDrmContentId(
+ ULONG DrmContentId,
+ PACXDRMRIGHTS DrmRights
+)
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(DrmContentId);
+ UNREFERENCED_PARAMETER(DrmRights);
+
+ //
+ // At this point the driver should enforce the new DrmRights.
+ //
+ // HDMI render: if DigitalOutputDisable or CopyProtect is true, enable HDCP.
+ //
+ // From MSDN:
+ //
+ // This sample doesn't forward protected content, but if your driver uses
+ // lower layer drivers or a different stack to properly work, please see the
+ // following info from MSDN:
+ //
+ // "Before allowing protected content to flow through a data path, the system
+ // verifies that the data path is secure. To do so, the system authenticates
+ // each module in the data path beginning at the upstream end of the data path
+ // and moving downstream. As each module is authenticated, that module gives
+ // the system information about the next module in the data path so that it
+ // can also be authenticated. To be successfully authenticated, a module's
+ // binary file must be signed as DRM-compliant.
+ //
+ // Two adjacent modules in the data path can communicate with each other in
+ // one of several ways. If the upstream module calls the downstream module
+ // through IoCallDriver, the downstream module is part of a WDM driver. In
+ // this case, the upstream module calls the AcxDrmForwardContentToDeviceObject
+ // function to provide the system with the device object representing the
+ // downstream module. (If the two modules communicate through the downstream
+ // module's content handlers, the upstream module calls AcxDrmAddContentHandlers
+ // instead.)
+ //
+ // For more information, see MSDN's DRM Functions and Interfaces.
+ //
+
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::GetHWLatency(
+ _Out_ ULONG* FifoSize,
+ _Out_ ULONG* Delay
+)
+{
+ PAGED_CODE();
+
+ *FifoSize = 128;
+ *Delay = 0;
+
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CSimPeakMeter *
+CStreamEngine::GetPeakMeter()
+{
+ PAGED_CODE();
+
+ return &m_PeakMeter;
+}
+
+_Use_decl_annotations_
+VOID
+CStreamEngine::s_EvtStreamPassCallback(
+ _In_ WDFTIMER Timer
+)
+{
+ CStreamEngine* This;
+ PSTREAM_TIMER_CONTEXT timerCtx;
+
+ // Get our stream engine pointer from the timer context
+ timerCtx = GetStreamTimerContext(Timer);
+ This = timerCtx->StreamEngine;
+
+ // Call the StreamPassCallback for the engine
+ This->StreamPassCallback();
+}
+
+// This is run every time the stream timer fires
+_Use_decl_annotations_
+VOID
+CStreamEngine::StreamPassCallback()
+{
+ ULONGLONG completedPacket;
+ ULONGLONG qpcCompleted;
+
+ // Process the packet (e.g. save render to file/generate capture data)
+ ProcessPacket();
+
+ // We've completed a packet! Increment our currently active packet
+ completedPacket = (ULONG)InterlockedIncrement((LONG*)&m_CurrentPacket) - 1;
+ // Save the time at which we moved to the next packet
+ qpcCompleted = (ULONGLONG)KeQueryPerformanceCounter(NULL).QuadPart;
+
+ InterlockedExchange64(&m_LastPacketStart.QuadPart, m_CurrentPacketStart.QuadPart);
+ InterlockedExchange64(&m_CurrentPacketStart.QuadPart, qpcCompleted);
+
+ // Tell ACX we've completed the packet.
+ (void)AcxRtStreamNotifyPacketComplete(m_Stream, completedPacket, qpcCompleted);
+
+ // Schedule when our new current packet will finish
+ ScheduleNextPass();
+}
+
+_Use_decl_annotations_
+VOID
+CStreamEngine::ScheduleNextPass()
+{
+ LONGLONG delay = 0;
+ ULONG bytesPerSecond;
+ ULONGLONG nextPacket = 0;
+ ULONGLONG nextPacketStartPosition = 0;
+ ULONGLONG nextPacketPositionFromLastPause = 0;
+ ULONGLONG nextPacketTimeFromLastPauseHns = 0;
+ ULONGLONG nextPacketTime = 0;
+ ULONGLONG currentTime;
+ BOOLEAN inTimerQueue = FALSE;
+
+ // Get the number of bytes per second from our stored stream format
+ bytesPerSecond = GetBytesPerSecond();
+
+ // Calculate the absolute position of the beginning of the next packet from the beginning of the stream
+ nextPacket = m_CurrentPacket + 1;
+ nextPacketStartPosition = nextPacket * m_PacketSize;
+
+ // Adjust next packet position to account for the last time we resumed from Pause
+ nextPacketPositionFromLastPause = nextPacketStartPosition - m_StartPosition;
+
+ // Convert from bytes to HNS (to prevent truncation, multiply first then divide)
+ nextPacketTimeFromLastPauseHns = nextPacketPositionFromLastPause * HNS_PER_SEC / bytesPerSecond;
+
+ // Next packet time is Time @ resume from Pause, offset for lost time due to glitch, with next packet time added
+ nextPacketTime = m_StartTime + m_GlitchAdjust + nextPacketTimeFromLastPauseHns;
+
+ currentTime = KSCONVERT_PERFORMANCE_TIME(m_PerformanceCounterFrequency.QuadPart, KeQueryPerformanceCounter(NULL));
+
+ // Determine how long we want to wait, in HNS. Negative since it's a relative wait
+ delay = -(LONGLONG)(nextPacketTime - currentTime);
+
+ // If the delay isn't negative, this means we lost some time (e.g. broken into kernel debugger). Update
+ // our glitch adjust to account for that lost time, and attempt to schedule again
+ if (delay >= 0)
+ {
+ // Glitch!!!
+ // Update the glitch adjustment and set the new delay.
+ m_GlitchAdjust += delay;
+
+ StreamPassCallback();
+
+ return;
+ }
+
+ // Start the timer for our next pass! Note the timer isn't periodic.
+ inTimerQueue = WdfTimerStart(m_NotificationTimer, delay);
+
+ // We shouldn't be scheduling our next pass if the timer was previously still pending
+ ASSERT(inTimerQueue == FALSE);
+}
+
+_Use_decl_annotations_
+VOID
+CStreamEngine::UpdatePosition()
+{
+ ULONGLONG currentTime;
+ ULONG bytesPerSecond;
+
+ if (m_CurrentState != AcxStreamStateRun)
+ {
+ return;
+ }
+ bytesPerSecond = GetBytesPerSecond();
+ currentTime = KSCONVERT_PERFORMANCE_TIME(m_PerformanceCounterFrequency.QuadPart, KeQueryPerformanceCounter(NULL));
+
+ // Update position
+ m_Position = m_StartPosition - m_GlitchAdjust + (currentTime - m_StartTime) * bytesPerSecond / HNS_PER_SEC;
+}
+
+_Use_decl_annotations_
+ULONG
+CStreamEngine::GetBytesPerSecond()
+{
+ ULONG bytesPerSecond;
+
+ bytesPerSecond = AcxDataFormatGetAverageBytesPerSec(m_StreamFormat);
+
+ return bytesPerSecond;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CStreamEngine::GetCurrentPacket(
+ _Out_ PULONG CurrentPacket
+)
+{
+ ULONG currentPacket;
+ PAGED_CODE();
+
+ currentPacket = (ULONG)InterlockedCompareExchange((LONG*)&m_CurrentPacket, -1, -1);
+
+ *CurrentPacket = currentPacket;
+
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CRenderStreamEngine::CRenderStreamEngine(
+ _In_ ACXSTREAM Stream,
+ _In_ ACXDATAFORMAT StreamFormat,
+ _In_ BOOL Offload,
+ _In_ CSimPeakMeter * CircuitPeakmeter
+
+)
+ : CStreamEngine(Stream, StreamFormat, Offload, CircuitPeakmeter)
+{
+ PAGED_CODE();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CRenderStreamEngine::~CRenderStreamEngine()
+{
+ PAGED_CODE();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CRenderStreamEngine::PrepareHardware()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ status = CStreamEngine::PrepareHardware();
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ status = m_SaveData.SetDataFormat((PKSDATAFORMAT)AcxDataFormatGetKsDataFormat(m_StreamFormat));
+ if (!NT_SUCCESS(status))
+ {
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ status = m_SaveData.Initialize(m_Offload);
+ if (!NT_SUCCESS(status))
+ {
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ status = m_SaveData.SetMaxWriteSize(m_PacketSize * m_PacketsCount * 16);
+ if (!NT_SUCCESS(status))
+ {
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+exit:
+ return status;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CRenderStreamEngine::ReleaseHardware()
+{
+ PAGED_CODE();
+
+ m_SaveData.WaitAllWorkItems();
+ m_SaveData.Cleanup();
+
+ return CStreamEngine::ReleaseHardware();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CRenderStreamEngine::AssignDrmContentId(
+ ULONG DrmContentId,
+ PACXDRMRIGHTS DrmRights
+ )
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(DrmContentId);
+
+ //
+ // At this point the driver should enforce the new DrmRights.
+ // The sample driver handles DrmRights per stream basis, and
+ // stops writing the stream to disk, if CopyProtect = TRUE.
+ //
+ // HDMI render: if DigitalOutputDisable or CopyProtect is true, enable HDCP.
+ // Loopback: if CopyProtect is true, disable loopback stream.
+ //
+
+ //
+ // Sample writes each stream seperately to disk. If the rights for this
+ // stream indicates that the stream is CopyProtected, stop writing to disk.
+ //
+ m_SaveData.Disable(DrmRights->CopyProtect);
+
+ //
+ // From MSDN:
+ //
+ // This sample doesn't forward protected content, but if your driver uses
+ // lower layer drivers or a different stack to properly work, please see the
+ // following info from MSDN:
+ //
+ // "Before allowing protected content to flow through a data path, the system
+ // verifies that the data path is secure. To do so, the system authenticates
+ // each module in the data path beginning at the upstream end of the data path
+ // and moving downstream. As each module is authenticated, that module gives
+ // the system information about the next module in the data path so that it
+ // can also be authenticated. To be successfully authenticated, a module's
+ // binary file must be signed as DRM-compliant.
+ //
+ // Two adjacent modules in the data path can communicate with each other in
+ // one of several ways. If the upstream module calls the downstream module
+ // through IoCallDriver, the downstream module is part of a WDM driver. In
+ // this case, the upstream module calls the AcxDrmForwardContentToDeviceObject
+ // function to provide the system with the device object representing the
+ // downstream module. (If the two modules communicate through the downstream
+ // module's content handlers, the upstream module calls AcxDrmAddContentHandlers
+ // instead.)
+ //
+ // For more information, see MSDN's DRM Functions and Interfaces.
+ //
+
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CRenderStreamEngine::SetRenderPacket(
+ _In_ ULONG Packet,
+ _In_ ULONG Flags,
+ _In_ ULONG EosPacketLength
+)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG currentPacket;
+
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(EosPacketLength);
+
+ PAGED_CODE();
+
+ currentPacket = (ULONG)InterlockedCompareExchange((LONG*)&m_CurrentPacket, -1, -1);
+
+ if (Packet <= currentPacket)
+ {
+ //ASSERT(FALSE);
+ status = STATUS_DATA_LATE_ERROR;
+ }
+ else if (Packet > currentPacket + 1)
+ {
+ //ASSERT(FALSE);
+ status = STATUS_DATA_OVERRUN;
+ }
+
+ return status;
+}
+
+_Use_decl_annotations_
+VOID
+CRenderStreamEngine::ProcessPacket()
+{
+ ULONG currentPacket;
+ ULONG packetIndex;
+ PBYTE packetBuffer;
+
+ currentPacket = (ULONG)InterlockedCompareExchange((LONG*)&m_CurrentPacket, -1, -1);
+
+ packetIndex = currentPacket % m_PacketsCount;
+ packetBuffer = (PBYTE)m_Packets[packetIndex];
+ // Packet 0 starts at an offset if the size isn't a multiple of page_size
+ if (packetIndex == 0)
+ {
+ packetBuffer += m_FirstPacketOffset;
+ }
+
+ m_SaveData.WriteData(packetBuffer, m_PacketSize);
+}
+
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CCaptureStreamEngine::CCaptureStreamEngine(
+ _In_ ACXSTREAM Stream,
+ _In_ ACXDATAFORMAT StreamFormat
+)
+ : CStreamEngine(Stream, StreamFormat, FALSE, NULL),
+ m_EnableWaveCapture(0)
+{
+ PAGED_CODE();
+
+ m_CurrentPacketStart.QuadPart = 0;
+ m_LastPacketStart.QuadPart = 0;
+
+ RtlInitUnicodeString(&m_HostCaptureFileName, NULL);
+ RtlInitUnicodeString(&m_LoopbackCaptureFileName, NULL);
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CCaptureStreamEngine::~CCaptureStreamEngine()
+{
+ PAGED_CODE();
+
+ RtlFreeUnicodeString(&m_HostCaptureFileName);
+ RtlFreeUnicodeString(&m_LoopbackCaptureFileName);
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CCaptureStreamEngine::PrepareHardware()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PWAVEFORMATEXTENSIBLE pwfext = NULL;
+
+ PAGED_CODE();
+
+ status = CStreamEngine::PrepareHardware();
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ (void)ReadRegistrySettings();
+
+ pwfext = (PWAVEFORMATEXTENSIBLE)AcxDataFormatGetWaveFormatExtensible(m_StreamFormat);
+ if (pwfext == NULL)
+ {
+ // Cannot initialize reader or generator with a format that's not understood
+ status = STATUS_NO_MATCH;
+ ASSERT(FALSE);
+ goto exit;
+ }
+
+ if (m_EnableWaveCapture)
+ {
+ status = m_WaveReader.Init(pwfext, &m_HostCaptureFileName);
+ if (!NT_SUCCESS(status))
+ {
+ m_EnableWaveCapture = FALSE;
+ }
+ }
+
+ if (!m_EnableWaveCapture)
+ {
+ status = m_ToneGenerator.Init(m_ToneFrequency, pwfext);
+ }
+
+exit:
+ return status;
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CCaptureStreamEngine::ReleaseHardware()
+{
+ PAGED_CODE();
+
+ if (m_EnableWaveCapture)
+ {
+ m_WaveReader.WaitAllWorkItems();
+ }
+
+ return CStreamEngine::ReleaseHardware();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CCaptureStreamEngine::GetCapturePacket(
+ _Out_ ULONG* LastCapturePacket,
+ _Out_ ULONGLONG* QPCPacketStart,
+ _Out_ BOOLEAN* MoreData
+)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG currentPacket;
+ LONGLONG qpcPacketStart;
+
+ PAGED_CODE();
+
+ currentPacket = (ULONG)InterlockedCompareExchange((LONG*)&m_CurrentPacket, -1, -1);
+ qpcPacketStart = InterlockedCompareExchange64(&m_LastPacketStart.QuadPart, -1, -1);
+
+ *LastCapturePacket = currentPacket - 1;
+ *QPCPacketStart = (ULONGLONG)qpcPacketStart;
+ *MoreData = FALSE;
+
+ return status;
+}
+
+_Use_decl_annotations_
+VOID
+CCaptureStreamEngine::ProcessPacket()
+{
+ ULONG currentPacket;
+ ULONG packetIndex;
+ PBYTE packetBuffer;
+
+ currentPacket = (ULONG)InterlockedCompareExchange((LONG*)&m_CurrentPacket, -1, -1);
+
+ packetIndex = currentPacket % m_PacketsCount;
+ packetBuffer = (PBYTE)m_Packets[packetIndex];
+
+ // Packet 0 starts at an offset if the size isn't a multiple of page_size
+ if (packetIndex == 0)
+ {
+ packetBuffer += m_FirstPacketOffset;
+ }
+
+ if (m_EnableWaveCapture)
+ {
+ m_WaveReader.ReadWaveData(packetBuffer, m_PacketSize);
+ }
+ else
+ {
+ m_ToneGenerator.GenerateSine(packetBuffer, m_PacketSize);
+ }
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CCaptureStreamEngine::ReadRegistrySettings()
+{
+ NTSTATUS status;
+ PDRIVER_OBJECT DriverObject;
+ HANDLE DriverKey;
+
+ RTL_QUERY_REGISTRY_TABLE paramTable[] = {
+ // QueryRoutine Flags Name EntryContext DefaultType DefaultData DefaultLength
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"EnableWaveCapture", &m_EnableWaveCapture, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_EnableWaveCapture, sizeof(DWORD) },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureFileName", &m_HostCaptureFileName, (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_SZ, &m_HostCaptureFileName, sizeof(UNICODE_STRING) },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"LoopbackCaptureFileName", &m_LoopbackCaptureFileName, (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_SZ, &m_LoopbackCaptureFileName, sizeof(UNICODE_STRING) },
+ { NULL, 0, NULL, NULL, 0, NULL, 0 }
+ };
+
+ PAGED_CODE();
+
+ DriverObject = WdfDriverWdmGetDriverObject(WdfGetDriver());
+ DriverKey = NULL;
+ status = IoOpenDriverRegistryKey(DriverObject,
+ DriverRegKeyParameters,
+ KEY_READ,
+ 0,
+ &DriverKey);
+
+ if (!NT_SUCCESS(status))
+ {
+ ASSERT(FALSE);
+ goto exit;
+ }
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PCWSTR) DriverKey,
+ &paramTable[0],
+ NULL,
+ NULL);
+
+ if (DriverKey)
+ {
+ ZwClose(DriverKey);
+ }
+
+exit:
+ if (!NT_SUCCESS(status))
+ {
+ m_EnableWaveCapture = FALSE;
+ }
+
+ return status;
+}
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CBufferedCaptureStreamEngine::CBufferedCaptureStreamEngine(
+ _In_ ACXSTREAM Stream,
+ _In_ ACXDATAFORMAT StreamFormat,
+ _In_ CKeywordDetector* KeywordDetector
+
+)
+ : CCaptureStreamEngine(Stream, StreamFormat),
+ m_KeywordDetector(KeywordDetector)
+{
+ PAGED_CODE();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+CBufferedCaptureStreamEngine::~CBufferedCaptureStreamEngine()
+{
+ PAGED_CODE();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CBufferedCaptureStreamEngine::Pause()
+{
+ PAGED_CODE();
+
+ m_KeywordDetector->Stop();
+ return CCaptureStreamEngine::Pause();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CBufferedCaptureStreamEngine::Run()
+{
+ PAGED_CODE();
+
+ m_KeywordDetector->Run();
+ return CCaptureStreamEngine::Run();
+}
+
+_Use_decl_annotations_
+PAGED_CODE_SEG
+NTSTATUS
+CBufferedCaptureStreamEngine::GetCapturePacket(
+ _Out_ ULONG* LastCapturePacket,
+ _Out_ ULONGLONG* QPCPacketStart,
+ _Out_ BOOLEAN* MoreData
+)
+{
+ PAGED_CODE();
+
+ // retrieve the packet from the fifo queue
+ return m_KeywordDetector->GetReadPacket(m_PacketsCount, m_PacketSize, m_Packets, LastCapturePacket, QPCPacketStart, MoreData);
+}
+
+_Use_decl_annotations_
+VOID
+CBufferedCaptureStreamEngine::ProcessPacket()
+{
+ LARGE_INTEGER qpc;
+ LARGE_INTEGER qpcFrequency;
+
+ qpc = KeQueryPerformanceCounter(&qpcFrequency);
+
+ // Add the next packet to the fifo queue
+ m_KeywordDetector->DpcRoutine(qpc.QuadPart, qpcFrequency.QuadPart);
+}
+
+//Streamengine callbacks
+
+VOID
+EvtStreamDestroy(
+ _In_ WDFOBJECT Object
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ ctx = GetStreamEngineContext((ACXSTREAM)Object);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+ ctx->StreamEngine = NULL;
+ delete streamEngine;
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamGetHwLatency(
+ _In_ ACXSTREAM Stream,
+ _Out_ ULONG* FifoSize,
+ _Out_ ULONG* Delay
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->GetHWLatency(FifoSize, Delay);
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamAllocateRtPackets(
+ _In_ ACXSTREAM Stream,
+ _In_ ULONG PacketCount,
+ _In_ ULONG PacketSize,
+ _Out_ PACX_RTPACKET* Packets
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->AllocateRtPackets(PacketCount, PacketSize, Packets);
+}
+
+PAGED_CODE_SEG
+VOID
+EvtStreamFreeRtPackets(
+ _In_ ACXSTREAM Stream,
+ _In_ PACX_RTPACKET Packets,
+ _In_ ULONG PacketCount
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->FreeRtPackets(Packets, PacketCount);
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamPrepareHardware(
+ _In_ ACXSTREAM Stream
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->PrepareHardware();
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamReleaseHardware(
+ _In_ ACXSTREAM Stream
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->ReleaseHardware();
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamRun(
+ _In_ ACXSTREAM Stream
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->Run();
+}
+
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamPause(
+ _In_ ACXSTREAM Stream
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->Pause();
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamAssignDrmContentId(
+ _In_ ACXSTREAM Stream,
+ _In_ ULONG DrmContentId,
+ _In_ PACXDRMRIGHTS DrmRights
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine * streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = (CStreamEngine*)ctx->StreamEngine;
+
+ return streamEngine->AssignDrmContentId(DrmContentId, DrmRights);
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamGetCurrentPacket(
+ _In_ ACXSTREAM Stream,
+ _Out_ PULONG CurrentPacket
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = static_cast<CStreamEngine*>(ctx->StreamEngine);
+
+ return streamEngine->GetCurrentPacket(CurrentPacket);
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtStreamGetPresentationPosition(
+ _In_ ACXSTREAM Stream,
+ _Out_ PULONGLONG PositionInBlocks,
+ _Out_ PULONGLONG QPCPosition
+)
+{
+ PSTREAMENGINE_CONTEXT ctx;
+ CStreamEngine* streamEngine = NULL;
+
+ PAGED_CODE();
+
+ ctx = GetStreamEngineContext(Stream);
+
+ streamEngine = static_cast<CStreamEngine*>(ctx->StreamEngine);
+
+ return streamEngine->GetPresentationPosition(PositionInBlocks, QPCPosition);
+}
+