diff options
| author | Wei Mao <[email protected]> | 2017-03-17 19:47:04 -0700 |
|---|---|---|
| committer | Wei Mao <[email protected]> | 2017-03-17 19:47:04 -0700 |
| commit | 1a3e0d580380e58bf336a242d2affc8a1e2d1ddf (patch) | |
| tree | bf5d9c5b0b4cba1b81726b9f78c4d5ff5c636fea /AVStream/avshws/capture.cpp | |
| parent | da21c8784c83c5fd614f3030323e229d6a5fb10e (diff) | |
Fix cases
Diffstat (limited to 'AVStream/avshws/capture.cpp')
| -rw-r--r-- | AVStream/avshws/capture.cpp | 1704 |
1 files changed, 1704 insertions, 0 deletions
diff --git a/AVStream/avshws/capture.cpp b/AVStream/avshws/capture.cpp new file mode 100644 index 00000000..f23ec0e7 --- /dev/null +++ b/AVStream/avshws/capture.cpp @@ -0,0 +1,1704 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.cpp + + Abstract: + + This file contains source for the video capture pin on the capture + filter. The capture sample performs "fake" DMA directly into + the capture buffers. Common buffer DMA will work slightly differently. + + For common buffer DMA, the general technique would be DPC schedules + processing with KsPinAttemptProcessing. The processing routine grabs + the leading edge, copies data out of the common buffer and advances. + Cloning would not be necessary with this technique. It would be + similiar to the way "AVSSamp" works, but it would be pin-centric. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "avshws.h" +#include <ksmedia.h> +#include "ntintsafe.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +#define DMAX_X 320 +#define DMAX_Y 240 +#define D_X 320 +#define D_Y 240 + +CCapturePin:: +CCapturePin ( + IN PKSPIN Pin + ) : + m_Pin (Pin) + ,m_PresentationTime (0) + +/*++ + +Routine Description: + + Construct a new capture pin. + +Arguments: + + Pin - + The AVStream pin object corresponding to the capture pin + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + PKSDEVICE Device = KsPinGetDevice (Pin); + + // + // Set up our device pointer. This gives us access to "hardware I/O" + // during the capture routines. + // + m_Device = reinterpret_cast <CCaptureDevice *> (Device -> Context); +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Create a new capture pin. This is the creation dispatch for + the video capture pin. + +Arguments: + + Pin - + The pin being created + + Irp - + The creation Irp + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + CCapturePin *CapPin = new (NonPagedPoolNx, 'niPC') CCapturePin (Pin); + + if (!CapPin) { + // + // Return failure if we couldn't create the pin. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } 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 ( + Pin -> Bag, + reinterpret_cast <PVOID> (CapPin), + reinterpret_cast <PFNKSFREE> (CCapturePin::Cleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete CapPin; + } else { + Pin -> Context = reinterpret_cast <PVOID> (CapPin); + } + + } + + // + // 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. + // + PKS_VIDEOINFOHEADER VideoInfoHeader = NULL; + + if (NT_SUCCESS (Status)) { + + VideoInfoHeader = CapPin -> CaptureVideoInfoHeader (); + if (!VideoInfoHeader) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(Status)) { + // + // We need to edit the descriptor to ensure we don't mess up any other + // pins using the descriptor or touch read-only memory. + // + Status = KsEdit ( + Pin, + &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 ( + Pin, + &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> ( + Pin -> Descriptor -> AllocatorFraming + ); + + Framing -> FramingItem [0].Frames = 2; + + // + // The physical and optimal ranges must be biSizeImage. We only + // support one frame size, precisely the size of each capture + // image. + // + Framing -> FramingItem [0].PhysicalRange.MinFrameSize = + Framing -> FramingItem [0].PhysicalRange.MaxFrameSize = + Framing -> FramingItem [0].FramingRange.Range.MinFrameSize = + Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize = + VideoInfoHeader -> bmiHeader.biSizeImage; + + Framing -> FramingItem [0].PhysicalRange.Stepping = + Framing -> FramingItem [0].FramingRange.Range.Stepping = + 0; + + } + + } + } + + if (NT_SUCCESS (Status)) { + // + // Adjust the stream header size. The video packets have extended + // header info (KS_FRAME_INFO). + // + Pin -> StreamHeaderSize = sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO); + + } + return Status; +} + +/*************************************************/ + + +PKS_VIDEOINFOHEADER +CCapturePin:: +CaptureVideoInfoHeader ( + ) + +/*++ + +Routine Description: + + Capture the video info header out of the connection format. This + is what we use to base synthesized images off. + +Arguments: + + None + +Return Value: + + The captured video info header or NULL if there is insufficient + memory. + +--*/ + +{ + + PAGED_CODE(); + + 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) + return NULL; + + // + // Bag the newly allocated header space. This will get cleaned up + // automatically when the pin closes. + // + NTSTATUS Status = + KsAddItemToObjectBag ( + m_Pin -> Bag, + reinterpret_cast <PVOID> (m_VideoInfoHeader), + NULL + ); + + if (!NT_SUCCESS (Status)) { + + ExFreePool (m_VideoInfoHeader); + return NULL; + + } else { + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + RtlCopyMemory ( + m_VideoInfoHeader, + ConnectionHeader, + KS_SIZE_VIDEOHEADER (ConnectionHeader) + ); + + } + + return m_VideoInfoHeader; + +} + + +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(); + + NTSTATUS Status = STATUS_SUCCESS; + PKSSTREAM_POINTER Leading; + + _DbgPrintF(DEBUGLVL_VERBOSE, ("Process")); + + 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); + Status = STATUS_SUCCESS; + } + + // + // 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_Device -> ProgramScatterGatherMappings ( + 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 in a completion DPC. + // + if (!NT_SUCCESS (Status) || Status == STATUS_PENDING) { + m_PendIo = TRUE; + } + + _DbgPrintF(DEBUGLVL_VERBOSE, ("Leaving Process...")); + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +CleanupReferences ( + ) + +/*++ + +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:: +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; + + switch (ToState) { + + case KSSTATE_STOP: + + // + // First, stop the hardware if we actually did anything to it. + // + if (m_HardwareState != HardwareStopped) { + Status = m_Device -> Stop (); + NT_ASSERT (NT_SUCCESS (Status)); + + m_HardwareState = HardwareStopped; + } + + // + // 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 = CleanupReferences (); + + // + // 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_Device -> ReleaseHardwareResources ( + ); + + 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. + // + if (FromState == KSSTATE_STOP) { + Status = m_Device -> AcquireHardwareResources ( + this, + m_VideoInfoHeader + ); + + 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; + } + + } else { + // + // Standard transport pins will always receive transitions in + // +/- 1 manner. This means we'll always see a PAUSE->ACQUIRE + // transition before stopping the pin. + // + // The below is done because on DirectX 8.0, when the pin gets + // a message to stop, the queue is inaccessible. The reset + // which comes on every stop happens after this (at which time + // the queue is inaccessible also). So, for compatibility with + // DirectX 8.0, I am stopping the "fake" hardware at this + // point and cleaning up all references we have on frames. See + // the comments above regarding the CleanupReferences call. + // + // If this sample were targeting XP only, the below code would + // not be here. Again, I only do this so the sample does not + // hang when it is stopped running on a configuration such as + // Win2K + DX8. + // + if (m_HardwareState != HardwareStopped) { + Status = m_Device -> Stop (); + NT_ASSERT (NT_SUCCESS (Status)); + + m_HardwareState = HardwareStopped; + } + + Status = CleanupReferences (); + } + + 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_Device -> Pause (TRUE); + + if (NT_SUCCESS (Status)) { + m_HardwareState = HardwarePaused; + } + + } + 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 (m_HardwareState == HardwarePaused) { + Status = m_Device -> Pause (FALSE); + } else { + Status = m_Device -> Start (); + } + + if (NT_SUCCESS (Status)) { + m_HardwareState = HardwareRunning; + } + + break; + + } + + 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 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 VideoInfoSpecifier = + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)}; + + NT_ASSERT(Filter); + NT_ASSERT(Irp); + NT_ASSERT(PinInstance); + NT_ASSERT(CallerDataRange); + NT_ASSERT(DescriptorDataRange); + NT_ASSERT(DataSize); + + ULONG DataFormatSize; + + // + // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER + // + 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; +} + +/*************************************************/ + +BOOL +MultiplyCheckOverflow ( + ULONG a, + ULONG b, + ULONG *pab + ) + +/*++ + +Routine Description: + + Perform a 32 bit unsigned multiplication and check for arithmetic overflow. + +Arguments: + + a - + First operand + + b - + Second operand + + pab - + Result + +Return Value: + + TRUE - + no overflow + + FALSE - + overflow occurred + +--*/ + +{ + PAGED_CODE(); + + *pab = a * b; + if ((a == 0) || (((*pab) / a) == b)) { + return TRUE; + } + return FALSE; +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +DispatchSetFormat ( + IN PKSPIN Pin, + IN PKSDATAFORMAT OldFormat OPTIONAL, + IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL, + IN const KSDATARANGE *DataRange, + IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL + ) + +/*++ + +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(); + + NTSTATUS Status = STATUS_NO_MATCH; + + const GUID VideoInfoSpecifier = + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)}; + + CCapturePin *CapPin = NULL; + + // + // 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. + // + if (OldFormat) { + CapPin = reinterpret_cast <CCapturePin *> (Pin -> Context); + } + + 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) || + + (ConnectionFormat -> VideoInfoHeader.bmiHeader.biCompression != + VIRange -> VideoInfoHeader.bmiHeader.biCompression) + + ) { + + Status = STATUS_NO_MATCH; + + } else { + + // + // 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) { + // + // 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 -> CaptureVideoInfoHeader ()) { + 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; + } + } + + } + + } + + } + return Status; +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +void +CCapturePin:: +CompleteMappings ( + IN ULONG NumMappings + ) + +/*++ + +Routine Description: + + Called to notify the pin that a given number of scatter / gather + mappings have completed. Let the buffers go if possible. + We're called at DPC. + +Arguments: + + NumMappings - + The number of mappings that have completed. + +Return Value: + + None + +--*/ + +{ + + ULONG MappingsRemaining = NumMappings; + + // + // Walk through the clones list and delete clones whose time has come. + // The list is guaranteed to be kept in the order they were cloned. + // + PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin); + + while (MappingsRemaining && Clone) { + + PKSSTREAM_POINTER NextClone = KsStreamPointerGetNextClone (Clone); + +#if defined(_X86_) + // + // Count up the number of bytes we've completed and mark this + // in the Stream Header. In mapped queues + // (KSPIN_FLAG_GENERATE_MAPPINGS), this is the responsibility of + // the minidriver. In non-mapped queues, AVStream performs this. + // + ULONG MappingsToCount = + (MappingsRemaining > Clone -> OffsetOut.Remaining) ? + Clone -> OffsetOut.Remaining : + MappingsRemaining; + + // + // Update DataUsed according to the mappings. + // + for (ULONG CurMapping = 0; CurMapping < MappingsToCount; CurMapping++) { + Clone -> StreamHeader -> DataUsed += + Clone -> OffsetOut.Mappings [CurMapping].ByteCount; + } +#endif + + // + // 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 !defined(_X86_) + if (Clone -> StreamHeader -> DataUsed >= Clone -> OffsetOut.Remaining) { +#else + if (MappingsRemaining >= Clone -> OffsetOut.Remaining) { +#endif + Clone -> StreamHeader -> Duration = + m_VideoInfoHeader -> AvgTimePerFrame; + + Clone -> StreamHeader -> PresentationTime.Numerator = + Clone -> StreamHeader -> PresentationTime.Denominator = 1; + + // + // If a clock has been assigned, timestamp the packets with the + // time shown on the clock. + // + if (m_Clock) { + + LONGLONG ClockTime = m_Clock -> GetTime (); + + Clone -> StreamHeader -> PresentationTime.Time = ClockTime; + + Clone -> StreamHeader -> OptionsFlags = + KSSTREAM_HEADER_OPTIONSF_TIMEVALID | + KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; + + } else { + // + // If there is no clock, don't time stamp the packets. + // + Clone -> StreamHeader -> PresentationTime.Time = 0; + + } + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + 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; + } + + + // + // If all of the mappings in this clone have been completed, + // delete the clone. We've already updated DataUsed above. + // + +#if !defined(_X86_) + MappingsRemaining--; +#else + MappingsRemaining -= Clone -> OffsetOut.Remaining; +#endif + 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. + // +#if !defined(_X86_) + (void)KsStreamPointerAdvanceOffsets ( + Clone, + 0, + Clone -> StreamHeader -> DataUsed, + FALSE + ); + +#else + (void)KsStreamPointerAdvanceOffsets ( + Clone, + 0, + MappingsRemaining, + FALSE + ); + +#endif + MappingsRemaining = 0; + + } + + // + // Go to the next clone. + // + Clone = NextClone; + + } + + // + // 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) { + m_PendIo = TRUE; + KsPinAttemptProcessing (m_Pin, TRUE); + } + +} + +/************************************************************************** + + DISPATCH AND DESCRIPTOR LAYOUT + +**************************************************************************/ + +// +// FormatRGB24Bpp_Capture: +// +// This is the data range description of the RGB24 capture format we support. +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_Capture = { + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X * D_Y * 3, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7d, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB24, + STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo + }, + + TRUE, // BOOL, bFixedSizeSamples (all samples same size?) + FALSE, // BOOL, bTemporalCompression (all I frames?) + 0, // Reserved (was StreamDescriptionFlags) + 0, // Reserved (was MemoryAllocationFlags + // (KS_VIDEO_ALLOC_*)) + // + // _KS_VIDEO_STREAM_CONFIG_CAPS + // + { + STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID + KS_AnalogVideo_None, // AnalogVideoStandard + D_X,D_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X,D_Y, // MinCroppingSize, smallest rcSrc cropping rect allowed + D_X,D_Y, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X, D_Y, // MinOutputSize, smallest bitmap stream can produce + D_X, D_Y, // MaxOutputSize, largest bitmap stream can produce + 8, // OutputGranularityX, granularity of output bitmap size + 1, // OutputGranularityY; + 0, // StretchTapsX (0 no stretch, 1 pix dup, 2 interp...) + 0, // StretchTapsY + 0, // ShrinkTapsX + 0, // ShrinkTapsY + 333667, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 30 * D_X * D_Y, // MinBitsPerSecond; + 8 * 3 * 30 * D_X * D_Y // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0,0,0,0, // RECT rcSource; + 0,0,0,0, // RECT rcTarget; + D_X * D_Y * 3 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + D_X, // LONG biWidth; + D_Y, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + D_X * D_Y * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +// FormatYUY2_Capture: +// +// This is the data range description of the YUY2 format we support. +// +const +KS_DATARANGE_VIDEO +FormatYUY2_Capture = { + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X * DMAX_Y * 2, // SampleSize + 0, // Reserved + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0x32595559, 0x0000, 0x0010, 0x80, 0x00, + 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, //aka. MEDIASUBTYPE_YUY2, + STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo + }, + + TRUE, // BOOL, bFixedSizeSamples (all samples same size?) + FALSE, // BOOL, bTemporalCompression (all I frames?) + 0, // Reserved (was StreamDescriptionFlags) + 0, // Reserved (was MemoryAllocationFlags + // (KS_VIDEO_ALLOC_*)) + + // + // _KS_VIDEO_STREAM_CONFIG_CAPS + // + { + STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID + KS_AnalogVideo_None, // AnalogVideoStandard + DMAX_X, DMAX_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X,D_Y, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X, DMAX_Y, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X, D_Y, // MinOutputSize, smallest bitmap stream can produce + DMAX_X, DMAX_Y, // MaxOutputSize, largest bitmap stream can produce + 8, // OutputGranularityX, granularity of output bitmap size + 1, // OutputGranularityY; + 0, // StretchTapsX (0 no stretch, 1 pix dup, 2 interp...) + 0, // StretchTapsY + 0, // ShrinkTapsX + 0, // ShrinkTapsY + 333667, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 30 * D_X * D_Y, // MinBitsPerSecond; + 8 * 2 * 30 * DMAX_X * DMAX_Y, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X * DMAX_Y * 2 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + DMAX_X * DMAX_Y * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +// CapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +const +KSPIN_DISPATCH +CapturePinDispatch = { + CCapturePin::DispatchCreate, // Pin Create + NULL, // Pin Close + CCapturePin::DispatchProcess, // Pin Process + NULL, // Pin Reset + CCapturePin::DispatchSetFormat, // Pin Set Data Format + CCapturePin::DispatchSetState, // Pin Set Device State + NULL, // Pin Connect + NULL, // Pin Disconnect + NULL, // Clock Dispatch + NULL // Allocator Dispatch +}; + +// +// CapturePinAllocatorFraming: +// +// This is the simple framing structure for the capture pin. Note that this +// will be modified via KsEdit when the actual capture format is determined. +// +DECLARE_SIMPLE_FRAMING_EX ( + CapturePinAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 2, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE + ); + +// +// CapturePinDataRanges: +// +// This is the list of data ranges supported on the capture pin. We support +// two: one RGB24, and one YUY2. +// +const +PKSDATARANGE +CapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT] = { + (PKSDATARANGE) &FormatYUY2_Capture, + (PKSDATARANGE) &FormatRGB24Bpp_Capture + }; |
