summaryrefslogtreecommitdiff
path: root/AVStream/avssamp/video.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'AVStream/avssamp/video.cpp')
-rw-r--r--AVStream/avssamp/video.cpp1433
1 files changed, 1433 insertions, 0 deletions
diff --git a/AVStream/avssamp/video.cpp b/AVStream/avssamp/video.cpp
new file mode 100644
index 00000000..d2291a24
--- /dev/null
+++ b/AVStream/avssamp/video.cpp
@@ -0,0 +1,1433 @@
+/**************************************************************************
+
+ AVStream Filter-Centric Sample
+
+ Copyright (c) 1999 - 2001, Microsoft Corporation
+
+ File:
+
+ video.cpp
+
+ Abstract:
+
+ This file contains the video capture pin implementation.
+
+ History:
+
+ created 6/11/01
+
+**************************************************************************/
+
+#include "avssamp.h"
+
+/**************************************************************************
+
+ PAGEABLE CODE
+
+**************************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg("PAGE")
+#endif // ALLOC_PRAGMA
+
+NTSTATUS
+CVideoCapturePin::
+DispatchCreate (
+ IN PKSPIN Pin,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ Create a new video 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;
+
+ CVideoCapturePin *CapPin = new (NonPagedPool) CVideoCapturePin (Pin);
+ CCapturePin *BasePin = static_cast <CCapturePin *> (CapPin);
+
+ 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> (BasePin),
+ reinterpret_cast <PFNKSFREE> (CCapturePin::BagCleanup)
+ );
+
+ if (!NT_SUCCESS (Status)) {
+ delete CapPin;
+ } else {
+ Pin -> Context = reinterpret_cast <PVOID> (BasePin);
+ }
+
+ }
+
+ //
+ // 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, 'aChS');
+
+ if (NT_SUCCESS (Status)) {
+ Status = KsEdit (
+ Pin,
+ &(Pin -> Descriptor -> AllocatorFraming),
+ 'aChS'
+ );
+ }
+
+ //
+ // If the edits proceeded without running out of memory, adjust
+ // the framing based on the video info header.
+ //
+ 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
+CVideoCapturePin::
+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),
+ AVSSMP_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
+CVideoCapturePin::
+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
+CVideoCapturePin::
+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;
+ CVideoCapturePin *VidCapPin = 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);
+
+ //
+ // We know this pin happens to be the video capture pin. Downcast it.
+ //
+ VidCapPin = static_cast <CVideoCapturePin *> (CapPin);
+ }
+
+ 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 (!VidCapPin -> 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;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CVideoCapturePin::
+Pause (
+ IN KSSTATE FromState
+ )
+
+/*++
+
+Routine Description:
+
+ Called when the pin transitions into the pause state. If we're in an
+ upward transition, start the capture DPC. Note that we do not actually
+ trigger capture in the pause state, but we start up our DPC.
+
+Arguments:
+
+ FromState -
+ The state that the pin is transitioning away from. This is either
+ KSSTATE_ACQUIRE, indicating an upward transition, or KSSTATE_RUN,
+ indicating a downward transition.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ //
+ // On the transition from acquire -> pause, start the timer DPC running.
+ //
+ if (FromState == KSSTATE_ACQUIRE) {
+ m_ParentFilter -> StartDPC (m_VideoInfoHeader -> AvgTimePerFrame);
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CVideoCapturePin::
+Acquire (
+ IN KSSTATE FromState
+ )
+
+/*++
+
+Routine Description:
+
+ This is called from the base class when the video capture pin transitions
+ into the acquire state (from either Stop or Pause). The state the pin
+ transitioned from is passed in.
+
+ During this phase, the video capture pin creates the image synthesizer
+ and initializes it.
+
+Arguments:
+
+ FromState -
+ The state transitioning from (KSSTATE_STOP or KSSTATE_PAUSE)
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ NT_ASSERT (m_VideoInfoHeader);
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (FromState == KSSTATE_STOP) {
+
+ m_SynthesisBuffer = reinterpret_cast <PUCHAR> (
+ ExAllocatePoolWithTag (
+ NonPagedPool,
+ m_VideoInfoHeader -> bmiHeader.biSizeImage,
+ AVSSMP_POOLTAG
+ )
+ );
+
+ if (!m_SynthesisBuffer) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ //
+ // Determine the necessary type of image synthesizer to create
+ // based on the format that has been set on this pin.
+ //
+ if (m_VideoInfoHeader -> bmiHeader.biBitCount == 24 &&
+ m_VideoInfoHeader -> bmiHeader.biCompression == KS_BI_RGB) {
+
+ //
+ // If we're RGB24, create a new RGB24 synth. RGB24 surfaces
+ // can be in either orientation. The origin is lower left if
+ // height < 0. Otherwise, it's upper left.
+ //
+ m_ImageSynth = new (NonPagedPool, 'RysI')
+ CRGB24Synthesizer (
+ m_VideoInfoHeader -> bmiHeader.biHeight >= 0,
+ m_VideoInfoHeader -> bmiHeader.biWidth,
+ ABS (m_VideoInfoHeader -> bmiHeader.biHeight)
+ );
+
+ } else
+ if (m_VideoInfoHeader -> bmiHeader.biBitCount == 16 &&
+ m_VideoInfoHeader -> bmiHeader.biCompression == FOURCC_YUV422) {
+
+ //
+ // If we're UYVY, create the YUV synth.
+ //
+ m_ImageSynth = new (NonPagedPool, 'YysI') CYUVSynthesizer (
+ m_VideoInfoHeader -> bmiHeader.biWidth,
+ m_VideoInfoHeader -> bmiHeader.biHeight
+ );
+
+ } else
+ //
+ // We don't synthesize anything but RGB 24 and UYVY.
+ //
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (NT_SUCCESS (Status) && !m_ImageSynth) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ }
+
+ //
+ // Bag the image synthesizer.
+ //
+ if (NT_SUCCESS (Status)) {
+
+ Status = KsAddItemToObjectBag (
+ m_Pin -> Bag,
+ m_ImageSynth,
+ reinterpret_cast <PFNKSFREE> (CVideoCapturePin::CleanupSynth)
+ );
+
+ }
+
+ //
+ // If everything is okay at this point, inform the synthesizer of
+ // the scratch buffer.
+ //
+ if (NT_SUCCESS (Status)) {
+ m_ImageSynth -> SetBuffer (m_SynthesisBuffer);
+ }
+
+ } else {
+
+ //
+ // The only other state we can come from is pause. If we're in a
+ // downward state transition below pause, tell the filter to stop the
+ // capture DPC.
+ //
+ m_ParentFilter -> StopDPC ();
+
+ }
+
+ return Status;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CVideoCapturePin::
+Stop (
+ IN KSSTATE FromState
+ )
+
+/*++
+
+Routine Description:
+
+ Called when the video capture pin transitions from acquire to stop.
+ This function will clean up the image synth and any data structures
+ that we need to clean up on stop.
+
+Arguments:
+
+ FromState -
+ The state the pin is transitioning away from. This should
+ always be KSSTATE_ACQUIRE for this call.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ PAGED_CODE();
+
+ NT_ASSERT (FromState == KSSTATE_ACQUIRE);
+
+ //
+ // Remove the image synthesizer from the object bag and free it.
+ //
+ KsRemoveItemFromObjectBag (
+ m_Pin -> Bag,
+ m_ImageSynth,
+ TRUE
+ );
+
+ m_ImageSynth = NULL;
+
+ if (m_SynthesisBuffer) {
+ ExFreePool (m_SynthesisBuffer);
+ m_SynthesisBuffer = NULL;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+/**************************************************************************
+
+ LOCKED CODE
+
+**************************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg()
+#endif // ALLOC_PRAGMA
+
+
+NTSTATUS
+CVideoCapturePin::
+CaptureFrame (
+ IN PKSPROCESSPIN ProcessPin,
+ IN ULONG Tick
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the filter processing function to capture
+ a frame for the video capture pin. The process pin to capture to is
+ passed.
+
+Arguments:
+
+ ProcessPin -
+ The process pin associated with this pin.
+
+ Tick -
+ The tick count on the filter. This is the number of timer DPC's that
+ have fired since the timer DPC started.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+
+ NT_ASSERT (ProcessPin -> Pin == m_Pin);
+
+ //
+ // Increment the frame number. This is the total count of frames which
+ // have attempted capture.
+ //
+ m_FrameNumber++;
+
+ //
+ // Since this pin is KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING, it
+ // means that we do not require frames available in order to process.
+ // This means that this routine can get called from our DPC with no
+ // buffers available to capture into. In this case, we increment our
+ // dropped frame counter and do nothing.
+ //
+ if (ProcessPin -> BytesAvailable) {
+
+ //
+ // Because we adjusted the allocator framing, each frame should be
+ // sufficient to trigger capture of the appropriate buffer size.
+ //
+ NT_ASSERT (ProcessPin -> BytesAvailable >=
+ m_VideoInfoHeader -> bmiHeader.biSizeImage);
+
+ //
+ // If we get an invalid buffer, kick it out.
+ //
+ if (ProcessPin -> BytesAvailable <
+ m_VideoInfoHeader -> bmiHeader.biSizeImage) {
+
+ ProcessPin -> BytesUsed = 0;
+ ProcessPin -> Terminate = TRUE;
+ m_DroppedFrames++;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Generate a synthesized image.
+ //
+ m_ImageSynth -> SynthesizeBars ();
+
+ //
+ // Overlay some activity onto the bars.
+ //
+ ULONG DropLength = (Tick * 2) %
+ (ABS (m_VideoInfoHeader -> bmiHeader.biHeight));
+
+ //
+ // Create a drop flowing down DropLength lines from the top of the
+ // image.
+ //
+ m_ImageSynth -> Fill (
+ 0, 0,
+ m_VideoInfoHeader -> bmiHeader.biWidth - 1, DropLength,
+ GREEN
+ );
+
+ //
+ // Overlay the dropped frame count over the image.
+ //
+ char Text [256];
+ Text[0] = '\0';
+ RtlStringCbPrintfA(Text, sizeof(Text), "Video Skipped: %ld", m_DroppedFrames);
+
+ m_ImageSynth -> OverlayText (
+ 10,
+ 10,
+ 1,
+ Text,
+ TRANSPARENT,
+ BLUE
+ );
+
+ //
+ // This is used to indicate that there is no audio pin.
+ //
+ if (m_NotifyAudDrop != (ULONG)-1) {
+ RtlStringCbPrintfA(Text, sizeof(Text), "Audio Skipped: %ld", m_NotifyAudDrop);
+
+ m_ImageSynth -> OverlayText (
+ 10,
+ 20,
+ 1,
+ Text,
+ TRANSPARENT,
+ BLUE
+ );
+ }
+
+ //
+ // Copy the synthesized image into the buffer.
+ //
+ RtlCopyMemory (
+ ProcessPin -> Data,
+ m_SynthesisBuffer,
+ m_VideoInfoHeader -> bmiHeader.biSizeImage
+ );
+
+ ProcessPin -> BytesUsed = m_VideoInfoHeader -> bmiHeader.biSizeImage;
+ ProcessPin -> Terminate = TRUE;
+
+
+ PKSSTREAM_HEADER StreamHeader =
+ ProcessPin -> StreamPointer -> StreamHeader;
+
+ //
+ // If there is a clock assigned to the pin, time stamp the sample.
+ //
+ if (m_Clock) {
+
+ StreamHeader -> PresentationTime.Time = GetTime ();
+ StreamHeader -> Duration = m_VideoInfoHeader -> AvgTimePerFrame;
+
+ StreamHeader -> OptionsFlags =
+ KSSTREAM_HEADER_OPTIONSF_TIMEVALID |
+ KSSTREAM_HEADER_OPTIONSF_DURATIONVALID;
+
+ }
+
+ //
+ // Update the extended header info.
+ //
+ NT_ASSERT (StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) +
+ sizeof (KS_FRAME_INFO));
+
+ //
+ // 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 (StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) +
+ sizeof (KS_FRAME_INFO)) {
+
+ PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (
+ StreamHeader + 1
+ );
+
+ FrameInfo -> ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
+ FrameInfo -> PictureNumber = (LONGLONG)m_FrameNumber;
+ FrameInfo -> DropCount = (LONGLONG)m_DroppedFrames;
+
+ }
+
+ } else {
+ m_DroppedFrames++;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+/**************************************************************************
+
+ DESCRIPTOR AND DISPATCH LAYOUT
+
+**************************************************************************/
+
+#define D_X 320
+#define D_Y 240
+
+//
+// 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?)
+ TRUE, // 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_NTSC_M |
+ KS_AnalogVideo_PAL_B, // AnalogVideoStandard
+ 720,480, // InputSize, (the inherent size of the incoming signal
+ // with every digitized pixel unique)
+ 160,120, // MinCroppingSize, smallest rcSrc cropping rect allowed
+ 720,480, // MaxCroppingSize, largest rcSrc cropping rect allowed
+ 8, // CropGranularityX, granularity of cropping size
+ 1, // CropGranularityY
+ 8, // CropAlignX, alignment of cropping rect
+ 1, // CropAlignY;
+ 160, 120, // MinOutputSize, smallest bitmap stream can produce
+ 720, 480, // 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 * 160 * 120, // MinBitsPerSecond;
+ 8 * 3 * 30 * 720 * 480 // MaxBitsPerSecond;
+ },
+
+ //
+ // KS_VIDEOINFOHEADER (default format)
+ //
+ {
+ 0,0,0,0, // RECT rcSource;
+ 0,0,0,0, // RECT rcTarget;
+ D_X * D_Y * 3 * 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;
+ }
+};
+
+#undef D_X
+#undef D_Y
+
+#define D_X 320
+#define D_Y 240
+
+//
+// FormatUYU2_Capture:
+//
+// This is the data range description of the UYVY format we support.
+//
+const
+KS_DATARANGE_VIDEO
+FormatUYU2_Capture = {
+
+ //
+ // KSDATARANGE
+ //
+ {
+ sizeof (KS_DATARANGE_VIDEO), // FormatSize
+ 0, // Flags
+ D_X * D_Y * 2, // SampleSize
+ 0, // Reserved
+ STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video
+ 0x59565955, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa,
+ 0x00, 0x38, 0x9b, 0x71, // aka. MEDIASUBTYPE_UYVY,
+ STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo
+ },
+
+ TRUE, // BOOL, bFixedSizeSamples (all samples same size?)
+ TRUE, // 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_NTSC_M |
+ KS_AnalogVideo_PAL_B, // AnalogVideoStandard
+ 720,480, // InputSize, (the inherent size of the incoming signal
+ // with every digitized pixel unique)
+ 160,120, // MinCroppingSize, smallest rcSrc cropping rect allowed
+ 720,480, // MaxCroppingSize, largest rcSrc cropping rect allowed
+ 8, // CropGranularityX, granularity of cropping size
+ 1, // CropGranularityY
+ 8, // CropAlignX, alignment of cropping rect
+ 1, // CropAlignY;
+ 160, 120, // MinOutputSize, smallest bitmap stream can produce
+ 720, 480, // 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 * 160 * 120, // MinBitsPerSecond;
+ 8 * 2 * 30 * 720 * 480 // MaxBitsPerSecond;
+ },
+
+ //
+ // KS_VIDEOINFOHEADER (default format)
+ //
+ {
+ 0,0,0,0, // RECT rcSource;
+ 0,0,0,0, // RECT rcTarget;
+ D_X * D_Y * 2 * 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;
+ 16, // WORD biBitCount;
+ FOURCC_YUV422, // DWORD biCompression;
+ D_X * D_Y * 2, // DWORD biSizeImage;
+ 0, // LONG biXPelsPerMeter;
+ 0, // LONG biYPelsPerMeter;
+ 0, // DWORD biClrUsed;
+ 0 // DWORD biClrImportant;
+ }
+};
+
+//
+// VideoCapturePinDispatch:
+//
+// This is the dispatch table for the capture pin. It provides notifications
+// about creation, closure, processing, data formats, etc...
+//
+const
+KSPIN_DISPATCH
+VideoCapturePinDispatch = {
+ CVideoCapturePin::DispatchCreate, // Pin Create
+ NULL, // Pin Close
+ NULL, // Pin Process
+ NULL, // Pin Reset
+ CVideoCapturePin::DispatchSetFormat, // Pin Set Data Format
+ CCapturePin::DispatchSetState, // Pin Set Device State
+ NULL, // Pin Connect
+ NULL, // Pin Disconnect
+ NULL, // Clock Dispatch
+ NULL // Allocator Dispatch
+};
+
+//
+// VideoCapturePinAllocatorFraming:
+//
+// 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 (
+ VideoCapturePinAllocatorFraming,
+ STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED),
+ KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
+ KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY,
+ 2,
+ 0,
+ 2 * PAGE_SIZE,
+ 2 * PAGE_SIZE
+ );
+
+//
+// VideoCapturePinDataRanges:
+//
+// This is the list of data ranges supported on the capture pin. We support
+// two: one RGB24, and one UYVY.
+//
+const
+PKSDATARANGE
+VideoCapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT] = {
+ (PKSDATARANGE) &FormatRGB24Bpp_Capture,
+ (PKSDATARANGE) &FormatUYU2_Capture
+ };
+