summaryrefslogtreecommitdiff
path: root/AVStream/avscamera/sys/Capture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'AVStream/avscamera/sys/Capture.cpp')
-rw-r--r--AVStream/avscamera/sys/Capture.cpp2086
1 files changed, 2086 insertions, 0 deletions
diff --git a/AVStream/avscamera/sys/Capture.cpp b/AVStream/avscamera/sys/Capture.cpp
new file mode 100644
index 00000000..764052b3
--- /dev/null
+++ b/AVStream/avscamera/sys/Capture.cpp
@@ -0,0 +1,2086 @@
+/**************************************************************************
+
+ A/V Stream Camera Sample
+
+ Copyright (c) 2001, Microsoft Corporation.
+
+ File:
+
+ capture.cpp
+
+ Abstract:
+
+ This is the implementation of the CCapturePin class.
+
+ CCapturePin wraps a PKSPIN object and does:
+ 1. Object construction and cleanup,
+ 2. State management,
+ 3. Accepts new frame buffers for filling,
+ 4. Completes frame buffers delivered by the simulation,
+ 5. Negotiates for and sets new formats / data ranges, and
+ 6. Manages allocator framing.
+
+ History:
+
+ created 3/8/2001
+
+**************************************************************************/
+
+#include "Common.h"
+#include <ksmedia.h>
+#include "ntintsafe.h"
+
+/**************************************************************************
+
+ PAGEABLE CODE
+
+**************************************************************************/
+
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg("PAGE")
+#endif // ALLOC_PRAGMA
+
+CCapturePin::
+CCapturePin (
+ _In_ PKSPIN Pin
+)
+ : m_Pin (Pin)
+ , m_PinState(PinStopped)
+ , m_Clock(nullptr)
+ , m_PendIo(FALSE)
+ , m_AcquiredResources(FALSE)
+ , m_VideoInfoHeader(nullptr)
+ , m_pBitmapInfoHeader(nullptr)
+ , m_PreviousStreamPointer(nullptr)
+ , m_PresentationTime(0)
+ , m_FrameNumber(0)
+ , m_DroppedFrames(0)
+ , m_DesiredFrames(2)
+/*++
+
+Routine Description:
+
+ Construct a new capture pin.
+
+Arguments:
+
+ Pin -
+ The AVStream pin object corresponding to the capture pin
+
+Return Value:
+
+ None
+
+--*/
+{
+ PAGED_CODE();
+
+ NT_ASSERT(Pin);
+
+ PKSDEVICE Device = KsPinGetDevice (Pin);
+
+ NT_ASSERT(Device);
+
+ //
+ // Set up our device pointer. This gives us access to "hardware I/O"
+ // during the capture routines.
+ //
+ m_Device = CCaptureDevice::Recast(Device);
+
+ NT_ASSERT( m_Device );
+
+ m_Sensor = m_Device->GetSensor( KsPinGetParentFilter(Pin ) );
+
+ NT_ASSERT( m_Sensor );
+}
+
+CCapturePin::
+~CCapturePin()
+{
+ PAGED_CODE();
+
+ SAFE_FREE( m_pBitmapInfoHeader );
+ SAFE_FREE( m_VideoInfoHeader );
+}
+
+/*************************************************/
+
+NTSTATUS
+CCapturePin::
+ReleaseAllFrames()
+
+/*++
+
+Routine Description:
+
+ Clean up any references we're holding on frames after we abruptly
+ stop the hardware.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin);
+ PKSSTREAM_POINTER NextClone = NULL;
+
+ //
+ // Walk through the clones, deleting them, and setting DataUsed to
+ // zero since we didn't use any data!
+ //
+ while (Clone)
+ {
+
+ NextClone = KsStreamPointerGetNextClone (Clone);
+
+ Clone->StreamHeader->DataUsed = 0;
+ KsStreamPointerDelete (Clone);
+
+ Clone = NextClone;
+
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+CCapturePin::
+Initialize()
+/*++
+
+Routine Description:
+
+ Post construction initialization:
+ 1. Add us to the Pin's bag,
+ 2. Capture information about the image/video format, and
+ 3. Update the allocator's framing to match.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE();
+
+ DBG_ENTER("(Pin=%d)", m_Pin->Id);
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if( !m_Sensor )
+ {
+ // Fail if we couldn't find the sensor.
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ //
+ // Add the item to the object bag if we we were successful.
+ // Whenever the pin closes, the bag is cleaned up and we will be
+ // freed.
+ //
+ Status = KsAddItemToObjectBag (
+ m_Pin->Bag,
+ this,
+ reinterpret_cast<PFNKSFREE> (Cleanup)
+ );
+
+ if( NT_SUCCESS(Status) )
+ {
+ m_Pin->Context = this;
+ }
+ }
+
+ //
+ // If we succeeded so far, stash the video info header away and change
+ // our allocator framing to reflect the fact that only now do we know
+ // the framing requirements based on the connection format.
+ //
+ if (NT_SUCCESS (Status))
+ {
+ Status = CaptureBitmapInfoHeader();
+ }
+
+ if (NT_SUCCESS(Status))
+ {
+ //
+ // Edit the framing descriptors to match our pin's requirements given the current format.
+ //
+ Status = UpdateAllocatorFraming();
+ }
+
+ DBG_LEAVE("(Pin=%d)=0x%08X", m_Pin->Id, Status);
+ return Status;
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CCapturePin::
+CaptureBitmapInfoHeader()
+/*++
+
+Routine Description:
+
+ Capture information about the image/video format for the pin.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ const GUID ImageInfoSpecifier ={ STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) };
+ const GUID VideoInfoSpecifier ={ STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) };
+
+ // Free any previous copy of these header.
+ SAFE_FREE(m_pBitmapInfoHeader);
+ SAFE_FREE(m_VideoInfoHeader);
+
+ m_pBitmapInfoHeader = reinterpret_cast <PKS_BITMAPINFOHEADER> (
+ ExAllocatePoolWithTag (
+ NonPagedPoolNx,
+ sizeof(KS_BITMAPINFOHEADER),
+ AVSHWS_POOLTAG
+ )
+ );
+ if( !m_pBitmapInfoHeader )
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if( IsEqualGUID( m_Pin->ConnectionFormat->Specifier, ImageInfoSpecifier ) &&
+ m_Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_IMAGEINFO) )
+ {
+
+ PKS_BITMAPINFOHEADER ConnectionHeader =
+ &((reinterpret_cast <PKS_DATAFORMAT_IMAGEINFO>
+ (m_Pin->ConnectionFormat))->ImageInfoHeader);
+
+ //
+ // Copy the connection format video info header into the newly
+ // allocated "captured" video info header.
+ //
+ *m_pBitmapInfoHeader = *ConnectionHeader;
+
+ m_VideoInfoHeader = reinterpret_cast <PKS_VIDEOINFOHEADER> (
+ ExAllocatePoolWithTag (
+ NonPagedPoolNx,
+ sizeof(KS_VIDEOINFOHEADER),
+ AVSHWS_POOLTAG
+ )
+ );
+ if( !m_VideoInfoHeader )
+ {
+ SAFE_FREE( m_pBitmapInfoHeader );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Copy the connection format video info header into the newly
+ // allocated "captured" video info header.
+ //
+ m_VideoInfoHeader->bmiHeader = *ConnectionHeader;
+
+ // If we don't have a known bit-depth (compressed formats), assume the worse-case.
+ if( m_pBitmapInfoHeader->biBitCount==0 )
+ {
+ m_pBitmapInfoHeader->biBitCount=32;
+ }
+
+ // Estimate the image size. The derived pin can chose to override this value.
+ m_VideoInfoHeader->bmiHeader.biSizeImage =
+ (m_pBitmapInfoHeader->biWidth*m_pBitmapInfoHeader->biHeight*m_pBitmapInfoHeader->biBitCount)/8;
+
+ // Estimate a frame rate. The derived pin can chose to override this value.
+ m_VideoInfoHeader->AvgTimePerFrame = ONESECOND/30;
+ }
+ else
+ if( IsEqualGUID( m_Pin->ConnectionFormat->Specifier, VideoInfoSpecifier ) &&
+ m_Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_VIDEOINFOHEADER) )
+ {
+ PKS_VIDEOINFOHEADER ConnectionHeader =
+ &((reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER>
+ (m_Pin->ConnectionFormat))->
+ VideoInfoHeader);
+
+ m_VideoInfoHeader = reinterpret_cast <PKS_VIDEOINFOHEADER> (
+ ExAllocatePoolWithTag (
+ NonPagedPoolNx,
+ KS_SIZE_VIDEOHEADER (ConnectionHeader),
+ AVSHWS_POOLTAG
+ )
+ );
+ if( !m_VideoInfoHeader )
+ {
+ SAFE_FREE( m_pBitmapInfoHeader );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Copy the connection format video info header into the newly
+ // allocated "captured" video info header.
+ //
+ RtlCopyMemory (
+ m_VideoInfoHeader,
+ ConnectionHeader,
+ KS_SIZE_VIDEOHEADER (ConnectionHeader)
+ );
+
+ //
+ // Copy the connection format video info header into the newly
+ // allocated "captured" video info header.
+ //
+ *m_pBitmapInfoHeader = m_VideoInfoHeader->bmiHeader;
+ }
+
+ DBG_TRACE( "+++ AvgTimePerFrame = %lld +++", m_VideoInfoHeader->AvgTimePerFrame );
+
+ return STATUS_SUCCESS;
+}
+
+/*************************************************/
+
+NTSTATUS
+CCapturePin::
+SetState (
+ _In_ KSSTATE ToState,
+ _In_ KSSTATE FromState
+)
+
+/*++
+
+Routine Description:
+
+ This is called when the caputre pin transitions state. The routine
+ attempts to acquire / release any hardware resources and start up
+ or shut down capture based on the states we are transitioning to
+ and away from.
+
+Arguments:
+
+ ToState -
+ The state we're transitioning to
+
+ FromState -
+ The state we're transitioning away from
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DBG_ENTER( "( Pin=%d, ToState=%s, FromState=%s )\n",
+ m_Pin->Id, KSStateToStateName(ToState), KSStateToStateName(FromState) ) ;
+
+ switch (ToState)
+ {
+
+ case KSSTATE_STOP:
+
+ //
+ // First, stop the hardware if we actually did anything to it.
+ //
+ if (m_PinState != PinStopped)
+ {
+ Status = m_Sensor->Stop(m_Pin);
+ NT_ASSERT (NT_SUCCESS (Status));
+
+ m_PinState = PinStopped;
+ }
+
+ //
+ // We've stopped the "fake hardware". It has cleared out
+ // it's scatter / gather tables and will no longer be
+ // completing clones. We had locks on some frames that were,
+ // however, in hardware. This will clean them up. An
+ // alternative location would be in the reset dispatch.
+ // Note, however, that the reset dispatch can occur in any
+ // state and this should be understood.
+ //
+ // Some hardware may fill all S/G mappings before stopping...
+ // in this case, you may not have to do this. The
+ // "fake hardware" here simply stops filling mappings and
+ // cleans its scatter / gather tables out on the Stop call.
+ //
+ Status = ReleaseAllFrames ();
+
+ //
+ // Release any hardware resources related to this pin.
+ //
+ if (m_AcquiredResources)
+ {
+ //
+ // If we got an interface to the clock, we must release it.
+ //
+ if (m_Clock)
+ {
+ m_Clock->Release ();
+ m_Clock = NULL;
+ }
+
+ m_Sensor->ReleaseHardwareResources (m_Pin);
+
+ m_AcquiredResources = FALSE;
+ }
+
+ break;
+
+ case KSSTATE_ACQUIRE:
+ //
+ // Acquire any hardware resources related to this pin. We should
+ // only acquire them here -- **NOT** at filter create time.
+ // This means we do not fail creation of a filter because of
+ // limited hardware resources.
+ //
+ // TODO: Move this to a derived CCapturePin class - one
+ // specifically for testing, not as a sample.
+ if(GetAcquireFailureKey())
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ if (FromState == KSSTATE_STOP)
+ {
+ Status =
+ m_Sensor->
+ AcquireHardwareResources (
+ m_Pin,
+ this,
+ m_VideoInfoHeader,
+ &m_HardwareSimulation
+ );
+
+ if (NT_SUCCESS (Status))
+ {
+ m_AcquiredResources = TRUE;
+
+ //
+ // Attempt to get an interface to the master clock.
+ // This will fail if one has not been assigned. Since
+ // one must be assigned while the pin is still in
+ // KSSTATE_STOP, this is a guranteed method of getting
+ // the clock should one be assigned.
+ //
+ if (!NT_SUCCESS (
+ KsPinGetReferenceClockInterface (
+ m_Pin,
+ &m_Clock
+ )
+ ))
+ {
+ //
+ // If we could not get an interface to the clock,
+ // don't use one.
+ //
+ m_Clock = NULL;
+ }
+ }
+ else
+ {
+ m_AcquiredResources = FALSE;
+ }
+
+ }
+
+ //
+ // Standard transport pins will always receive transitions in
+ // +/- 1 manner. This means we'll always see a PAUSE->ACQUIRE
+ // transition before stopping the pin.
+ //
+
+ m_FrameNumber = 0;
+ m_DroppedFrames = 0;
+ break;
+
+ case KSSTATE_PAUSE:
+ //
+ // Stop the hardware simulation if we're coming down from run.
+ //
+ if (FromState == KSSTATE_RUN)
+ {
+ m_PresentationTime = 0;
+ Status = m_Sensor->Pause (m_Pin, TRUE);
+
+ if (NT_SUCCESS (Status))
+ {
+ m_PinState = PinPaused;
+ }
+ }
+ m_FrameNumber = 0;
+ break;
+
+ case KSSTATE_RUN:
+ //
+ // Start the hardware simulation or unpause it depending on
+ // whether we're initially running or we've paused and restarted.
+ //
+ if (FromState == KSSTATE_PAUSE && m_PinState == PinPaused)
+ {
+ Status = m_Sensor->Pause (m_Pin, FALSE);
+ }
+ else
+ {
+ Status = m_Sensor->Start (m_Pin);
+ }
+
+ if (NT_SUCCESS (Status))
+ {
+ m_PinState = PinRunning;
+ }
+ break;
+
+ }
+
+ DBG_LEAVE( "( Pin=%d, ToState=%s, FromState=%s ) = 0x%08X\n",
+ m_Pin->Id, KSStateToStateName(ToState), KSStateToStateName(FromState), Status ) ;
+
+ return Status;
+
+}
+
+NTSTATUS
+CCapturePin::
+Process()
+
+/*++
+
+Routine Description:
+
+ The process dispatch for the pin bridges to this location.
+ We handle setting up scatter gather mappings, etc...
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+ PAGED_CODE( );
+
+ DBG_ENTER( "( Pin=%d )", m_Pin->Id );
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ PKSSTREAM_POINTER Leading;
+
+ Leading = KsPinGetLeadingEdgeStreamPointer( m_Pin, KSSTREAM_POINTER_STATE_LOCKED );
+
+ while( NT_SUCCESS( Status ) && Leading )
+ {
+
+ PKSSTREAM_POINTER ClonePointer;
+ PSTREAM_POINTER_CONTEXT SPContext = NULL;
+
+ //
+ // If no data is present in the Leading edge stream pointer, just
+ // move on to the next frame
+ //
+ if( NULL == Leading->StreamHeader->Data )
+ {
+ Status = KsStreamPointerAdvance( Leading );
+ continue;
+ }
+ //
+ // For optimization sake in this particular sample, I will only keep
+ // one clone stream pointer per frame. This complicates the logic
+ // here but simplifies the completions.
+ //
+ // I'm also choosing to do this since I need to keep track of the
+ // virtual addresses corresponding to each mapping since I'm faking
+ // DMA. It simplifies that too.
+ //
+ if( !m_PreviousStreamPointer )
+ {
+ //
+ // First thing we need to do is clone the leading edge. This allows
+ // us to keep reference on the frames while they're in DMA.
+ //
+ Status = KsStreamPointerClone(
+ Leading,
+ NULL,
+ sizeof (STREAM_POINTER_CONTEXT),
+ &ClonePointer
+ );
+
+ //
+ // I use this for easy chunking of the buffer. We're not really
+ // dealing with physical addresses. This keeps track of what
+ // virtual address in the buffer the current scatter / gather
+ // mapping corresponds to for the fake hardware.
+ //
+ if( NT_SUCCESS( Status ) )
+ {
+ //
+ // Set the stream header data used to 0. We update this
+ // in the DMA completions. For queues with DMA, we must
+ // update this field ourselves.
+ //
+ ClonePointer->StreamHeader->DataUsed = 0;
+
+ SPContext = reinterpret_cast <PSTREAM_POINTER_CONTEXT>
+ (ClonePointer->Context);
+
+ SPContext->BufferVirtual =
+ reinterpret_cast <PUCHAR> (
+ ClonePointer->StreamHeader->Data
+ );
+ }
+ }
+ else
+ {
+ ClonePointer = m_PreviousStreamPointer;
+ SPContext = reinterpret_cast <PSTREAM_POINTER_CONTEXT>
+ (ClonePointer->Context);
+ }
+
+ //
+ // If the clone failed, likely we're out of resources. Break out
+ // of the loop for now. We may end up starving DMA.
+ //
+ if( !NT_SUCCESS( Status ) )
+ {
+ KsStreamPointerUnlock( Leading, FALSE );
+ break;
+ }
+
+ //
+ // Program the fake hardware. I would use Clone->OffsetOut.*, but
+ // because of the optimization of one stream pointer per frame, it
+ // doesn't make complete sense.
+ //
+ ULONG MappingsUsed =
+ m_Sensor->ProgramScatterGatherMappings(
+ m_Pin,
+ &ClonePointer,
+ &(SPContext->BufferVirtual),
+ Leading->OffsetOut.Mappings,
+ Leading->OffsetOut.Remaining
+ );
+
+ //
+ // In order to keep one clone per frame and simplify the fake DMA
+ // logic, make a check to see if we completely used the mappings in
+ // the leading edge. Set a flag.
+ //
+ if( MappingsUsed == Leading->OffsetOut.Remaining )
+ {
+ m_PreviousStreamPointer = NULL;
+ }
+ else
+ {
+ m_PreviousStreamPointer = ClonePointer;
+ }
+
+ if( MappingsUsed )
+ {
+ //
+ // If any mappings were added to scatter / gather queues,
+ // advance the leading edge by that number of mappings. If
+ // we run off the end of the queue, Status will be
+ // STATUS_DEVICE_NOT_READY. Otherwise, the leading edge will
+ // point to a new frame. The previous one will not have been
+ // dismissed (unless "DMA" completed) since there's a clone
+ // pointer referencing the frames.
+ //
+ Status =
+ KsStreamPointerAdvanceOffsets(
+ Leading,
+ 0,
+ MappingsUsed,
+ FALSE
+ );
+ }
+ else
+ {
+ //
+ // The hardware was incapable of adding more entries. The S/G
+ // table is full.
+ //
+ Status = STATUS_PENDING;
+ break;
+ }
+ }
+
+ //
+ // If the leading edge failed to lock (this is always possible, remember
+ // that locking CAN occassionally fail), don't blow up passing NULL
+ // into KsStreamPointerUnlock. Also, set m_PendIo to kick us later...
+ //
+ if( !Leading )
+ {
+ m_PendIo = TRUE;
+
+ //
+ // If the lock failed, there's no point in getting called back
+ // immediately. The lock could fail due to insufficient memory,
+ // etc... In this case, we don't want to get called back immediately.
+ // Return pending. The m_PendIo flag will cause us to get kicked
+ // later.
+ //
+ Status = STATUS_PENDING;
+ }
+
+ //
+ // If we didn't run the leading edge off the end of the queue, unlock it.
+ //
+ if( NT_SUCCESS( Status ) && Leading )
+ {
+ KsStreamPointerUnlock( Leading, FALSE );
+ }
+ else
+ {
+ //
+ // DEVICE_NOT_READY indicates that the advancement ran off the end
+ // of the queue. We couldn't lock the leading edge.
+ //
+ if( Status == STATUS_DEVICE_NOT_READY )
+ {
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // If we failed with something that requires pending, set the pending I/O
+ // flag so we know we need to start it again.
+ //
+ if( !NT_SUCCESS( Status ) || Status == STATUS_PENDING )
+ {
+ m_PendIo = TRUE;
+ }
+
+ DBG_LEAVE( "( Pin=%d )=0x%08X", m_Pin->Id, Status );
+
+ return Status;
+}
+
+//
+// Emit metadata here for video or preview pin.
+//
+// This function gives us one last chance to tack metadata onto the sample.
+//
+void
+CCapturePin::
+EmitMetadata(
+ _Inout_ PKSSTREAM_HEADER pStreamHeader
+)
+{
+ PAGED_CODE();
+
+ NT_ASSERT(pStreamHeader);
+}
+
+
+NTSTATUS
+CCapturePin::
+CompleteMapping(
+ _In_ PKSSTREAM_POINTER Clone
+ )
+
+/*++
+
+Routine Description:
+
+ Called to notify the pin that a given number of scatter / gather
+ mappings have completed. Let the buffers go if possible.
+
+Arguments:
+
+ Clone -
+ The stream pointer for the frame to complete.
+ If Clone is null, use the head of the queue.
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DBG_ENTER("(Clone=%p)", Clone);
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if( !Clone )
+ {
+ Clone = KsPinGetFirstCloneStreamPointer(m_Pin);
+ }
+
+ //
+ // If we have completed all remaining mappings in this clone, it
+ // is an indication that the clone is ready to be deleted and the
+ // buffer released. Set anything required in the stream header which
+ // has not yet been set. If we have a clock, we can timestamp the
+ // sample.
+ //
+ if( Clone )
+ {
+ if( Clone->StreamHeader->DataUsed >= Clone->OffsetOut.Remaining )
+ {
+ Clone->StreamHeader->Duration =
+ m_VideoInfoHeader->AvgTimePerFrame;
+
+ Clone->StreamHeader->OptionsFlags |=
+ KSSTREAM_HEADER_OPTIONSF_DURATIONVALID;
+
+ //
+ // Increment the frame number. This is the total count of frames which
+ // have attempted capture.
+ //
+ m_FrameNumber++;
+ DBG_TRACE( "m_FrameNumber=%lld", m_FrameNumber );
+
+ //
+ // Double check the Stream Header size. AVStream makes no guarantee
+ // that because StreamHeaderSize is set to a specific size that you
+ // will get that size. If the proper data type handlers are not
+ // installed, the stream header will be of default size.
+ //
+ if ( Clone->StreamHeader->Size >= sizeof (KSSTREAM_HEADER) +
+ sizeof (KS_FRAME_INFO))
+ {
+ PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (
+ Clone->StreamHeader + 1
+ );
+
+ FrameInfo->ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
+ FrameInfo->dwFrameFlags = KS_VIDEO_FLAG_FRAME;
+ FrameInfo->PictureNumber = (LONGLONG)m_FrameNumber;
+
+ // I don't really have a way to tell if the device has dropped a frame
+ // or was not able to send a frame on time.
+ FrameInfo->DropCount = (LONGLONG)m_DroppedFrames;
+ }
+
+ KsStreamPointerDelete (Clone);
+ }
+ else
+ {
+ //
+ // If only part of the mappings in this clone have been completed,
+ // update the pointers. Since we're guaranteed this won't advance
+ // to a new frame by the check above, it won't fail.
+ //
+ Status =
+ KsStreamPointerAdvanceOffsets(
+ Clone,
+ 0,
+ Clone->StreamHeader->DataUsed,
+ FALSE
+ );
+ }
+ }
+ else
+ {
+ // We had nothing to process.
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // If we've used all the mappings in hardware and pended, we can kick
+ // processing to happen again if we've completed mappings.
+ //
+ if (m_PendIo)
+ {
+ KsPinAttemptProcessing (m_Pin, TRUE);
+ m_PendIo = FALSE;
+ }
+
+ DBG_LEAVE("(Clone=%p)=0x%08X", Clone, Status);
+ return Status;
+}
+
+
+bool
+CCapturePin::
+GetAcquireFailureKey()
+{
+ NTSTATUS ntstatus;
+ bool acquireFailure = false;
+ UNICODE_STRING RegistryKeyName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE handleRegKey = NULL;
+ PKEY_VALUE_FULL_INFORMATION pKeyInfo = NULL;
+ ULONG ulKeyInfoSizeNeeded = 0;
+ UNICODE_STRING ValueName = {0};
+
+
+ PAGED_CODE();
+
+ // Get the Registry key
+ RtlInitUnicodeString(&RegistryKeyName, L"\\Registry\\Machine\\Software\\Microsoft\\wtt\\MachineConfig");
+ InitializeObjectAttributes(&ObjectAttributes,
+ &RegistryKeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL, // handle
+ NULL);
+
+ ntstatus = ZwOpenKey(&handleRegKey, KEY_READ, &ObjectAttributes);
+ // If we can't open the key, we're done. Worst case, we cannot stream.
+ if(NT_SUCCESS(ntstatus))
+ {
+ // Get the location from the registry key
+ RtlInitUnicodeString(&ValueName, L"AcquireFailure");
+
+ // Figure out how big keyInfo needs to be
+ ntstatus = ZwQueryValueKey(handleRegKey,
+ &ValueName,
+ KeyValueFullInformation,
+ pKeyInfo,
+ 0,
+ &ulKeyInfoSizeNeeded );
+
+ // We expect one of these errors
+ if( (ntstatus == STATUS_BUFFER_TOO_SMALL) || (ntstatus == STATUS_BUFFER_OVERFLOW) )
+ {
+ // Allocate the memory needed for the key
+ pKeyInfo = (PKEY_VALUE_FULL_INFORMATION) new (NonPagedPoolNx) BYTE[ulKeyInfoSizeNeeded];
+ RtlZeroMemory(pKeyInfo, ulKeyInfoSizeNeeded);
+
+ // Now get the actual key data
+ ntstatus = ZwQueryValueKey( handleRegKey,
+ &ValueName,
+ KeyValueFullInformation,
+ pKeyInfo,
+ ulKeyInfoSizeNeeded,
+ &ulKeyInfoSizeNeeded );
+
+ if(ntstatus == STATUS_SUCCESS)
+ {
+ acquireFailure = true;
+ }
+
+ delete[] pKeyInfo;
+ }
+ }
+
+ // All done with the registry
+ if (NULL != handleRegKey)
+ {
+ ZwClose(handleRegKey);
+ }
+
+ return acquireFailure;
+}
+
+
+NTSTATUS
+CCapturePin::
+DispatchClose(
+ _In_ PKSPIN Pin,
+ _In_ PIRP Irp
+)
+/*++
+
+Routine Description:
+
+ Static thunking function to forward Close
+
+Arguments:
+
+ Pin -
+ Our pin
+ Irp -
+ The IRP the request came in on.
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+
+ DBG_ENTER("()");
+
+ NT_ASSERT( Pin );
+ NT_ASSERT( Pin->Context );
+ if( Pin && Pin->Context )
+ {
+ CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+ Status = pPin->Close( Irp );
+ }
+
+ DBG_LEAVE("()=0x%08X", Status);
+ return Status;
+}
+
+NTSTATUS
+CCapturePin::
+DispatchSetState(
+ _In_ PKSPIN Pin,
+ _In_ KSSTATE ToState,
+ _In_ KSSTATE FromState
+)
+/*++
+
+Routine Description:
+
+ Static thunking function to forward SetState
+
+Arguments:
+
+ Pin -
+ Our pin
+ Irp -
+ The IRP the request came in on.
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+
+ DBG_ENTER("()");
+
+ NT_ASSERT( Pin );
+ NT_ASSERT( Pin->Context );
+ if( Pin && Pin->Context )
+ {
+ CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+ Status = pPin->SetState( ToState, FromState );
+ }
+
+ DBG_LEAVE("()=0x%08X", Status);
+ return Status;
+}
+
+NTSTATUS
+CCapturePin::
+DispatchSetFormat(
+ _In_ PKSPIN Pin,
+ _In_opt_ PKSDATAFORMAT OldFormat,
+ _In_opt_ PKSMULTIPLE_ITEM OldAttributeList,
+ _In_ const KSDATARANGE *DataRange,
+ _In_opt_ const KSATTRIBUTE_LIST *AttributeRange
+)
+
+/*++
+
+Routine Description:
+
+ This is the set data format dispatch for the capture pin. It is called
+ in two circumstances.
+
+ 1: Before Pin's creation dispatch has been made to verify that
+ Pin->ConnectionFormat is an acceptable format for the range
+ DataRange. In this case OldFormat is NULL.
+
+ 2: After Pin's creation dispatch has been made and an initial format
+ selected in order to change the format for the pin. In this case,
+ OldFormat will not be NULL.
+
+ Validate that the format is acceptible and perform the actions necessary
+ to change format if appropriate.
+
+Arguments:
+
+ Pin -
+ The pin this format is being set on. The format itself will be in
+ Pin->ConnectionFormat.
+
+ OldFormat -
+ The previous format used on this pin. If this is NULL, it is an
+ indication that Pin's creation dispatch has not yet been made and
+ that this is a request to validate the initial format and not to
+ change formats.
+
+ OldAttributeList -
+ The old attribute list for the prior format
+
+ DataRange -
+ A range out of our list of data ranges which was determined to be
+ at least a partial match for Pin->ConnectionFormat. If the format
+ there is unacceptable for the range, STATUS_NO_MATCH should be
+ returned.
+
+ AttributeRange -
+ The attribute range
+
+Return Value:
+
+ Success / Failure
+
+ STATUS_SUCCESS -
+ The format is acceptable / the format has been changed
+
+ STATUS_NO_MATCH -
+ The format is not-acceptable / the format has not been changed
+
+--*/
+
+{
+
+ PAGED_CODE( );
+
+ DBG_ENTER( "(Pin->Id=%d)", Pin->Id );
+
+ NTSTATUS Status = STATUS_NO_MATCH;
+ const GUID ImageInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) };
+ const GUID VideoInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) };
+
+ //
+ // Find the pin, if it exists yet. OldFormat will be an indication of
+ // this. If we're changing formats, OldFormat will be non-NULL.
+ //
+ // You cannot use Pin->Context to make the determination. AVStream
+ // preinitializes this to the filter's context.
+ //
+ CCapturePin *CapPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+
+ // For debugging...
+ UNICODE_STRING NewFormat;
+ UNICODE_STRING RefFormat;
+ RtlStringFromGUID( Pin->ConnectionFormat->SubFormat, &NewFormat );
+ RtlStringFromGUID( DataRange->SubFormat, &RefFormat );
+
+ if( IsEqualGUID( Pin->ConnectionFormat->Specifier, ImageInfoSpecifier ) &&
+ Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_IMAGEINFO) )
+ {
+ PKS_DATAFORMAT_IMAGEINFO ConnectionFormat = reinterpret_cast <PKS_DATAFORMAT_IMAGEINFO> (Pin->ConnectionFormat);
+
+ //
+ // DataRange comes out of OUR data range list. I know the range
+ // is valid as such.
+ //
+ const KS_DATARANGE_IMAGE *VIRange = reinterpret_cast <const KS_DATARANGE_IMAGE *> (DataRange);
+
+ //
+ // Check that bmiHeader.biSize is valid since we use it later.
+ //
+ ULONG ImageHeaderSize = ConnectionFormat->ImageInfoHeader.biSize;
+ ULONG DataFormatSize = sizeof (KSDATAFORMAT) +sizeof(KS_BITMAPINFOHEADER);
+
+ if( ImageHeaderSize < ConnectionFormat->ImageInfoHeader.biSize ||
+ DataFormatSize < ImageHeaderSize ||
+ DataFormatSize > ConnectionFormat->DataFormat.FormatSize )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Check that the format is a match for the selected range.
+ //
+ else if( (ConnectionFormat->ImageInfoHeader.biWidth != VIRange->ImageInfoHeader.biWidth) ||
+ (ConnectionFormat->ImageInfoHeader.biHeight != VIRange->ImageInfoHeader.biHeight) ||
+ !IsEqualGUID( Pin->ConnectionFormat->SubFormat, DataRange->SubFormat ) )
+ {
+ Status = STATUS_NO_MATCH;
+ }
+ else
+ {
+
+ ULONG ImageSize;
+
+ if( !MultiplyCheckOverflow( (ULONG) ConnectionFormat->ImageInfoHeader.biWidth, (ULONG) abs( ConnectionFormat->ImageInfoHeader.biHeight ), &ImageSize ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ else if( !MultiplyCheckOverflow( ImageSize, (ULONG) (ConnectionFormat->ImageInfoHeader.biBitCount/8), &ImageSize ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Valid for the formats we use. Otherwise, this would be
+ // checked later.
+ //
+ else if( ConnectionFormat->ImageInfoHeader.biSizeImage < ImageSize )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ //
+ // We can accept the format.
+ //
+ Status = STATUS_SUCCESS;
+
+ //
+ // OldFormat is an indication that this is a format change.
+ // Since I do not implement the
+ // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do
+ // not handle dynamic format changes.
+ //
+ // If something changes while we're in the stop state, we're
+ // fine to handle it since we haven't "configured the hardware"
+ // yet.
+ //
+ if( OldFormat && CapPin )
+ {
+ //
+ // If we're in the stop state, we can handle just about any
+ // change. We don't support dynamic format changes.
+ //
+ if( Pin->DeviceState == KSSTATE_STOP )
+ {
+ Status = CapPin->CaptureBitmapInfoHeader();
+ }
+ else
+ {
+ //
+ // Because we don't accept dynamic format changes, we
+ // should never get here. Just being over-protective.
+ //
+ Status = STATUS_INVALID_DEVICE_STATE;
+ }
+ }
+ }
+ }
+ }
+
+ else if( IsEqualGUID( Pin->ConnectionFormat->Specifier,
+ VideoInfoSpecifier ) &&
+ Pin->ConnectionFormat->FormatSize >=
+ sizeof (KS_DATAFORMAT_VIDEOINFOHEADER) )
+ {
+
+ PKS_DATAFORMAT_VIDEOINFOHEADER ConnectionFormat =
+ reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER>
+ (Pin->ConnectionFormat);
+
+ //
+ // DataRange comes out of OUR data range list. I know the range
+ // is valid as such.
+ //
+ const KS_DATARANGE_VIDEO *VIRange =
+ reinterpret_cast <const KS_DATARANGE_VIDEO *>
+ (DataRange);
+
+ //
+ // Check that bmiHeader.biSize is valid since we use it later.
+ //
+ ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER(
+ &ConnectionFormat->VideoInfoHeader
+ );
+
+ ULONG DataFormatSize = FIELD_OFFSET(
+ KS_DATAFORMAT_VIDEOINFOHEADER, VideoInfoHeader
+ ) + VideoHeaderSize;
+
+ if( VideoHeaderSize < ConnectionFormat->VideoInfoHeader.bmiHeader.biSize ||
+ DataFormatSize < VideoHeaderSize ||
+ DataFormatSize > ConnectionFormat->DataFormat.FormatSize )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Check that the format is a match for the selected range.
+ //
+ else if(
+ (ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth !=
+ VIRange->VideoInfoHeader.bmiHeader.biWidth) ||
+ (ConnectionFormat->VideoInfoHeader.bmiHeader.biHeight !=
+ VIRange->VideoInfoHeader.bmiHeader.biHeight) ||
+ !IsEqualGUID( Pin->ConnectionFormat->SubFormat, DataRange->SubFormat ) )
+ {
+ DBG_TRACE("FAILED: Height=(%d,%d), Width=(%d,%d), SubFormat=(%wZ,%wZ)",
+ ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth,
+ VIRange->VideoInfoHeader.bmiHeader.biWidth,
+ ConnectionFormat->VideoInfoHeader.bmiHeader.biHeight,
+ VIRange->VideoInfoHeader.bmiHeader.biHeight,
+ &NewFormat, &RefFormat );
+ Status = STATUS_NO_MATCH;
+ }
+ else
+ {
+ DBG_TRACE("MATCH: Height=(%d,%d), Width=(%d,%d), SubFormat=(%wZ,%wZ)",
+ ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth,
+ VIRange->VideoInfoHeader.bmiHeader.biWidth,
+ ConnectionFormat->VideoInfoHeader.bmiHeader.biHeight,
+ VIRange->VideoInfoHeader.bmiHeader.biHeight,
+ &NewFormat, &RefFormat );
+
+ //
+ // Compute the minimum size of our buffers to validate against.
+ // The image synthesis routines synthesize |biHeight| rows of
+ // biWidth pixels in either RGB24 or UYVY. In order to ensure
+ // safe synthesis into the buffer, we need to know how large an
+ // image this will produce.
+ //
+ // I do this explicitly because of the method that the data is
+ // synthesized. A variation of this may or may not be necessary
+ // depending on the mechanism the driver in question fills the
+ // capture buffers. The important thing is to ensure that they
+ // aren't overrun during capture.
+ //
+ ULONG ImageSize;
+
+ if( !MultiplyCheckOverflow(
+ (ULONG) ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth,
+ (ULONG) abs( ConnectionFormat->
+ VideoInfoHeader.bmiHeader.biHeight ),
+ &ImageSize
+ ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // We only support KS_BI_RGB (24) and KS_BI_YUV422 (16), so
+ // this is valid for those formats.
+ //
+ else if( !MultiplyCheckOverflow(
+ ImageSize,
+ (ULONG) (ConnectionFormat->
+ VideoInfoHeader.bmiHeader.biBitCount / 8),
+ &ImageSize
+ ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Valid for the formats we use. Otherwise, this would be
+ // checked later.
+ //
+ else if( ConnectionFormat->VideoInfoHeader.bmiHeader.biSizeImage <
+ ImageSize )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ //
+ // We can accept the format.
+ //
+ Status = STATUS_SUCCESS;
+
+ //
+ // OldFormat is an indication that this is a format change.
+ // Since I do not implement the
+ // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do
+ // not handle dynamic format changes.
+ //
+ // If something changes while we're in the stop state, we're
+ // fine to handle it since we haven't "configured the hardware"
+ // yet.
+ //
+ if( OldFormat && CapPin )
+ {
+ //
+ // If we're in the stop state, we can handle just about any
+ // change. We don't support dynamic format changes.
+ //
+ if( Pin->DeviceState == KSSTATE_STOP )
+ {
+ if( !CapPin->CaptureBitmapInfoHeader( ) )
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ else
+ {
+ //
+ // Because we don't accept dynamic format changes, we
+ // should never get here. Just being over-protective.
+ //
+ Status = STATUS_INVALID_DEVICE_STATE;
+ }
+ }
+ }
+ }
+ }
+
+ RtlFreeUnicodeString(&NewFormat);
+ RtlFreeUnicodeString(&RefFormat);
+
+ DBG_LEAVE( "(Pin->Id=%d)=0x%08X", Pin->Id, Status );
+ return Status;
+}
+
+NTSTATUS
+CCapturePin::
+IntersectHandler(
+ _In_ PKSFILTER Filter,
+ _In_ PIRP Irp,
+ _In_ PKSP_PIN PinInstance,
+ _In_ PKSDATARANGE CallerDataRange,
+ _In_ PKSDATARANGE DescriptorDataRange,
+ _In_ ULONG BufferSize,
+ _Out_opt_ PVOID Data OPTIONAL,
+ _Out_ PULONG DataSize
+)
+
+/*++
+
+Routine Description:
+
+ This routine handles video pin intersection queries by determining the
+ intersection between two data ranges.
+
+Arguments:
+
+ Filter -
+ Contains a void pointer to the filter structure.
+
+ Irp -
+ Contains a pointer to the data intersection property request.
+
+ PinInstance -
+ Contains a pointer to a structure indicating the pin in question.
+
+ CallerDataRange -
+ Contains a pointer to one of the data ranges supplied by the client
+ in the data intersection request. The format type, subtype and
+ specifier are compatible with the DescriptorDataRange.
+
+ DescriptorDataRange -
+ Contains a pointer to one of the data ranges from the pin descriptor
+ for the pin in question. The format type, subtype and specifier are
+ compatible with the CallerDataRange.
+
+ BufferSize -
+ Contains the size in bytes of the buffer pointed to by the Data
+ argument. For size queries, this value will be zero.
+
+ Data -
+ Optionally contains a pointer to the buffer to contain the data
+ format structure representing the best format in the intersection
+ of the two data ranges. For size queries, this pointer will be
+ NULL.
+
+ DataSize -
+ Contains a pointer to the location at which to deposit the size
+ of the data format. This information is supplied by the function
+ when the format is actually delivered and in response to size
+ queries.
+
+Return Value:
+
+ STATUS_SUCCESS if there is an intersection and it fits in the supplied
+ buffer, STATUS_BUFFER_OVERFLOW for successful size queries,
+ STATUS_NO_MATCH if the intersection is empty, or
+ STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small.
+
+--*/
+
+{
+ PAGED_CODE( );
+
+ const GUID ImageInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) };
+ const GUID VideoInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) };
+ ULONG DataFormatSize;
+
+ NT_ASSERT( Filter );
+ NT_ASSERT( Irp );
+ NT_ASSERT( PinInstance );
+ NT_ASSERT( CallerDataRange );
+ NT_ASSERT( DescriptorDataRange );
+ NT_ASSERT( DataSize );
+
+ //
+ // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER
+ //
+ if( IsEqualGUID( CallerDataRange->Specifier, ImageInfoSpecifier ) &&
+ CallerDataRange->FormatSize >= sizeof (KS_DATARANGE_IMAGE) )
+ {
+
+ PKS_DATARANGE_IMAGE callerDataRange =
+ reinterpret_cast <PKS_DATARANGE_IMAGE> (CallerDataRange);
+
+ PKS_DATARANGE_IMAGE descriptorDataRange =
+ reinterpret_cast <PKS_DATARANGE_IMAGE> (DescriptorDataRange);
+
+ //
+ // Check that the other fields match
+ //
+ if( (RtlCompareMemory( &callerDataRange->ConfigCaps, &descriptorDataRange->ConfigCaps, sizeof (KS_VIDEO_STREAM_CONFIG_CAPS) )
+ != sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) )
+ {
+ return STATUS_NO_MATCH;
+ }
+
+ {
+ ULONG ImageHeaderSize = ((PKS_DATARANGE_IMAGE) callerDataRange)->ImageInfoHeader.biSize;
+ ULONG DataRangeSize = FIELD_OFFSET( KS_DATARANGE_IMAGE, ImageInfoHeader ) + ImageHeaderSize;
+
+ if( ImageHeaderSize < callerDataRange->ImageInfoHeader.biSize ||
+ DataRangeSize < ImageHeaderSize ||
+ DataRangeSize > callerDataRange->DataRange.FormatSize )
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ DataFormatSize = sizeof (KSDATAFORMAT) +sizeof(KS_BITMAPINFOHEADER);
+ //
+ // If the passed buffer size is 0, it indicates that this is a size
+ // only query. Return the size of the intersecting data format and
+ // pass back STATUS_BUFFER_OVERFLOW.
+ //
+ if( BufferSize == 0 )
+ {
+ *DataSize = DataFormatSize;
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // Verify that the provided structure is large enough to
+ // accept the result.
+ //
+ if( BufferSize < DataFormatSize )
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader
+ //
+ *DataSize = DataFormatSize;
+
+ PKS_DATAFORMAT_IMAGEINFO pFormatImageInfoHeader = PKS_DATAFORMAT_IMAGEINFO( Data );
+
+ //
+ // Copy over the KSDATAFORMAT. This is precisely the same as the
+ // KSDATARANGE (it's just the GUIDs, etc... not the format information
+ // following any data format.
+ //
+ RtlCopyMemory( pFormatImageInfoHeader, DescriptorDataRange, sizeof (KSDATAFORMAT) );
+ pFormatImageInfoHeader->DataFormat.FormatSize = DataFormatSize;
+
+ //
+ // Copy over the callers requested KS_BITMAPINFOHEADER
+ //
+ RtlCopyMemory( &pFormatImageInfoHeader->ImageInfoHeader, &callerDataRange->ImageInfoHeader, sizeof(KS_BITMAPINFOHEADER) );
+ return STATUS_SUCCESS;
+ }
+ else if( IsEqualGUID( CallerDataRange->Specifier, VideoInfoSpecifier ) &&
+ CallerDataRange->FormatSize >= sizeof (KS_DATARANGE_VIDEO) )
+ {
+
+ PKS_DATARANGE_VIDEO callerDataRange =
+ reinterpret_cast <PKS_DATARANGE_VIDEO> (CallerDataRange);
+
+ PKS_DATARANGE_VIDEO descriptorDataRange =
+ reinterpret_cast <PKS_DATARANGE_VIDEO> (DescriptorDataRange);
+
+ PKS_DATAFORMAT_VIDEOINFOHEADER FormatVideoInfoHeader;
+
+ //
+ // Check that the other fields match
+ //
+ if( (callerDataRange->bFixedSizeSamples !=
+ descriptorDataRange->bFixedSizeSamples) ||
+ (callerDataRange->bTemporalCompression !=
+ descriptorDataRange->bTemporalCompression) ||
+ (callerDataRange->StreamDescriptionFlags !=
+ descriptorDataRange->StreamDescriptionFlags) ||
+ (callerDataRange->MemoryAllocationFlags !=
+ descriptorDataRange->MemoryAllocationFlags) ||
+ (RtlCompareMemory( &callerDataRange->ConfigCaps,
+ &descriptorDataRange->ConfigCaps,
+ sizeof (KS_VIDEO_STREAM_CONFIG_CAPS) ) !=
+ sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) )
+ {
+ return STATUS_NO_MATCH;
+ }
+
+ //
+ // KS_SIZE_VIDEOHEADER() below is relying on bmiHeader.biSize from
+ // the caller's data range. This **MUST** be validated; the
+ // extended bmiHeader size (biSize) must not extend past the end
+ // of the range buffer. Possible arithmetic overflow is also
+ // checked for.
+ //
+ ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER(
+ &callerDataRange->VideoInfoHeader
+ );
+
+ ULONG DataRangeSize =
+ FIELD_OFFSET( KS_DATARANGE_VIDEO, VideoInfoHeader ) +
+ VideoHeaderSize;
+
+ //
+ // Check that biSize does not extend past the buffer. The
+ // first two checks are for arithmetic overflow on the
+ // operations to compute the alleged size. (On unsigned
+ // math, a+b < a iff an arithmetic overflow occurred).
+ //
+ if( VideoHeaderSize < callerDataRange->VideoInfoHeader.bmiHeader.biSize ||
+ DataRangeSize < VideoHeaderSize ||
+ DataRangeSize > callerDataRange->DataRange.FormatSize )
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ DataFormatSize =
+ sizeof (KSDATAFORMAT) +
+ KS_SIZE_VIDEOHEADER( &callerDataRange->VideoInfoHeader );
+
+
+ //
+ // If the passed buffer size is 0, it indicates that this is a size
+ // only query. Return the size of the intersecting data format and
+ // pass back STATUS_BUFFER_OVERFLOW.
+ //
+ if( BufferSize == 0 )
+ {
+ *DataSize = DataFormatSize;
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // Verify that the provided structure is large enough to
+ // accept the result.
+ //
+ if( BufferSize < DataFormatSize )
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader
+ //
+ *DataSize = DataFormatSize;
+
+ FormatVideoInfoHeader = PKS_DATAFORMAT_VIDEOINFOHEADER( Data );
+
+ //
+ // Copy over the KSDATAFORMAT. This is precisely the same as the
+ // KSDATARANGE (it's just the GUIDs, etc... not the format information
+ // following any data format.
+ //
+ RtlCopyMemory(
+ &FormatVideoInfoHeader->DataFormat,
+ DescriptorDataRange,
+ sizeof (KSDATAFORMAT) );
+
+ FormatVideoInfoHeader->DataFormat.FormatSize = DataFormatSize;
+
+ //
+ // Copy over the callers requested VIDEOINFOHEADER
+ //
+
+ RtlCopyMemory(
+ &FormatVideoInfoHeader->VideoInfoHeader,
+ &callerDataRange->VideoInfoHeader,
+ KS_SIZE_VIDEOHEADER( &callerDataRange->VideoInfoHeader )
+ );
+
+ //
+ // Calculate biSizeImage for this request, and put the result in both
+ // the biSizeImage field of the bmiHeader AND in the SampleSize field
+ // of the DataFormat.
+ //
+ // Note that for compressed sizes, this calculation will probably not
+ // be just width * height * bitdepth
+ //
+ FormatVideoInfoHeader->VideoInfoHeader.bmiHeader.biSizeImage =
+ FormatVideoInfoHeader->DataFormat.SampleSize =
+ KS_DIBSIZE( FormatVideoInfoHeader->VideoInfoHeader.bmiHeader );
+
+ //
+ // REVIEW - Perform other validation such as cropping and scaling checks
+ //
+
+ return STATUS_SUCCESS;
+
+ } // End of VIDEOINFOHEADER specifier
+
+ return STATUS_NO_MATCH;
+}
+
+NTSTATUS
+CCapturePin::
+DispatchProcess(
+ _In_ PKSPIN Pin
+)
+/*++
+
+Routine Description:
+
+ Static thunking function to forward Process
+
+Arguments:
+
+ Pin -
+ Our pin
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+
+ DBG_ENTER("()");
+
+ NT_ASSERT( Pin );
+ NT_ASSERT( Pin->Context );
+ if( Pin && Pin->Context )
+ {
+ CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+ Status = pPin->Process();
+ }
+
+ DBG_LEAVE("()=0x%08X", Status);
+ return Status;
+}
+
+void
+CCapturePin::
+DispatchReset(
+ _In_ PKSPIN Pin
+)
+/*++
+
+Routine Description:
+
+ Static thunking function to forward Reset
+
+Arguments:
+
+ Pin -
+ Our pin
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ DBG_ENTER("()");
+
+ NT_ASSERT( Pin );
+ NT_ASSERT( Pin->Context );
+ if( Pin && Pin->Context )
+ {
+ CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+ pPin->Reset( );
+ }
+ DBG_LEAVE("()");
+}
+
+NTSTATUS
+CCapturePin::
+DispatchConnect(
+ _In_ PKSPIN Pin
+)
+/*++
+
+Routine Description:
+
+ Static thunking function to forward Reset
+
+Arguments:
+
+ Pin -
+ Our pin
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+
+ DBG_ENTER("()");
+
+ NT_ASSERT( Pin );
+ NT_ASSERT( Pin->Context );
+ if( Pin && Pin->Context )
+ {
+ CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+ Status = pPin->Connect( );
+ }
+
+ DBG_LEAVE("()=0x%08X", Status);
+ return Status;
+}
+
+void
+CCapturePin::
+DispatchDisconnect(
+ _In_ PKSPIN Pin
+)
+/*++
+
+Routine Description:
+
+ Static thunking function to forward Disconnect
+
+Arguments:
+
+ Pin -
+ Our pin
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ DBG_ENTER("()");
+
+ NT_ASSERT( Pin );
+ NT_ASSERT( Pin->Context );
+ if( Pin && Pin->Context )
+ {
+ CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context);
+ pPin->Disconnect( );
+ }
+ DBG_LEAVE("()");
+}
+
+
+NTSTATUS
+CCapturePin::
+Close(
+ _In_ PIRP Irp
+)
+/*++
+
+Routine Description:
+
+ Close handler.
+
+ Replace or overload if you need special handling.
+
+Arguments:
+
+ Irp -
+ The IRP the request came in on.
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ // Some reasonable default behavior.
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CCapturePin::
+SetFormat(
+ _In_opt_ PKSDATAFORMAT OldFormat,
+ _In_opt_ PKSMULTIPLE_ITEM OldAttributeList,
+ _In_ const KSDATARANGE *DataRange,
+ _In_opt_ const KSATTRIBUTE_LIST *AttributeRange
+)
+/*++
+
+Routine Description:
+
+ SetFormat handler.
+
+ Replace or overload if you need special handling.
+
+Arguments:
+
+ OldFormat -
+ The previous format used on this pin. If this is NULL, it is an
+ indication that Pin's creation dispatch has not yet been made and
+ that this is a request to validate the initial format and not to
+ change formats.
+
+ OldAttributeList -
+ The old attribute list for the prior format
+
+ DataRange -
+ A range out of our list of data ranges which was determined to be
+ at least a partial match for Pin->ConnectionFormat. If the format
+ there is unacceptable for the range, STATUS_NO_MATCH should be
+ returned.
+
+ AttributeRange -
+ The attribute range
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ // Some reasonable default behavior here.
+ return STATUS_SUCCESS;
+}
+
+void
+CCapturePin::
+Reset()
+/*++
+
+Routine Description:
+
+ Reset handler.
+
+ Replace or overload if you need special handling.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE( );
+
+ // Some reasonable default behavior here.
+ m_Sensor->Reset(m_Pin);
+}
+
+NTSTATUS
+CCapturePin::
+Connect()
+/*++
+
+Routine Description:
+
+ Connect handler.
+
+ Replace or overload if you need special handling.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Success / Failure
+
+--*/
+{
+ PAGED_CODE( );
+
+ // Some reasonable default behavior here.
+ return STATUS_SUCCESS;
+}
+
+void
+CCapturePin::
+Disconnect()
+/*++
+
+Routine Description:
+
+ Disconnect handler.
+
+ Replace or overload if you need special handling.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE( );
+
+ // Some reasonable default behavior here.
+}
+
+// Update the Pin's Allocator Framing
+NTSTATUS
+CCapturePin::
+UpdateAllocatorFraming()
+{
+ PAGED_CODE();
+
+ // Acquire the lock and update the Pin's allocator framing.
+ NTSTATUS Status =
+ KsEdit( m_Pin, &m_Pin->Descriptor, AVSHWS_POOLTAG);
+
+ if( NT_SUCCESS( Status ) )
+ {
+ //
+ // If the edits proceeded without running out of memory, adjust
+ // the framing based on the video info header.
+ //
+ Status = KsEdit( m_Pin, &m_Pin->Descriptor->AllocatorFraming, AVSHWS_POOLTAG );
+
+ if( NT_SUCCESS( Status ) )
+ {
+ //
+ // We've KsEdit'ed this... I'm safe to cast away constness as
+ // long as the edit succeeded.
+ //
+ PKSALLOCATOR_FRAMING_EX Framing =
+ const_cast <PKSALLOCATOR_FRAMING_EX> (
+ m_Pin->Descriptor->AllocatorFraming
+ );
+
+ Framing->FramingItem[0].Frames = m_DesiredFrames;
+ Framing->FramingItem[0].PhysicalRange.MinFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage;
+ Framing->FramingItem[0].PhysicalRange.MaxFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage;
+ Framing->FramingItem [0].FramingRange.Range.MinFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage;
+ Framing->FramingItem [0].FramingRange.Range.MaxFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage;
+
+ Framing->FramingItem [0].PhysicalRange.Stepping = 0;
+ Framing->FramingItem [0].FramingRange.Range.Stepping = 0;
+
+ DBG_TRACE( "Advertising Frame requirement: %d", m_DesiredFrames );
+ DBG_TRACE("Image size estimate of: %d bytes", m_VideoInfoHeader->bmiHeader.biSizeImage );
+ }
+ }
+
+ DBG_LEAVE("()=0x%08X",Status);
+ return Status;
+}
+