Sample Code
Windows Driver Samples/ AVStream simulated hardware sample driver (Avshws)/ C++/ capture.cpp/
/************************************************************************** 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 (NonPagedPool) 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 ( NonPagedPool, 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 ( &(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 };
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials