summaryrefslogtreecommitdiff
path: root/avstream/avssamp/Filter.cpp
diff options
context:
space:
mode:
authorJ M Rossy <[email protected]>2015-07-29 15:40:09 -0700
committerJ M Rossy <[email protected]>2015-07-29 16:34:40 -0700
commit5d61a4a79a1e96dc5d9af1e5e712c84507307544 (patch)
tree49ed270893578cd18758b74ffcca16dc7ab948d6 /avstream/avssamp/Filter.cpp
parent5d41613e26e147d2300c786bb2b34cb4b4067a25 (diff)
Samples update for public Windows 10 release
Fix #2 Enabling WPP Recorder in Sensors Samples causes errors Fix #4 Add back fixed KMDOD sample Add new BarcodeScanner sample in pos folder Add new MagneticStripeReader sample in pos folder Add new SynpaticsTouch sample in input folder Add new Power Engine Plugin sample in pofx folder Add new DeviceMft sample in avstream folder Add new AvsCamera sample in root Add new SimBatt sample in root Add other pre-existing samples not yet released for Win10
Diffstat (limited to 'avstream/avssamp/Filter.cpp')
-rw-r--r--avstream/avssamp/Filter.cpp836
1 files changed, 836 insertions, 0 deletions
diff --git a/avstream/avssamp/Filter.cpp b/avstream/avssamp/Filter.cpp
new file mode 100644
index 00000000..524d6ad8
--- /dev/null
+++ b/avstream/avssamp/Filter.cpp
@@ -0,0 +1,836 @@
+/**************************************************************************
+
+ AVStream Filter-Centric Sample
+
+ Copyright (c) 1999 - 2001, Microsoft Corporation
+
+ File:
+
+ filter.cpp
+
+ Abstract:
+
+ This file contails the capture filter implementation (including
+ frame synthesis) for the fake capture filter.
+
+ History:
+
+ created 5/31/01
+
+**************************************************************************/
+
+#include "avssamp.h"
+
+//
+// TimerRoutine():
+//
+// This is the timer routine called every 1/Nth of a second to trigger
+// capture by the filter.
+//
+KDEFERRED_ROUTINE TimerRoutine;
+void
+TimerRoutine (
+ IN PKDPC Dpc,
+ IN PVOID This,
+ IN PVOID SystemArg1,
+ IN PVOID SystemArg2
+ )
+{
+ CCaptureFilter *pCCaptureFilter = (CCaptureFilter*)This;
+ if (pCCaptureFilter)
+ {
+ pCCaptureFilter -> TimerDpc ();
+ }
+}
+
+
+/**************************************************************************
+
+ PAGEABLE CODE
+
+**************************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg("PAGE")
+#endif // ALLOC_PRAGMA
+
+
+CCaptureFilter::
+CCaptureFilter (
+ IN PKSFILTER Filter
+ ) :
+ m_Filter (Filter)
+
+/*++
+
+Routine Description:
+
+ This is the constructor for the capture filter. It initializes all the
+ structures necessary to kick off timer DPC's for capture.
+
+Arguments:
+
+ Filter -
+ The AVStream filter being created.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // Initialize the DPC's, timers, and events necessary to cause a
+ // capture trigger to happen.
+ //
+ KeInitializeDpc (
+ &m_TimerDpc,
+ TimerRoutine,
+ this
+ );
+
+ KeInitializeEvent (
+ &m_StopDPCEvent,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ KeInitializeTimer (&m_Timer);
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CCaptureFilter::
+DispatchCreate (
+ IN PKSFILTER Filter,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This is the creation dispatch for the capture filter. It creates
+ the CCaptureFilter object, associates it with the AVStream filter
+ object, and bag the CCaptureFilter for later cleanup.
+
+Arguments:
+
+ Filter -
+ The AVStream filter being created
+
+ Irp -
+ The creation Irp
+
+Return Value:
+
+ Success / failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ CCaptureFilter *CapFilter = new (NonPagedPool) CCaptureFilter (Filter);
+
+ if (!CapFilter) {
+ //
+ // Return failure if we couldn't create the filter.
+ //
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ } else {
+ //
+ // Add the item to the object bag if we we were successful.
+ // Whenever the filter closes, the bag is cleaned up and we will be
+ // freed.
+ //
+ Status = KsAddItemToObjectBag (
+ Filter -> Bag,
+ reinterpret_cast <PVOID> (CapFilter),
+ reinterpret_cast <PFNKSFREE> (CCaptureFilter::Cleanup)
+ );
+
+ if (!NT_SUCCESS (Status)) {
+ delete CapFilter;
+ } else {
+ Filter -> Context = reinterpret_cast <PVOID> (CapFilter);
+ }
+
+ }
+
+ //
+ // Create the wave reader. We need it at this point because the data
+ // ranges exposed on the audio pin need to change dynamically right
+ // now.
+ //
+ if (NT_SUCCESS (Status)) {
+
+ CapFilter -> m_WaveObject =
+ new (NonPagedPool, 'evaW') CWaveObject (
+ L"\\DosDevices\\c:\\avssamp.wav"
+ );
+
+ if (!CapFilter -> m_WaveObject) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ Status = CapFilter -> m_WaveObject -> ParseAndRead ();
+
+ //
+ // If the file cannot be found, don't fail to create the filter.
+ // This simply means that audio cannot be synthesized.
+ //
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
+ Status == STATUS_ACCESS_DENIED) {
+ delete CapFilter -> m_WaveObject;
+ CapFilter -> m_WaveObject = NULL;
+ Status = STATUS_SUCCESS;
+ }
+
+ }
+
+ }
+
+ if (NT_SUCCESS (Status) && CapFilter -> m_WaveObject) {
+ //
+ // Add the wave object to the filter's bag for auto-cleanup.
+ //
+ Status = KsAddItemToObjectBag (
+ Filter -> Bag,
+ reinterpret_cast <PVOID> (CapFilter -> m_WaveObject),
+ reinterpret_cast <PFNKSFREE> (CWaveObject::Cleanup)
+ );
+
+ if (!NT_SUCCESS (Status)) {
+ delete CapFilter -> m_WaveObject;
+ CapFilter -> m_WaveObject = NULL;
+ } else {
+ Status = CapFilter -> BindAudioToWaveObject ();
+ }
+ }
+
+ return Status;
+
+}
+
+/*************************************************/
+
+NTSTATUS
+CCaptureFilter::
+BindAudioToWaveObject (
+ )
+
+/*++
+
+Routine Description:
+
+ Create an audio pin directly bound to m_WaveObject (aka: it only exposes
+ the format (channels, frequency, etc...) that m_WaveObject represents.
+ This will actually create a pin on the filter dynamically.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ NT_ASSERT (m_WaveObject);
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Build a pin descriptor from the template. This descriptor is
+ // temporary scratch space because the call to AVStream to create the
+ // pin will actually duplicate the descriptor.
+ //
+ KSPIN_DESCRIPTOR_EX PinDescriptor = AudioPinDescriptorTemplate;
+
+ //
+ // The data range must be dynamically created since we're basing it
+ // on dynamic reading of a wave file!
+ //
+ PKSDATARANGE_AUDIO DataRangeAudio =
+ reinterpret_cast <PKSDATARANGE_AUDIO> (
+ ExAllocatePoolWithTag (PagedPool, sizeof (KSDATARANGE_AUDIO), AVSSMP_POOLTAG)
+ );
+
+ PKSDATARANGE_AUDIO *DataRanges =
+ reinterpret_cast <PKSDATARANGE_AUDIO *> (
+ ExAllocatePoolWithTag (PagedPool, sizeof (PKSDATARANGE_AUDIO), AVSSMP_POOLTAG)
+ );
+
+ PKSALLOCATOR_FRAMING_EX Framing =
+ reinterpret_cast <PKSALLOCATOR_FRAMING_EX> (
+ ExAllocatePoolWithTag (PagedPool, sizeof (KSALLOCATOR_FRAMING_EX), AVSSMP_POOLTAG)
+ );
+
+ if (DataRangeAudio && DataRanges && Framing) {
+ DataRangeAudio -> DataRange.FormatSize = sizeof (KSDATARANGE_AUDIO);
+ DataRangeAudio -> DataRange.Flags = 0;
+ DataRangeAudio -> DataRange.SampleSize = 0;
+ DataRangeAudio -> DataRange.Reserved = 0;
+ DataRangeAudio -> DataRange.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
+ DataRangeAudio -> DataRange.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ DataRangeAudio -> DataRange.Specifier =
+ KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
+
+ m_WaveObject -> WriteRange (DataRangeAudio);
+
+ *DataRanges = DataRangeAudio;
+
+ } else {
+ if (DataRangeAudio) {
+ ExFreePool (DataRangeAudio);
+ DataRangeAudio = NULL;
+ }
+ if (DataRanges) {
+ ExFreePool (DataRanges);
+ DataRanges = NULL;
+ }
+ if (Framing) {
+ ExFreePool (Framing);
+ Framing = NULL;
+ }
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (NT_SUCCESS (Status)) {
+ //
+ // Bag the newly created range information in the filter's bag since
+ // this will be alive for the lifetime of the filter.
+ //
+ Status = KsAddItemToObjectBag (
+ m_Filter -> Bag,
+ DataRangeAudio,
+ NULL
+ );
+
+ if (!NT_SUCCESS (Status)) {
+ ExFreePool (DataRangeAudio);
+ ExFreePool (DataRanges);
+ ExFreePool (Framing);
+ }
+
+ }
+
+ if (NT_SUCCESS (Status)) {
+
+ Status = KsAddItemToObjectBag (
+ m_Filter -> Bag,
+ DataRanges,
+ NULL
+ );
+
+ if (!NT_SUCCESS (Status)) {
+ ExFreePool (DataRanges);
+ ExFreePool (Framing);
+ }
+
+ }
+
+ if (NT_SUCCESS (Status)) {
+
+ Status = KsAddItemToObjectBag (
+ m_Filter -> Bag,
+ Framing,
+ NULL
+ );
+
+ if (!NT_SUCCESS (Status)) {
+ ExFreePool (Framing);
+ }
+
+ }
+
+ if (NT_SUCCESS (Status)) {
+ //
+ // The physical and optimal ranges must block aligned and
+ // the size of 1/(fps) * bytes_per_sec in size. It's true
+ // that we don't know the frame rate at this point due
+ // to the fact that the video pin doesn't exist yet; however, that
+ // would also be true if this were edited at audio pin creation.
+ //
+ // Thus, we instead adjust the allocator for the minimum frame rate
+ // we support (which is 1/30 of a second).
+ //
+ *Framing = *PinDescriptor.AllocatorFraming;
+
+ Framing -> FramingItem [0].PhysicalRange.MinFrameSize =
+ Framing -> FramingItem [0].PhysicalRange.MaxFrameSize =
+ Framing -> FramingItem [0].FramingRange.Range.MinFrameSize =
+ Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize =
+ ((DataRangeAudio -> MaximumSampleFrequency *
+ DataRangeAudio -> MaximumBitsPerSample *
+ DataRangeAudio -> MaximumChannels) + 29) / 30;
+
+ Framing -> FramingItem [0].PhysicalRange.Stepping =
+ Framing -> FramingItem [0].FramingRange.Range.Stepping =
+ 0;
+
+ PinDescriptor.AllocatorFraming = Framing;
+
+ PinDescriptor.PinDescriptor.DataRangesCount = 1;
+ PinDescriptor.PinDescriptor.DataRanges =
+ reinterpret_cast <const PKSDATARANGE *> (DataRanges);
+
+ //
+ // Create the actual pin. We need to save the pin id returned. It
+ // is how we refer to the audio pin in the future.
+ //
+ Status = KsFilterCreatePinFactory (
+ m_Filter,
+ &PinDescriptor,
+ &m_AudioPinId
+ );
+
+ }
+
+ return Status;
+
+}
+
+
+/*************************************************/
+
+
+void
+CCaptureFilter::
+StartDPC (
+ IN LONGLONG TimerInterval
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts the timer DPC running at a specified interval. The
+ specified interval is the amount of time between triggering frame captures.
+ Once this routine returns, the timer DPC should be running and attempting
+ to trigger processing on the capture filter as a whole.
+
+Arguments:
+
+ TimerInterval -
+ The amount of time between timer DPC's. This is the amount of delay
+ between one frame and the next. Since the DPC is driven off the
+ video capture pin, this should be an amount of time specified by
+ the video info header.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ //
+ // Initialize any variables used by the timer DPC.
+ //
+ m_Tick = 0;
+ m_TimerInterval = TimerInterval;
+ KeQuerySystemTime (&m_StartTime);
+
+ //
+ // Schedule the DPC to happen one frame time from now.
+ //
+ LARGE_INTEGER NextTime;
+ NextTime.QuadPart = m_StartTime.QuadPart + m_TimerInterval;
+
+ KeSetTimer (&m_Timer, NextTime, &m_TimerDpc);
+
+}
+
+/*************************************************/
+
+
+void
+CCaptureFilter::
+StopDPC (
+ )
+
+/*++
+
+Routine Description:
+
+ Stop the timer DPC from firing. After this routine returns, there is
+ a guarantee that no more timer DPC's will fire and no more processing
+ attempts will occur. Note that this routine does block.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ m_StoppingDPC = TRUE;
+
+ KeWaitForSingleObject (
+ &m_StopDPCEvent,
+ Suspended,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ NT_ASSERT (m_StoppingDPC == FALSE);
+
+}
+
+/**************************************************************************
+
+ LOCKED CODE
+
+**************************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg()
+#endif // ALLOC_PRAGMA
+
+
+LONGLONG
+CCaptureFilter::
+GetTimerInterval (
+ )
+
+/*++
+
+Routine Description:
+
+ Return the timer interval being used to fire DPC's.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ The timer interval being used to fire DPC's.
+
+--*/
+
+{
+
+ return m_TimerInterval;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CCaptureFilter::
+Process (
+ IN PKSPROCESSPIN_INDEXENTRY ProcessPinsIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This is the processing function for the capture filter. It is responsible
+ for copying synthesized image data into the image buffers. The timer DPC
+ will attempt to trigger processing (and hence indirectly call this routine)
+ to trigger a capture.
+
+Arguments:
+
+ ProcessPinsIndex -
+ Contains a pointer to an array of process pin index entries. This
+ array is indexed by pin ID. An index entry indicates the number
+ of pin instances for the corresponding filter type and points to the
+ first corresponding process pin structure in the ProcessPins array.
+ This allows the process pin structure to be quickly accessed by pin ID
+ when the number of instances per type is not known in advance.
+
+Return Value:
+
+ Indication of whether more processing should be done if frames are
+ available. A value of STATUS_PENDING indicates that processing should not
+ continue even if frames are available on all required queues.
+ STATUS_SUCCESS indicates processing should continue if frames are available
+ on all required queues.
+
+--*/
+
+{
+
+ //
+ // The audio and video pins do not necessarily need to exist (one could
+ // be capturing video w/o audio or vice-versa). Do not assume the
+ // existence by checking Index[ID].Pins[0]. Always check the Count
+ // field first.
+ //
+ PKSPROCESSPIN VideoPin = NULL;
+ CCapturePin *VidCapPin = NULL;
+ PKSPROCESSPIN AudioPin = NULL;
+ CCapturePin *AudCapPin = NULL;
+ ULONG VidCapDrop = 0;
+ ULONG AudCapDrop = (ULONG)-1;
+
+ if (ProcessPinsIndex [VIDEO_PIN_ID].Count != 0) {
+ //
+ // There can be at most one instance via the possible instances field,
+ // so the below is safe.
+ //
+ VideoPin = ProcessPinsIndex [VIDEO_PIN_ID].Pins [0];
+ VidCapPin =
+ reinterpret_cast <CCapturePin *> (VideoPin -> Pin -> Context);
+ }
+
+ //
+ // The audio pin only exists on the filter if the wave object does.
+ // They're tied together at filter create time.
+ //
+ if (m_WaveObject && ProcessPinsIndex [m_AudioPinId].Count != 0) {
+ //
+ // There can be at most one instance via the possible instances field,
+ // so the below is safe.
+ //
+ AudioPin = ProcessPinsIndex [m_AudioPinId].Pins [0];
+ AudCapPin =
+ reinterpret_cast <CCapturePin *> (AudioPin -> Pin -> Context);
+ }
+
+ if (VidCapPin) {
+ VidCapDrop = VidCapPin -> QueryFrameDrop ();
+ }
+
+ if (AudCapPin) {
+ AudCapDrop = AudCapPin -> QueryFrameDrop ();
+ }
+
+ //
+ // If there's a video pin around, trigger capture on it. We call the
+ // pin object to actually synthesize the frame; however, we could just
+ // as easily have done that here.
+ //
+ if (VidCapPin) {
+ //
+ // This is used to notify the pin how many frames have been dropped
+ // on each pin to allow that to be rendered.
+ //
+ VidCapPin -> NotifyDrops (VidCapDrop, AudCapDrop);
+ VidCapPin -> CaptureFrame (VideoPin, m_Tick);
+ }
+
+ //
+ // If there's an audio pin around, trigger capture on it. Since the
+ // audio capture pin isn't necessary for capture, there might be an
+ // instance which is connected and is in the stop state when we get
+ // called [there will never be one in acquire or pause since we specify
+ // KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY]. Don't bother triggering capture
+ // on the pin unless it's actually running.
+ //
+ // On DX8.x platforms, the Pin -> ClientState field does not exist.
+ // Hence, we check the state we maintain ourselves. DeviceState is not
+ // the right thing to check here.
+ //
+ if (AudCapPin && AudCapPin -> GetState () == KSSTATE_RUN) {
+ AudCapPin -> CaptureFrame (AudioPin, m_Tick);
+ }
+
+ //
+ // STATUS_PENDING indicates that we do not want to be called back if
+ // there is more data available. We only want to trigger processing
+ // (and hence capture) on the timer ticks.
+ //
+ return STATUS_PENDING;
+
+}
+
+/*************************************************/
+
+
+void
+CCaptureFilter::
+TimerDpc (
+ )
+
+/*++
+
+Routine Description:
+
+ This is the timer function for our timer (bridged to from TimerRoutine
+ in the context of the appropriate CCaptureFilter). It is called every
+ 1/Nth of a second as specified in StartDpc() to trigger capture of a video
+ frame.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+
+--*/
+
+{
+
+ //
+ // Increment the tick counter. This keeps track of the number of ticks
+ // that have happened since the timer DPC started running. Note that the
+ // timer DPC starts running before the pins go into run state and this
+ // variable gets incremented from the original start point.
+ //
+ m_Tick++;
+
+ //
+ // Trigger processing on the filter. Since the filter is prepared to
+ // run at DPC, we do not request asynchronous processing. Thus, if
+ // possible, processing will occur in the context of this DPC.
+ //
+ KsFilterAttemptProcessing (m_Filter, FALSE);
+
+ //
+ // Reschedule the timer if the hardware isn't being stopped.
+ //
+ if (!m_StoppingDPC) {
+
+ LARGE_INTEGER NextTime;
+
+ NextTime.QuadPart = m_StartTime.QuadPart +
+ (m_TimerInterval * (m_Tick + 1));
+
+ KeSetTimer (&m_Timer, NextTime, &m_TimerDpc);
+
+ } else {
+
+ //
+ // If another thread is waiting on the DPC to stop running, raise
+ // the stop event and clear the flag.
+ //
+ m_StoppingDPC = FALSE;
+ KeSetEvent (&m_StopDPCEvent, IO_NO_INCREMENT, FALSE);
+
+ }
+
+}
+
+/**************************************************************************
+
+ DESCRIPTOR AND DISPATCH LAYOUT
+
+**************************************************************************/
+
+GUID g_PINNAME_VIDEO_CAPTURE = {STATIC_PINNAME_VIDEO_CAPTURE};
+
+//
+// CaptureFilterCategories:
+//
+// The list of category GUIDs for the capture filter.
+//
+const
+GUID
+CaptureFilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT] = {
+ STATICGUIDOF (KSCATEGORY_VIDEO),
+ STATICGUIDOF (KSCATEGORY_CAPTURE)
+};
+
+//
+// CaptureFilterPinDescriptors:
+//
+// The list of pin descriptors on the capture filter.
+//
+const
+KSPIN_DESCRIPTOR_EX
+CaptureFilterPinDescriptors [CAPTURE_FILTER_PIN_COUNT] = {
+ //
+ // Video Capture Pin
+ //
+ {
+ &VideoCapturePinDispatch,
+ NULL,
+ {
+ NULL, // Interfaces (NULL, 0 == default)
+ 0,
+ NULL, // Mediums (NULL, 0 == default)
+ 0,
+ SIZEOF_ARRAY (VideoCapturePinDataRanges), // Range Count
+ VideoCapturePinDataRanges, // Ranges
+ KSPIN_DATAFLOW_OUT, // Dataflow
+ KSPIN_COMMUNICATION_BOTH, // Communication
+ &KSCATEGORY_VIDEO, // Category
+ &g_PINNAME_VIDEO_CAPTURE, // Name
+ 0 // Reserved
+ },
+ KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING | // Flags
+ KSPIN_FLAG_DO_NOT_INITIATE_PROCESSING |
+ KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY,
+ 1, // Instances Possible
+ 1, // Instances Necessary
+ &VideoCapturePinAllocatorFraming, // Allocator Framing
+ reinterpret_cast <PFNKSINTERSECTHANDLEREX>
+ (CVideoCapturePin::IntersectHandler)
+ }
+};
+
+//
+// CaptureFilterDispatch:
+//
+// This is the dispatch table for the capture filter. It provides notification
+// of creation, closure, processing, and resets.
+//
+const
+KSFILTER_DISPATCH
+CaptureFilterDispatch = {
+ CCaptureFilter::DispatchCreate, // Filter Create
+ NULL, // Filter Close
+ CCaptureFilter::DispatchProcess, // Filter Process
+ NULL // Filter Reset
+};
+
+//
+// CaptureFilterDescription:
+//
+// The descriptor for the capture filter. We don't specify any topology
+// since there's only one pin on the filter. Realistically, there would
+// be some topological relationships here because there would be input
+// pins from crossbars and the like.
+//
+const
+KSFILTER_DESCRIPTOR
+CaptureFilterDescriptor = {
+ &CaptureFilterDispatch, // Dispatch Table
+ NULL, // Automation Table
+ KSFILTER_DESCRIPTOR_VERSION, // Version
+ KSFILTER_FLAG_DISPATCH_LEVEL_PROCESSING,// Flags
+ &KSNAME_Filter, // Reference GUID
+ DEFINE_KSFILTER_PIN_DESCRIPTORS (CaptureFilterPinDescriptors),
+ DEFINE_KSFILTER_CATEGORIES (CaptureFilterCategories),
+
+ DEFINE_KSFILTER_NODE_DESCRIPTORS_NULL,
+ DEFINE_KSFILTER_DEFAULT_CONNECTIONS,
+
+ NULL // Component ID
+};
+
+