Sample Code

Windows Driver Samples/ AVStream filter-centric simulated capture sample driver (Avssamp)/ C++/ Filter.cpp/

/**************************************************************************

    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
};


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS