diff options
Diffstat (limited to 'AVStream/avssamp/Filter.cpp')
| -rw-r--r-- | AVStream/avssamp/Filter.cpp | 836 |
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 +}; + + |
