diff options
| author | Wei Mao <[email protected]> | 2017-03-17 19:47:04 -0700 |
|---|---|---|
| committer | Wei Mao <[email protected]> | 2017-03-17 19:47:04 -0700 |
| commit | 1a3e0d580380e58bf336a242d2affc8a1e2d1ddf (patch) | |
| tree | bf5d9c5b0b4cba1b81726b9f78c4d5ff5c636fea /AVStream/avssamp | |
| parent | da21c8784c83c5fd614f3030323e229d6a5fb10e (diff) | |
Fix cases
Diffstat (limited to 'AVStream/avssamp')
| -rw-r--r-- | AVStream/avssamp/Filter.cpp | 836 | ||||
| -rw-r--r-- | AVStream/avssamp/README.md | 51 | ||||
| -rw-r--r-- | AVStream/avssamp/audio.cpp | 655 | ||||
| -rw-r--r-- | AVStream/avssamp/audio.h | 150 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.cpp | 243 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.h | 252 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.inf | 93 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.rc | 22 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.sln | 28 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.vcxproj | 202 | ||||
| -rw-r--r-- | AVStream/avssamp/avssamp.vcxproj.Filters | 52 | ||||
| -rw-r--r-- | AVStream/avssamp/capture.cpp | 249 | ||||
| -rw-r--r-- | AVStream/avssamp/capture.h | 284 | ||||
| -rw-r--r-- | AVStream/avssamp/filter.h | 253 | ||||
| -rw-r--r-- | AVStream/avssamp/image.cpp | 647 | ||||
| -rw-r--r-- | AVStream/avssamp/image.h | 476 | ||||
| -rw-r--r-- | AVStream/avssamp/purecall.c | 50 | ||||
| -rw-r--r-- | AVStream/avssamp/video.cpp | 1433 | ||||
| -rw-r--r-- | AVStream/avssamp/video.h | 221 | ||||
| -rw-r--r-- | AVStream/avssamp/wave.cpp | 598 | ||||
| -rw-r--r-- | AVStream/avssamp/wave.h | 192 |
21 files changed, 6987 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 +}; + + diff --git a/AVStream/avssamp/README.md b/AVStream/avssamp/README.md new file mode 100644 index 00000000..6188c5b3 --- /dev/null +++ b/AVStream/avssamp/README.md @@ -0,0 +1,51 @@ +AVStream filter-centric simulated capture sample driver (Avssamp) +================================================================= + +The AVStream filter-centric simulated capture sample driver (Avssamp) provides a filter-centric [AVStream](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554240) capture driver with functional audio. This streaming media driver performs video captures at 320 x 240 pixel resolution in RGB24 or YUV422 format while playing a user-provided Pulse Code Modulation (PCM) wave audio file in a loop. The sample demonstrates how to write a filter-centric AVStream minidriver. + + +Installation instructions +------------------------- + +1. Copy AVssamp.inf to a directory, for example, C:\\Avstream\\. +2. In this directory, create a new subdirectory named objfre\_x86 if the target operating system is x86-based, or objfre\_amd64 for an x64-based target operating system, for example, C:\\AVstream\\objfre\_x86\\. +3. Copy the processor-appropriate Avssamp.sys file to the objfre\_\* directory. +4. Start a command prompt with administrator privilege and run the processor-specific WDK tool Devcon.exe to launch the installation. For example: + + `C:\WinDDK\7600.16384.0\tools\devcon\i386\devcon.exe install C:\AVstream\avssamp.inf SW\{20698827-7099-4c4e-861A-4879D639A35F}` + +Programming Tour +---------------- + +[**DriverEntry**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558717) in Avssamp.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling [**KsInitializeDriver**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562683). In this call, the minidriver passes the device descriptor, an AVStream structure that recursively defines the AVStream object hierarchy for a driver. This is common behavior for an AVStream minidriver. + +Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563534) structure for the single capture pin. Audio.cpp contains the **KSPIN\_DESCRIPTOR\_EX** structure for the audio capture pin. This pin is dynamically created only if C:\\avssamp.wav exists and is a valid and readable PCM format wave file. + +The filter dispatch structure [**KSFILTER\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562554) in Filter.cpp provides dispatches to create and process data. The **DispatchProcess** method is defined inline in Filter.h. It calls the **Process** method in Filter.cpp in the context of the **CCaptureFilter** class. Be aware that the process dispatch is provided in **KSFILTER\_DISPATCH** because this sample is filter-centric. + +Audio.cpp lays out a [**KSPIN\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563535) pin dispatch structure, which contains the dispatch table for the audio pin. Be aware that the **Process** member of this structure is **NULL** because the sample is filter-centric. Similarly, Video.cpp contains the **KSPIN\_DISPATCH** structure for the video capture pin, again with the **Process** member set to **NULL**. + +For more information, see the comments in all .cpp files. + +File Manifest +------------- + +File | Description +-----|---------- +Audio.cpp | Audio capture pin implementation. +Audio.h | Header file for Audio.cpp. +Avssamp.cpp | Main file for the AVStream filter-centric sample. +Avssamp.h | Main header for the AVStream filter-centric sample. +Avssamp.inf | Installation information for the AVStream sample driver (avssamp.sys). +Capture.cpp | Capture pin implementation for all capture pins on the sample filter. +Capture .h | Capture pin level header for all capture pins on the sample filter. +Filter.cpp | Capture filter implementation (including frame synthesis) for the fake capture filter. +Filter.h | Filter level header for the filter-centric capture filter. +Image.cpp | Image synthesis and overlay code. These objects provide image synthesis (pixel, color-bar, etc) onto RGB24 and UYVY buffers as well as software string overlay into these buffers. +Image.h | Image synthesis and overlay header. +Purecall.h | _purecall stub necessary for virtual function usage in drivers. +Video.cpp | Video capture pin implementation. +Video.h | Video capture pin header. +Wave.cpp | Wave object implementation +Wave.h | Wave object header + diff --git a/AVStream/avssamp/audio.cpp b/AVStream/avssamp/audio.cpp new file mode 100644 index 00000000..cc35f869 --- /dev/null +++ b/AVStream/avssamp/audio.cpp @@ -0,0 +1,655 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + audio.cpp + + Abstract: + + This file contains the audio capture pin implementation. + + History: + + created 6/28/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +NTSTATUS +CAudioCapturePin:: +DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Create a new audio capture pin. This is the creation dispatch for + the audio capture pin. + +Arguments: + + Pin - + The pin being created + + Irp - + The creation Irp + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + CAudioCapturePin *CapPin = new (NonPagedPool) CAudioCapturePin (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); + } + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CAudioCapturePin:: +Acquire ( + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + Called when the pin transitions into acquire, this gets and releases + our hold on the wave object we use to synthesize audio streams. + +Arguments: + + FromState - + The state the pin is transitioning away from + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + if (FromState == KSSTATE_STOP) { + // + // On the transition into acquire from stop, get ahold of the + // wave object we're synthesizing from. + // + m_WaveObject = m_ParentFilter -> GetWaveObject (); + NT_ASSERT (m_WaveObject); + + // + // There must be a wave object or something is really wrong. + // + if (!m_WaveObject) { + Status = STATUS_INTERNAL_ERROR; + } else { + m_WaveObject -> Reset (); + } + + } else { + // + // Ensure we hold no reference on the wave object. + // + m_WaveObject = NULL; + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CAudioCapturePin:: +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: + + The intersect handler for the audio capture pin. This is really quite + simple because the audio pin only exposes the number of channels, + sampling frequency, etc... that the wave file it is synthesizing from + contains. + +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(); + + // + // Verify that the inpassed range is valid size. + // + if (CallerDataRange -> FormatSize < sizeof (KSDATARANGE_AUDIO)) { + return STATUS_NO_MATCH; + } + + // + // Because the only range we expose is such that it will match + // KSDATARANGE_AUDIO, it is safe to interpret the data structures as + // KSDATARANGE_AUDIO. This is due to the fact that AVStream will have + // prematched the GUIDs for us. + // + PKSDATARANGE_AUDIO CallerAudioRange = + reinterpret_cast <PKSDATARANGE_AUDIO> (CallerDataRange); + + PKSDATARANGE_AUDIO DescriptorAudioRange = + reinterpret_cast <PKSDATARANGE_AUDIO> (DescriptorDataRange); + + // + // We are returning a KSDATAFORMAT_WAVEFORMATEX. Specify such if a size + // query happens. + // + if (BufferSize == 0) { + *DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + return STATUS_BUFFER_OVERFLOW; + } + + if (BufferSize < sizeof (KSDATAFORMAT_WAVEFORMATEX)) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Match the blocks. We only support one format (not really a range), so + // this intersection aught to be really simple. It's more of a check + // if the format we are going to use intersects somewhere in + // CallerAudioRange. + // + if (DescriptorAudioRange -> MaximumChannels > + CallerAudioRange -> MaximumChannels || + DescriptorAudioRange -> MinimumBitsPerSample < + CallerAudioRange -> MinimumBitsPerSample || + DescriptorAudioRange -> MinimumBitsPerSample > + CallerAudioRange -> MaximumBitsPerSample || + DescriptorAudioRange -> MinimumSampleFrequency < + CallerAudioRange -> MinimumSampleFrequency || + DescriptorAudioRange -> MinimumSampleFrequency > + CallerAudioRange -> MaximumSampleFrequency) { + + // + // If the descriptor's "range" (more of a single format specified + // in a range) doesn't intersect the caller's, no match the call. + // + *DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + return STATUS_NO_MATCH; + + } + + // + // Build the format. + // + PKSDATAFORMAT_WAVEFORMATEX WaveFormat = + reinterpret_cast <PKSDATAFORMAT_WAVEFORMATEX> (Data); + + RtlCopyMemory ( + &WaveFormat -> DataFormat, + &DescriptorAudioRange -> DataRange, + sizeof (KSDATAFORMAT) + ); + + WaveFormat -> WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; + WaveFormat -> WaveFormatEx.nChannels = + (WORD)DescriptorAudioRange -> MaximumChannels; + WaveFormat -> WaveFormatEx.nSamplesPerSec = + DescriptorAudioRange -> MaximumSampleFrequency; + WaveFormat -> WaveFormatEx.wBitsPerSample = + (WORD)DescriptorAudioRange -> MaximumBitsPerSample; + WaveFormat -> WaveFormatEx.nBlockAlign = + (WaveFormat -> WaveFormatEx.wBitsPerSample / 8) * + WaveFormat -> WaveFormatEx.nChannels; + WaveFormat -> WaveFormatEx.nAvgBytesPerSec = + WaveFormat -> WaveFormatEx.nBlockAlign * + WaveFormat -> WaveFormatEx.nSamplesPerSec; + WaveFormat -> WaveFormatEx.cbSize = 0; + WaveFormat -> DataFormat.SampleSize = + WaveFormat -> WaveFormatEx.nBlockAlign; + + WaveFormat -> DataFormat.FormatSize = + *DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + + return STATUS_SUCCESS; + +} + +/*************************************************/ + + +NTSTATUS +CAudioCapturePin:: +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(); + + // + // This pin does not accept any format changes. It is fixed format based + // on what the wave file we're synthesizing from is. Thus, we don't + // need to worry about this being called in any context except pin + // creation (KSPIN_FLAG_FIXED_FORMAT ensures this). Knowing that the + // format already is a GUID match for the range and we only have one + // range, the interpretation without any guid checks is safe. + // + NT_ASSERT (!OldFormat); + + const KSDATARANGE_AUDIO *DataRangeAudio = + reinterpret_cast <const KSDATARANGE_AUDIO *> (DataRange); + + // + // Verify the format is the right size. + // + if (Pin -> ConnectionFormat -> FormatSize < + sizeof (KSDATAFORMAT_WAVEFORMATEX)) { + + return STATUS_NO_MATCH; + } + + PKSDATAFORMAT_WAVEFORMATEX WaveFormat = + reinterpret_cast <PKSDATAFORMAT_WAVEFORMATEX> ( + Pin -> ConnectionFormat + ); + + // + // This is not an intersection, but rather a direct comparison due to + // the fact that we're fixed to a single format and do not really have + // a range. + // + if (WaveFormat -> WaveFormatEx.wFormatTag != WAVE_FORMAT_PCM || + WaveFormat -> WaveFormatEx.nChannels != + DataRangeAudio -> MaximumChannels || + WaveFormat -> WaveFormatEx.nSamplesPerSec != + DataRangeAudio -> MaximumSampleFrequency || + WaveFormat -> WaveFormatEx.wBitsPerSample != + DataRangeAudio -> MaximumBitsPerSample) { + + return STATUS_NO_MATCH; + + } + + // + // The format passes consideration. Allow the pin creation with this + // particular format. + // + return STATUS_SUCCESS; + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +NTSTATUS +CAudioCapturePin:: +CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ) + +/*++ + +Routine Description: + + Called to synthesize a frame of audio data from the wave object. + +Arguments: + + ProcessPin - + The process pin from the filter's process pins index + + Tick - + The tick counter from the filter (the number of DPC's that have + happened since the DPC timer started). Note that the DPC timer + starts at pause and capture starts at run. + +Return Value: + + Success / Failure + +--*/ + +{ + + NT_ASSERT (ProcessPin -> Pin == m_Pin); + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + m_FrameNumber++; + + // + // Find out how much time worth of audio data to synthesize into + // the buffer a buffer (or how much time to skip if there are no available + // capture buffers). + // + LONGLONG TimerInterval = m_ParentFilter -> GetTimerInterval (); + + // + // 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 skip forward into the audio stream. + // + if (ProcessPin -> BytesAvailable) { + // + // Synthesize a fixed amount of audio data based on the timer interval. + // + ULONG BytesUsed = m_WaveObject -> SynthesizeFixed ( + TimerInterval, + ProcessPin -> Data, + ProcessPin -> BytesAvailable + ); + + ProcessPin -> BytesUsed = BytesUsed; + ProcessPin -> Terminate = TRUE; + + // + // Time stamp the packet if there is a clock assigned. + // + if (m_Clock) { + PKSSTREAM_HEADER StreamHeader = + ProcessPin -> StreamPointer -> StreamHeader; + + StreamHeader -> PresentationTime.Time = m_Clock -> GetTime (); + StreamHeader -> PresentationTime.Numerator = + StreamHeader -> PresentationTime.Denominator = 1; + StreamHeader -> OptionsFlags |= + KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + } + + } else { + m_DroppedFrames++; + + // + // Since we've skipped an audio frame, inform the wave object to + // skip forward this much. + // + m_WaveObject -> SkipFixed (TimerInterval); + } + + return STATUS_SUCCESS; + +} + +/************************************************************************** + + DESCRIPTOR / DISPATCH LAYOUT + +**************************************************************************/ + +// +// AudioCapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +const +KSPIN_DISPATCH +AudioCapturePinDispatch = { + CAudioCapturePin::DispatchCreate, // Pin Create + NULL, // Pin Close + NULL, // Pin Process + NULL, // Pin Reset + CAudioCapturePin::DispatchSetFormat, // Pin Set Data Format + CCapturePin::DispatchSetState, // Pin Set Device State + NULL, // Pin Connect + NULL, // Pin Disconnect + NULL, // Clock Dispatch + NULL // Allocator Dispatch +}; + +// +// AudioDefaultAllocatorFraming: +// +// A default framing for the audio pin. In order for this to work properly, +// the frame size must be at least 1/fps * bytes_per_sec large. Otherwise, +// the audio stream will fall behind. This is dynamically adjusted when +// the actual pin is created. +// +DECLARE_SIMPLE_FRAMING_EX ( + AudioDefaultAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 25, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE + ); + +// +// g_PINNAME_AUDIO_CAPTURE: +// +// A GUID identifying the name of the audio capture pin. I use the standard +// STATIC_PINNAME_VIDEO_CAPTURE for the video capture pin, but a custom name +// as defined in avssamp.inf for the audio capture pin. +// +GUID g_PINNAME_AUDIO_CAPTURE = + {0xba1184b9, 0x1fe6, 0x488a, 0xae, 0x78, 0x6e, 0x99, 0x7b, 0x2, 0xca, 0xea}; + +// +// AudioPinDescriptorTemplate: +// +// The template for the audio pin descriptor. The audio pin on this filter +// is created dynamically -- if and only if c:\avssamp.wav exists and is +// a valid and readable wave file. +// +const +KSPIN_DESCRIPTOR_EX +AudioPinDescriptorTemplate = { + // + // Audio Capture Pin + // + &AudioCapturePinDispatch, + NULL, + { + NULL, // Interfaces (NULL, 0 == default) + 0, + NULL, // Mediums (NULL, 0 == default) + 0, + 0, // Range count (filled in later) + NULL, // Ranges (filled in later) + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &KSCATEGORY_AUDIO, // Category + &g_PINNAME_AUDIO_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 | + KSPIN_FLAG_FIXED_FORMAT, + 1, // Instances Possible + 0, // Instances Necessary + &AudioDefaultAllocatorFraming, // Allocator Framing (filled later) + reinterpret_cast <PFNKSINTERSECTHANDLEREX> // Intersect Handler + (CAudioCapturePin::IntersectHandler) +}; + diff --git a/AVStream/avssamp/audio.h b/AVStream/avssamp/audio.h new file mode 100644 index 00000000..0240ddc8 --- /dev/null +++ b/AVStream/avssamp/audio.h @@ -0,0 +1,150 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + audio.h + + Abstract: + + This file contains the audio capture pin header. + + History: + + created 6/28/01 + +**************************************************************************/ + +class CAudioCapturePin : + public CCapturePin + +{ + +private: + + // + // The wave object used to synthesize audio data. + // + CWaveObject *m_WaveObject; + +public: + + // + // CAudioCapturePin(): + // + // Construct a new audio capture pin. + // + CAudioCapturePin ( + IN PKSPIN Pin + ) : CCapturePin (Pin) + { + } + + // + // ~CAudioCapturePin(): + // + // Destruct an audio capture pin. + // + ~CAudioCapturePin ( + ) + { + } + + // + // Acquire(): + // + // Called when the audio capture pin is transitioning into the acquire + // state (from either stop or pause). This routine will get ahold of + // the wave object from the filter. + // + virtual + NTSTATUS + Acquire ( + IN KSSTATE FromState + ); + + // + // CaptureFrame(): + // + // This is called when the filter processes and wants to trigger processing + // of an audio frame. The routine will compute how far into the stream + // we've progressed and ask the filter's wave object to copy enough + // "synthesized" audio data from the wave object in order to reach + // the position. + // + virtual + NTSTATUS + CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ); + + /************************************************* + + Dispatch Functions + + *************************************************/ + + // + // DispatchCreate(): + // + // This is the creation dispatch for the audio capture pin on the filter. + // It creates the CAudioCapturePin, associates it with the AVStream pin + // object and bags the class object for automatic cleanup when the + // pin is closed. + // + static + NTSTATUS + DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ); + + // + // DispatchSetFormat(): + // + // This is the set data format dispatch for the pin. This will be called + // BEFORE pin creation to validate that a data format selected is a match + // for the range pulled out of our range list. It will also be called + // for format changes. + // + // If OldFormat is NULL, this is an indication that it's the initial + // call and not a format change. Even fixed format pins get this call + // once. + // + static + NTSTATUS + DispatchSetFormat ( + IN PKSPIN Pin, + IN PKSDATAFORMAT OldFormat OPTIONAL, + IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL, + IN const KSDATARANGE *DataRange, + IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL + ); + + // + // IntersectHandler(): + // + // This is the data intersection handler for the capture pin. This + // determines an optimal format in the intersection of two ranges, + // one local and one possibly foreign. If there is no compatible format, + // STATUS_NO_MATCH is returned. + // + static + NTSTATUS + 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 + ); + + +}; diff --git a/AVStream/avssamp/avssamp.cpp b/AVStream/avssamp/avssamp.cpp new file mode 100644 index 00000000..5a2c2b77 --- /dev/null +++ b/AVStream/avssamp/avssamp.cpp @@ -0,0 +1,243 @@ +/************************************************************************** + +AVStream Filter-Centric Sample + +Copyright (c) 1999 - 2001, Microsoft Corporation + +File: + +avssamp.cpp + +Abstract: + +This is the main file for the filter-centric sample. + +History: + +created 6/18/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + +INITIALIZATION CODE + +**************************************************************************/ + + +extern "C" DRIVER_INITIALIZE DriverEntry; + +PVOID operator new +( + size_t iSize, + _When_((poolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + POOL_TYPE poolType + ) +{ + PVOID result = ExAllocatePoolWithTag(poolType, iSize, 'wNCK'); + + if (result) { + RtlZeroMemory(result, iSize); + } + + return result; +} + +PVOID operator new +( + size_t iSize, + _When_((poolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + POOL_TYPE poolType, + ULONG tag + ) +{ + PVOID result = ExAllocatePoolWithTag(poolType, iSize, tag); + + if (result) { + RtlZeroMemory(result, iSize); + } + + return result; +} + +/*++ + +Routine Description: + +Array delete() operator. + +Arguments: + +pVoid - +The memory to free. + +Return Value: + +None + +--*/ +void +__cdecl +operator delete[]( + PVOID pVoid + ) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + +Sized delete() operator. + +Arguments: + +pVoid - +The memory to free. + +size - +The size of the memory to free. + +Return Value: + +None + +--*/ +void __cdecl operator delete +( + void *pVoid, + size_t /*size*/ + ) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + +Sized delete[]() operator. + +Arguments: + +pVoid - +The memory to free. + +size - +The size of the memory to free. + +Return Value: + +None + +--*/ +void __cdecl operator delete[] +( + void *pVoid, + size_t /*size*/ + ) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +extern "C" +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath +) + +/*++ + +Routine Description: + +Driver entry point. Pass off control to the AVStream initialization +function (KsInitializeDriver) and return the status code from it. + +Arguments: + +DriverObject - +The WDM driver object for our driver + +RegistryPath - +The registry path for our registry info + +Return Value: + +As from KsInitializeDriver + +--*/ + +{ + + // + // Simply pass the device descriptor and parameters off to AVStream + // to initialize us. This will cause filter factories to be set up + // at add & start. Everything is done based on the descriptors passed + // here. + // + return + KsInitializeDriver( + DriverObject, + RegistryPath, + &CaptureDeviceDescriptor + ); + +} + +/************************************************************************** + +DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +// +// FilterDescriptors: +// +// The table of filter descriptors that this device supports. Each one of +// these will be used as a template to create a filter-factory on the device. +// +DEFINE_KSFILTER_DESCRIPTOR_TABLE(FilterDescriptors) { + &CaptureFilterDescriptor +}; + +// +// CaptureDeviceDescriptor: +// +// This is the device descriptor for the capture device. It points to the +// dispatch table and contains a list of filter descriptors that describe +// filter-types that this device supports. Note that the filter-descriptors +// can be created dynamically and the factories created via +// KsCreateFilterFactory as well. +// +const +KSDEVICE_DESCRIPTOR +CaptureDeviceDescriptor = { + // + // Since this is a software sample (filter-centric filters usually are + // software kinds of transforms), we really don't care about device level + // notifications and work. The default behavior done on behalf of us + // by AVStream will be quite sufficient. + // + NULL, + SIZEOF_ARRAY(FilterDescriptors), + FilterDescriptors, + KSDEVICE_DESCRIPTOR_VERSION +}; + diff --git a/AVStream/avssamp/avssamp.h b/AVStream/avssamp/avssamp.h new file mode 100644 index 00000000..7d0bd7c3 --- /dev/null +++ b/AVStream/avssamp/avssamp.h @@ -0,0 +1,252 @@ +/************************************************************************** + +AVStream Filter-Centric Sample + +Copyright (c) 2001, Microsoft Corporation + +File: + +avssamp.h + +Abstract: + +AVStream Filter-Centric Sample header file. This is the main +header. + +History: + +created 6/18/01 + +**************************************************************************/ + +/************************************************* + +Standard Includes + +*************************************************/ + +extern "C" { +#include <wdm.h> +} + +#include <windef.h> +#include <stdio.h> +#include <stdlib.h> +#include <ntstrsafe.h> +#define NOBITMAP +#include <mmreg.h> +#undef NOBITMAP +#include <unknown.h> +#include <ks.h> +#include <ksmedia.h> +#pragma warning (disable : 4100 4101 4131 4127 4189 4701 4706) +/************************************************* + +Misc Definitions + +*************************************************/ + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) + +#ifndef mmioFOURCC +#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \ + ( (DWORD)(BYTE)(ch0) | ( (DWORD)(BYTE)(ch1) << 8 ) | \ + ( (DWORD)(BYTE)(ch2) << 16 ) | ( (DWORD)(BYTE)(ch3) << 24 ) ) +#endif + +#define FOURCC_YUV422 mmioFOURCC('U', 'Y', 'V', 'Y') + +// +// CAPTURE_PIN_DATA_RANGE_COUNT: +// +// The number of ranges supported on the capture pin. +// +#define CAPTURE_PIN_DATA_RANGE_COUNT 2 + +// +// CAPTURE_FILTER_PIN_COUNT: +// +// The number of pins on the capture filter. +// +#define CAPTURE_FILTER_PIN_COUNT 1 + +// +// CAPTURE_FILTER_CATEGORIES_COUNT: +// +// The number of categories for the capture filter. +// +#define CAPTURE_FILTER_CATEGORIES_COUNT 2 + +#define AVSSMP_POOLTAG 'sSVA' + +/************************************************* + +Externed information + +*************************************************/ + +// +// filter.cpp externs: +// +extern +const +KSFILTER_DISPATCH +CaptureFilterDispatch; + +extern +const +KSFILTER_DESCRIPTOR +CaptureFilterDescriptor; + +extern +const +KSPIN_DESCRIPTOR_EX +CaptureFilterPinDescriptors[CAPTURE_FILTER_PIN_COUNT]; + +extern +const +GUID +CaptureFilterCategories[CAPTURE_FILTER_CATEGORIES_COUNT]; + +// +// video.cpp externs: +// +extern +const +KSALLOCATOR_FRAMING_EX +VideoCapturePinAllocatorFraming; + +extern +const +KSPIN_DISPATCH +VideoCapturePinDispatch; + +extern +const +PKSDATARANGE +VideoCapturePinDataRanges[CAPTURE_PIN_DATA_RANGE_COUNT]; + +// +// audio.cpp externs: +// +extern +const +KSPIN_DESCRIPTOR_EX +AudioPinDescriptorTemplate; + +// +// avssamp.cpp externs: +// +extern +const +KSDEVICE_DESCRIPTOR +CaptureDeviceDescriptor; + +#ifndef _NEW_DELETE_OPERATORS_ +#define _NEW_DELETE_OPERATORS_ + +PVOID operator new +( + size_t iSize, + _When_((poolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + POOL_TYPE poolType + ); + +PVOID operator new +( + size_t iSize, + _When_((poolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + POOL_TYPE poolType, + ULONG tag + ); + +/*++ + +Routine Description: + +Array delete() operator. + +Arguments: + +pVoid - +The memory to free. + +Return Value: + +None + +--*/ +void +__cdecl +operator delete[]( + PVOID pVoid + ); + +/*++ + +Routine Description: + +Sized delete() operator. + +Arguments: + +pVoid - +The memory to free. + +size - +The size of the memory to free. + +Return Value: + +None + +--*/ +void __cdecl operator delete +( + void *pVoid, + size_t /*size*/ + ); + +/*++ + +Routine Description: + +Sized delete[]() operator. + +Arguments: + +pVoid - +The memory to free. + +size - +The size of the memory to free. + +Return Value: + +None + +--*/ +void __cdecl operator delete[] +( + void *pVoid, + size_t /*size*/ + ); + +#endif // _NEW_DELETE_OPERATORS_ + +/************************************************* + +Internal Includes + +*************************************************/ + +#include "image.h" +#include "wave.h" +#include "filter.h" +#include "capture.h" +#include "video.h" +#include "audio.h" diff --git a/AVStream/avssamp/avssamp.inf b/AVStream/avssamp/avssamp.inf new file mode 100644 index 00000000..62db5009 --- /dev/null +++ b/AVStream/avssamp/avssamp.inf @@ -0,0 +1,93 @@ +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; avssamp.INF -- This file contains installation information for the filter-based +; AVStream sample driver avssamp.sys +; +; Note: +; +; This INF expects the following hierarchy in the installation folder: +; +; \ +; avssamp.inf +; avssamp.sys +; + +[Version] +Signature="$Windows NT$" +Class=MEDIA +ClassGUID={4d36e96c-e325-11ce-bfc1-08002be10318} +Provider=%ProviderName% +CatalogFile=avssamp.cat +DriverVer=09/30/2004,1.0.0.0 + +[SourceDisksNames] +1000 = %cdname%,,, + +[SourceDisksFiles] +avssamp.sys = 1000 + +[ControlFlags] +ExcludeFromSelect=* + +[DestinationDirs] +avssamp.CopyFiles=12 + +[Manufacturer] +%ManufacturerName%=Standard,NTamd64,NTx86 + +;--------------------------------------------------------------- +; The preferred method to install as a Root-enumerated device. +; NOTE: DO NOT INCLUDE THIS FOR A HARDWARE DRIVER! +;--------------------------------------------------------------- + +[DeviceInstall32] +AddDevice = ROOT\SW\{20698827-7099-4c4e-861A-4879D639A35F},,avssamp_RootEnumInstall + +[avssamp_RootEnumInstall] +HardwareIds = SW\{20698827-7099-4c4e-861A-4879D639A35F} +;--------------------------------------------------------------- + +[Standard.NTx86] +%avssamp.DeviceDesc%=avssamp,SW\{20698827-7099-4c4e-861A-4879D639A35F} + +[Standard.NTamd64] +%avssamp.DeviceDesc%=avssamp,SW\{20698827-7099-4c4e-861A-4879D639A35F} + +[avssamp.NT] +include=ks.inf,kscaptur.inf +needs=KS.Registration,KSCAPTUR.Registration.NT +CopyFiles=avssamp.CopyFiles + +[avssamp.CopyFiles] +avssamp.sys + +[avssamp.NT.Services] +AddService=avssamp, 0x00000002, avssamp.ServiceInstall + +[avssamp.ServiceInstall] +DisplayName=%avssamp.DeviceDesc% +ServiceType=%SERVICE_KERNEL_DRIVER% +StartType=%SERVICE_DEMAND_START% +ErrorControl=%SERVICE_ERROR_NORMAL% +ServiceBinary=%12%\avssamp.sys + +[Strings] +; non-localizable +Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" +avssamp.DeviceId="{20698827-7099-4c4e-861A-4879D639A35F}" +KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" +KSSTRING_Filter="{9B365890-165F-11D0-A195-0020AFD156E4}" + +SERVICE_KERNEL_DRIVER=1 +SERVICE_DEMAND_START=3 +SERVICE_ERROR_NORMAL=1 +REG_EXPAND_SZ=0x00020000 +REG_DWORD=0x00010001 + +;localizable +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +avssamp.DeviceDesc="AVStream Filter-Centric Sample Driver" +avssamp.Reader.FriendlyName="avssamp Source" + +cdname="Disk 1" diff --git a/AVStream/avssamp/avssamp.rc b/AVStream/avssamp/avssamp.rc new file mode 100644 index 00000000..b8c7f25b --- /dev/null +++ b/AVStream/avssamp/avssamp.rc @@ -0,0 +1,22 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1999 - 1999 +// +// File: avssamp.rc +// +//-------------------------------------------------------------------------- + +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AVStream Filter-Centric Sample" +#define VER_INTERNALNAME_STR "avssamp.sys" +#define VER_ORIGINALFILENAME_STR "avssamp.sys" + +#include "common.ver" + diff --git a/AVStream/avssamp/avssamp.sln b/AVStream/avssamp/avssamp.sln new file mode 100644 index 00000000..6b6d6b07 --- /dev/null +++ b/AVStream/avssamp/avssamp.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avssamp", "avssamp.vcxproj", "{2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Debug|Win32.ActiveCfg = Debug|Win32 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Debug|Win32.Build.0 = Debug|Win32 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Release|Win32.ActiveCfg = Release|Win32 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Release|Win32.Build.0 = Release|Win32 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Debug|x64.ActiveCfg = Debug|x64 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Debug|x64.Build.0 = Debug|x64 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Release|x64.ActiveCfg = Release|x64 + {2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/AVStream/avssamp/avssamp.vcxproj b/AVStream/avssamp/avssamp.vcxproj new file mode 100644 index 00000000..21c292d2 --- /dev/null +++ b/AVStream/avssamp/avssamp.vcxproj @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{2CCFF21D-0B89-4F39-A4FC-2D0CCC25A071}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{B0781D0B-B397-4C3A-9D2D-6993E9C47C60}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>avssamp</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>avssamp</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>avssamp</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>avssamp</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="audio.cpp" /> + <ClCompile Include="avssamp.cpp" /> + <ClCompile Include="capture.cpp" /> + <ClCompile Include="filter.cpp" /> + <ClCompile Include="image.cpp" /> + <ClCompile Include="purecall.c" /> + <ClCompile Include="video.cpp" /> + <ClCompile Include="wave.cpp" /> + <ResourceCompile Include="avssamp.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/AVStream/avssamp/avssamp.vcxproj.Filters b/AVStream/avssamp/avssamp.vcxproj.Filters new file mode 100644 index 00000000..9b056e4d --- /dev/null +++ b/AVStream/avssamp/avssamp.vcxproj.Filters @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{CA27E3B0-4B55-4FE3-9F26-29F35E3AF608}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{3F416EF8-A9B0-42DF-9371-BAA2CC072CD3}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{C2BB2712-8840-4220-B911-1CD96B679F83}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{F67003C9-C833-4C37-97BC-2593BB03BE03}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="audio.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="avssamp.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="capture.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="filter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="image.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="purecall.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="video.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wave.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="avssamp.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/AVStream/avssamp/capture.cpp b/AVStream/avssamp/capture.cpp new file mode 100644 index 00000000..019b5ad7 --- /dev/null +++ b/AVStream/avssamp/capture.cpp @@ -0,0 +1,249 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + capture.cpp + + Abstract: + + This file contains the capture pin implementation for all capture + pins on the sample filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CCapturePin:: +CCapturePin ( + IN PKSPIN Pin + ) : + m_Pin (Pin), + m_State (KSSTATE_STOP) + +/*++ + +Routine Description: + + Construct a new capture pin. Find out the filter associated with this + pin and stash a pointer to our parent filter. + +Arguments: + + Pin - + The AVStream pin object being created. + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + PKSFILTER ParentFilter = KsPinGetParentFilter (Pin); + + m_ParentFilter = reinterpret_cast <CCaptureFilter *> ( + ParentFilter -> Context + ); + +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +SetState ( + IN KSSTATE ToState, + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + Called when the pin is transitioning state. This is a bridge from + DispatchSetState in the context of the capture pin. The function itself + performs basic clock handling (things that all the derived pins would use) + and then calls the appropriate method in the derived class. + +Arguments: + + FromState - + The state the pin is transitioning away from + + ToState - + The state the pin is transitioning towards + +Return Value: + + Success / Failure of state transition. + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + switch (ToState) { + + case KSSTATE_STOP: + + // + // Reset the dropped frame counter. + // + m_DroppedFrames = 0; + m_FrameNumber = 0; + + // + // On a transition to stop, the clock will be released. + // + if (m_Clock) { + m_Clock -> Release (); + m_Clock = NULL; + } + + Status = Stop (FromState); + break; + + case KSSTATE_ACQUIRE: + + // + // On a transition to acqiure (from stop), the pin queries for + // its assigned clock. This can be done either here or at the + // transition to pause. + // + if (FromState == KSSTATE_STOP) { + + Status = KsPinGetReferenceClockInterface ( + m_Pin, + &m_Clock + ); + + if (!NT_SUCCESS (Status)) { + m_Clock = NULL; + } + + } + + Status = Acquire (FromState); + break; + + case KSSTATE_PAUSE: + + Status = Pause (FromState); + break; + + case KSSTATE_RUN: + + Status = Run (FromState); + break; + + } + + if (NT_SUCCESS (Status)) { + m_State = ToState; + } + + return Status; + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +ULONG +CCapturePin:: +QueryFrameDrop ( + ) + +/*++ + +Routine Description: + + Return the number of frames which have been dropped on this pin. + +Arguments: + + None + +Return Value: + + The number of frames which have been dropped on this pin. + +--*/ + +{ + + return m_DroppedFrames; + +} + +/*************************************************/ + + +void +CCapturePin:: +NotifyDrops ( + IN ULONG VidDrop, + IN ULONG AudDrop + ) + +/*++ + +Routine Description: + + Stash the number of dropped frames on each pin in this pin to allow + this data to be incorporated into any synthesis. + +Arguments: + + VidDrop - + Number of video frames that have been dropped + + AudDrop - + Number of audio frames that have been dropped + +Return Value: + + None + +--*/ + +{ + + m_NotifyVidDrop = VidDrop; + m_NotifyAudDrop = AudDrop; + +} diff --git a/AVStream/avssamp/capture.h b/AVStream/avssamp/capture.h new file mode 100644 index 00000000..a761358f --- /dev/null +++ b/AVStream/avssamp/capture.h @@ -0,0 +1,284 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + capture.h + + Abstract: + + This file contains the capture pin level header for all capture pins + on the sample filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +class CCapturePin +{ + +protected: + + // + // The clock object associated with this pin. + // + PIKSREFERENCECLOCK m_Clock; + + // + // The AVStream pin object associated with this pin. + // + PKSPIN m_Pin; + + // + // The CCaptureFilter owning this pin. + // + CCaptureFilter *m_ParentFilter; + + // + // The count of dropped frames. The base class will reset this upon + // stopping the pin. + // + ULONG m_DroppedFrames; + + // + // The frame number. + // + ULONGLONG m_FrameNumber; + + // + // Notifications as to frame drop. This is used to incorporate frame + // drop data into the synthesis. + // + ULONG m_NotifyVidDrop; + ULONG m_NotifyAudDrop; + + // + // Current state. + // + KSSTATE m_State; + +public: + + // + // CCapturePin(): + // + // Construct a new capture pin. + // + CCapturePin ( + IN PKSPIN Pin + ); + + // + // ~CCapturePin(): + // + // Destruct a capture pin. The destructor is virtual because the cleanup + // code will delete the derived class as a CCapturePin. + // + virtual + ~CCapturePin ( + ) + { + } + + // + // ClockAssigned(): + // + // Determine whether or not there is a clock assigned to the pin. + // + BOOLEAN + ClockAssigned ( + ) + { + return (m_Clock != NULL); + } + + // + // GetTime(): + // + // Get the time on the clock. There must be a clock assigned to the pin + // for this call to work. Verification should be made through + // the ClockAssigned() call. + // + LONGLONG + GetTime ( + ) + { + return m_Clock -> GetTime (); + } + + // + // SetState(): + // + // Called to set the state of the pin. The base class performs clock + // handling and calls the appropriate derived method (Run/Pause/Acquire/ + // Stop). + // + NTSTATUS + SetState ( + IN KSSTATE ToState, + IN KSSTATE FromState + ); + + // + // Run(): + // + // Called when a pin transitions to KSSTATE_ACQUIRE by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Run ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // Pause(): + // + // Called when a pin transitions to KSSTATE_PAUSE by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Pause ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // Acquire(): + // + // Called when a pin transitions to KSSTATE_ACQUIRE by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Acquire ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // Stop(): + // + // Called when a pin transitions to KSSTATE_STOP by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Stop ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // GetState(): + // + // Return the current state of the pin. + // + KSSTATE + GetState ( + ) + { + return m_State; + } + + // + // CaptureFrame(): + // + // Called in order to trigger capture of a frame on the given pin. The + // filter's "tick" count is passed as a reference to synthesize an + // appropriate frame. + // + virtual + NTSTATUS + CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ) = 0; + + // + // QueryFrameDrop(): + // + // Query the number of dropped frames. + // + ULONG + QueryFrameDrop ( + ); + + // + // NotifyDrops(): + // + // Notify the pin how many frames have been dropped on all pins. + // + void + NotifyDrops ( + IN ULONG VidDrop, + IN ULONG AudDrop + ); + + /************************************************* + + Dispatch Functions + + *************************************************/ + + // + // DispatchSetState(): + // + // This is the set device state dispatch for the pin. It merely acts + // as a bridge to SetState() in the context of the CCapturePin associated + // with Pin. + // + static + NTSTATUS + DispatchSetState ( + IN PKSPIN Pin, + IN KSSTATE ToState, + IN KSSTATE FromState + ) + { + return + (reinterpret_cast <CCapturePin *> (Pin -> Context)) -> + SetState (ToState, FromState); + } + + // + // BagCleanup(): + // + // This is the free callback for the CCapturePin that we bag. Normally, + // ExFreePool would be used, but we must delete instead. This function + // will just delete the CCapturePin instead of freeing it. Because our + // destructor is virtual, the appropriate derived class destructor will + // get called. + // + static + void + BagCleanup ( + IN CCapturePin *This + ) + + { + + delete This; + + } + +}; diff --git a/AVStream/avssamp/filter.h b/AVStream/avssamp/filter.h new file mode 100644 index 00000000..b39255ea --- /dev/null +++ b/AVStream/avssamp/filter.h @@ -0,0 +1,253 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + filter.h + + Abstract: + + This file contails the filter level header for the filter-centric + capture filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +/************************************************************************** + + DEFINES + +**************************************************************************/ + +// +// VIDEO_PIN_ID: +// +// The pin factory id of the video pin (the order in the descriptor table). +// +#define VIDEO_PIN_ID 0 + +/************************************************************************** + + CLASSES + +**************************************************************************/ + +class CCaptureFilter { + +private: + + // + // The AVStream filter object associated with this CCaptureFilter. + // + PKSFILTER m_Filter; + + // + // The DPC used for the timer. + // + KDPC m_TimerDpc; + + // + // The timer used for simulation of capture timings. + // + KTIMER m_Timer; + + // + // Boolean used to detect whether the DPC routine is to shutdown or not + // + BOOLEAN m_StoppingDPC; + + // + // The event used to signal successful shutdown of the timer DPC + // + KEVENT m_StopDPCEvent; + + // + // The number of timer ticks that have occurred since the timer DPC + // started firing. + // + volatile ULONG m_Tick; + + // + // The system time at the point that the timer DPC starts. + // + LARGE_INTEGER m_StartTime; + + // + // The amount of time between timer DPC's (and hence frame capture + // triggers). + // + LONGLONG m_TimerInterval; + + // + // The wave object. This is passed to the audio pin later, but it's + // used at filter create time to determine what ranges to expose on + // the audio pin. + // + CWaveObject *m_WaveObject; + + // + // The audio pin factory id. This is dynamic since the pin is created + // dynamically at filter create time. + // + ULONG m_AudioPinId; + + // + // Process(): + // + // The process routine for the capture filter. This is responsible for + // copying synthesized data into image buffers. The DispatchProcess() + // function bridges to this routine in the context of the CCaptureFilter. + // + NTSTATUS + Process ( + IN PKSPROCESSPIN_INDEXENTRY ProcessPinsIndex + ); + + + // + // BindAudioToWaveObject(): + // + // This function call binds the audio stream exposed by the filter to + // the wave object m_WaveObject. + // + NTSTATUS + BindAudioToWaveObject ( + ); + + // + // Cleanup(): + // + // This is the bag cleanup callback for the CCaptureFilter. Not providing + // one would cause ExFreePool to be used. This is not good for C++ + // constructed objects. We simply delete the object here. + // + static + void + Cleanup ( + IN CCaptureFilter *CapFilter + ) + { + delete CapFilter; + } + +public: + + // + // CCaptureFilter(): + // + // The capture filter object constructor. Since the new operator will + // have zeroed the memory, do not bother initializing any NULL or 0 + // fields. Only initialize non-NULL, non-0 fields. + // + CCaptureFilter ( + IN PKSFILTER Filter + ); + + // + // ~CCaptureFilter(): + // + // The capture filter destructor. + // + ~CCaptureFilter ( + ) + { + } + + // + // StartDPC(): + // + // This is called in order to start the timer DPC running. + // + void + StartDPC ( + IN LONGLONG TimerInterval + ); + + // + // StopDPC(): + // + // This is called in order to stop the timer DPC running. The function + // will not return until it guarantees that no more timer DPC's fire. + // + void + StopDPC ( + ); + + // + // GetWaveObject(): + // + // Returns the wave object that has been opened for the filter. + // + CWaveObject * + GetWaveObject ( + ) + { + return m_WaveObject; + } + + // + // GetTimerInterval(): + // + // Returns the timer interval we're using to generate DPC's. + // + LONGLONG + GetTimerInterval ( + ); + + /************************************************* + + Dispatch Routines + + *************************************************/ + + // + // DispatchCreate(): + // + // This is the filter creation dispatch for the capture filter. It + // creates the CCaptureFilter object, associates it with the AVStream + // object, and bags it for easy cleanup later. + // + static + NTSTATUS + DispatchCreate ( + IN PKSFILTER Filter, + IN PIRP Irp + ); + + // + // DispatchProcess(): + // + // This is the filter process dispatch for the capture filter. It merely + // bridges to Process() in the context of the CCaptureFilter. + // + static + NTSTATUS + DispatchProcess ( + IN PKSFILTER Filter, + IN PKSPROCESSPIN_INDEXENTRY ProcessPinsIndex + ) + { + return + (reinterpret_cast <CCaptureFilter *> (Filter -> Context)) -> + Process (ProcessPinsIndex); + } + + + // + // TimerDpc(): + // + // The timer dpc routine. This is bridged to from TimerRoutine in the + // context of the appropriate CCaptureFilter. + // + void + TimerDpc ( + ); + +}; + diff --git a/AVStream/avssamp/image.cpp b/AVStream/avssamp/image.cpp new file mode 100644 index 00000000..f4e70655 --- /dev/null +++ b/AVStream/avssamp/image.cpp @@ -0,0 +1,647 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + image.cpp + + Abstract: + + The image synthesis and overlay code. These objects provide image + synthesis (pixel, color-bar, etc...) onto RGB24 and UYVY buffers as + well as software string overlay into these buffers. + + This entire file, data and all, must be in locked segments. + + History: + + created 1/16/2001 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + Constants + +**************************************************************************/ + +// +// g_FontData: +// +// The following is an 8x8 bitmapped font for use in the text overlay +// code. +// +UCHAR g_FontData [256][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e}, + {0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e}, + {0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, + {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, + {0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c}, + {0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00}, + {0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff}, + {0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00}, + {0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff}, + {0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78}, + {0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18}, + {0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0}, + {0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0}, + {0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99}, + {0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00}, + {0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00}, + {0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18}, + {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, + {0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00}, + {0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78}, + {0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00}, + {0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff}, + {0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00}, + {0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00}, + {0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00}, + {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00}, + {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, + {0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00}, + {0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00}, + {0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, + {0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00}, + {0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00}, + {0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00}, + {0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00}, + {0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00}, + {0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00}, + {0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60}, + {0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00}, + {0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00}, + {0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00}, + {0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00}, + {0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00}, + {0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00}, + {0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00}, + {0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00}, + {0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00}, + {0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00}, + {0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00}, + {0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00}, + {0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60}, + {0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00}, + {0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00}, + {0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00}, + {0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, + {0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00}, + {0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00}, + {0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00}, + {0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00}, + {0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00}, + {0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00}, + {0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00}, + {0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00}, + {0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00}, + {0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00}, + {0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, + {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, + {0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00}, + {0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00}, + {0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00}, + {0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00}, + {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00}, + {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, + {0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00}, + {0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00}, + {0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00}, + {0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00}, + {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00}, + {0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00}, + {0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00}, + {0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00}, + {0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00}, + {0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00}, + {0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78}, + {0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00}, + {0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00}, + {0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0}, + {0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e}, + {0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00}, + {0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00}, + {0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00}, + {0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00}, + {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00}, + {0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00}, + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, + {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, + {0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00}, + {0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78}, + {0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00}, + {0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38}, + {0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00}, + {0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, + {0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, + {0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00}, + {0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00}, + {0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00}, + {0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00}, + {0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00}, + {0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00}, + {0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18}, + {0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00}, + {0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30}, + {0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7}, + {0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70}, + {0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00}, + {0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00}, + {0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00}, + {0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00}, + {0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00}, + {0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f}, + {0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03}, + {0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00}, + {0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00}, + {0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88}, + {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa}, + {0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36}, + {0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36}, + {0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}, + {0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}, + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00}, + {0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0}, + {0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00}, + {0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00}, + {0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00}, + {0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00}, + {0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0}, + {0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc}, + {0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00}, + {0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00}, + {0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00}, + {0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0}, + {0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00}, + {0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00}, + {0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00}, + {0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00}, + {0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00}, + {0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70}, + {0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00}, + {0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00}, + {0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00}, + {0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c}, + {0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00}, + {0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +// +// Standard definition of EIA-189-A color bars. The actual color definitions +// are either in CRGB24Synthesizer or CYUVSynthesizer. +// +const COLOR g_ColorBars[] = + {WHITE, YELLOW, CYAN, GREEN, MAGENTA, RED, BLUE, BLACK}; + +const UCHAR CRGB24Synthesizer::Colors [MAX_COLOR][3] = { + {0, 0, 0}, // BLACK + {255, 255, 255}, // WHITE + {0, 255, 255}, // YELLOW + {255, 255, 0}, // CYAN + {0, 255, 0}, // GREEN + {255, 0, 255}, // MAGENTA + {0, 0, 255}, // RED + {255, 0, 0}, // BLUE + {128, 128, 128} // GREY +}; + +const UCHAR CYUVSynthesizer::Colors [MAX_COLOR][3] = { + {128, 16, 128}, // BLACK + {128, 235, 128}, // WHITE + {16, 211, 146}, // YELLOW + {166, 170, 16}, // CYAN + {54, 145, 34}, // GREEN + {202, 106, 222}, // MAGENTA + {90, 81, 240}, // RED + {240, 41, 109}, // BLUE + {128, 125, 128}, // GREY +}; + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +void +CImageSynthesizer:: +SynthesizeBars ( + ) + +/*++ + +Routine Description: + + Synthesize EIA-189-A standard color bars onto the Image. The image + in question is the current synthesis buffer. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + ULONG ColorCount = SIZEOF_ARRAY (g_ColorBars); + + // + // Set the default cursor... + // + GetImageLocation (0, 0); + + // + // Synthesize a single line. + // + PUCHAR ImageStart = m_Cursor; + for (ULONG x = 0; x < m_Width; x++) + PutPixel (g_ColorBars [((x * ColorCount) / m_Width)]); + + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the synthesized line to all subsequent lines. + // + for (ULONG line = 1; line < m_Height; line++) { + + GetImageLocation (0, line); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } +} + +/*************************************************/ + + +void +CImageSynthesizer:: +Fill ( + IN ULONG X_TopLeft, + IN ULONG Y_TopLeft, + IN ULONG X_BottomRight, + IN ULONG Y_BottomRight, + IN COLOR Color + ) + +{ + + // + // Set the default cursor and capture the copy location. Draw a line + // of the specified color at the default location. + // + PUCHAR ImageStart = GetImageLocation (X_TopLeft, Y_TopLeft); + for (ULONG x = X_TopLeft; x <= X_BottomRight; x++) + PutPixel (Color); + + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the fill line from the current location downward to the requested + // end location. + // + for (ULONG y = Y_TopLeft + 1; y <= Y_BottomRight; y++) { + + GetImageLocation (X_TopLeft, y); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + + } + +} + +/*************************************************/ + + +void +CImageSynthesizer:: +OverlayText ( + _In_ ULONG LocX, + _In_ ULONG LocY, + _In_ ULONG Scaling, + _In_ LPSTR Text, + _In_ COLOR BgColor, + _In_ COLOR FgColor + ) + +/*++ + +Routine Description: + + Overlay text onto the synthesized image. Clip to fit the image + if the overlay does not fit. The image buffer used is the set + synthesis buffer. + +Arguments: + + LocX - + The X location on the image to begin the overlay. This MUST + be inside the image. POSITION_CENTER may be used to indicate + horizontal centering. + + LocY - + The Y location on the image to begin the overlay. This MUST + be inside the image. POSITION_CENTER may be used to indicate + vertical centering. + + Scaling - + Normally, the overlay is done in 8x8 font. A scaling of + 2 indicates 16x16, 3 indicates 24x24 and so forth. + + Text - + A character string containing the information to overlay + + BgColor - + The background color of the overlay window. For transparency, + indicate TRANSPARENT here. + + FgColor - + The foreground color for the text overlay. + +Return Value: + + None + +--*/ + +{ + + NT_ASSERT ((LocX <= m_Width || LocX == POSITION_CENTER) && + (LocY <= m_Height || LocY == POSITION_CENTER)); + + ULONG StrLen = 0; + CHAR* CurChar; + + // + // Determine the character length of the string. + // + for (CurChar = Text; CurChar && *CurChar; CurChar++) + StrLen++; + + // + // Determine the physical size of the string plus border. There is + // a definable NO_CHARACTER_SEPARATION. If this is defined, there will + // be no added space between font characters. Otherwise, one empty pixel + // column is added between characters. + // + #ifndef NO_CHARACTER_SEPARATION + ULONG LenX = (StrLen * (Scaling << 3)) + 1 + StrLen; + #else // NO_CHARACTER_SEPARATION + ULONG LenX = (StrLen * (Scaling << 3)) + 2; + #endif // NO_CHARACTER_SEPARATION + + ULONG LenY = 2 + (Scaling << 3); + + // + // Adjust for center overlays. + // + // NOTE: If the overlay doesn't fit into the synthesis buffer, this + // merely left aligns the overlay and clips off the right side. + // + if (LocX == POSITION_CENTER) { + if (LenX >= m_Width) { + LocX = 0; + } else { + LocX = (m_Width >> 1) - (LenX >> 1); + } + } + + if (LocY == POSITION_CENTER) { + if (LenY >= m_Height) { + LocY = 0; + } else { + LocY = (m_Height >> 1) - (LenY >> 1); + } + } + + // + // Determine the amount of space available on the synthesis buffer. + // We will clip anything that finds itself outside the synthesis buffer. + // + ULONG SpaceX = m_Width - LocX; + ULONG SpaceY = m_Height - LocY; + + // + // Set the default cursor position. + // + GetImageLocation (LocX, LocY); + + // + // Overlay a background color row. + // + if (BgColor != TRANSPARENT && SpaceY) { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) { + PutPixel (BgColor); + } + } + LocY++; + if (SpaceY) SpaceY--; + + // + // Loop across each row of the image. + // + for (ULONG row = 0; row < 8 && SpaceY; row++) { + // + // Generate a line. + // + GetImageLocation (LocX, LocY++); + + PUCHAR ImageStart = m_Cursor; + + ULONG CurSpaceX = SpaceX; + if (CurSpaceX) { + PutPixel (BgColor); + CurSpaceX--; + } + + // + // Generate the row'th row of the overlay. + // + CurChar = Text; + while (CurChar && *CurChar) { + + UCHAR CharBase = g_FontData [*CurChar++][row]; + for (ULONG mask = 0x80; mask && CurSpaceX; mask >>= 1) { + for (ULONG scale = 0; scale < Scaling && CurSpaceX; scale++) { + if (CharBase & mask) { + PutPixel (FgColor); + } else { + PutPixel (BgColor); + } + CurSpaceX--; + } + } + + // + // Separate each character by one space. Account for the border + // space at the end by placing the separator after the last + // character also. + // + #ifndef NO_CHARACTER_SEPARATION + if (CurSpaceX) { + PutPixel (BgColor); + CurSpaceX--; + } + #endif // NO_CHARACTER_SEPARATION + + } + + // + // If there is no separation character defined, account for the + // border. + // + #ifdef NO_CHARACTER_SEPARATION + if (CurSpaceX) { + PutPixel (BgColor); + CurSpaceX--; + } + #endif // NO_CHARACTER_SEPARATION + + + PUCHAR ImageEnd = m_Cursor; + // + // Copy the line downward scale times. + // + for (ULONG scale = 1; scale < Scaling && SpaceY; scale++) { + GetImageLocation (LocX, LocY++); + RtlCopyMemory (m_Cursor, ImageStart, ImageEnd - ImageStart); + SpaceY--; + } + + } + + // + // Add the bottom section of the overlay. + // + GetImageLocation (LocX, LocY); + if (BgColor != TRANSPARENT && SpaceY) { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) { + PutPixel (BgColor); + } + } + +} diff --git a/AVStream/avssamp/image.h b/AVStream/avssamp/image.h new file mode 100644 index 00000000..6d9091e0 --- /dev/null +++ b/AVStream/avssamp/image.h @@ -0,0 +1,476 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + image.h + + Abstract: + + The image synthesis and overlay header. These objects provide image + synthesis (pixel, color-bar, etc...) onto RGB24 and UYVY buffers as + well as software string overlay into these buffers. + + History: + + created 1/16/2001 + +**************************************************************************/ + +/************************************************************************** + + Constants + +**************************************************************************/ + +// +// COLOR: +// +// Pixel color for placement onto the synthesis buffer. +// +typedef enum { + + BLACK = 0, + WHITE, + YELLOW, + CYAN, + GREEN, + MAGENTA, + RED, + BLUE, + GREY, + + MAX_COLOR, + TRANSPARENT, + +} COLOR; + +// +// POSITION_CENTER: +// +// Only useful for text overlay. This can be substituted for LocX or LocY +// in order to center the text screen on the synthesis buffer. +// +#define POSITION_CENTER ((ULONG)-1) + +/************************************************* + + CImageSynthesizer + + This class synthesizes images in various formats for output from the + capture filter. It is capable of performing various text overlays onto + the image surface. + +*************************************************/ + +class CImageSynthesizer { + +protected: + + // + // The width and height the synthesizer is set to. + // + ULONG m_Width; + ULONG m_Height; + + // + // The synthesis buffer. All scan conversion happens in the synthesis + // buffer. This must be set with SetBuffer() before any scan conversion + // routines are called. + // + PUCHAR m_SynthesisBuffer; + + // + // The default cursor. This is a pointer into the synthesis buffer where + // a non specific PutPixel will be placed. + // + PUCHAR m_Cursor; + +public: + + // + // PutPixel(): + // + // Place a pixel at the specified image cursor and move right + // by one pixel. No bounds checking... wrap around occurs. + // + virtual void + PutPixel ( + PUCHAR *ImageLocation, + COLOR Color + ) = 0; + + // + // PutPixel(): + // + // Place a pixel at the default image cursor and move right + // by one pixel. No bounds checking... wrap around occurs. + // + // If the derived class doesn't provide an implementation, provide + // one. + // + virtual void + PutPixel ( + COLOR Color + ) + { + PutPixel (&m_Cursor, Color); + } + + // + // Fill(): + // + // Fill an area of the image with a specific color. + // + virtual void + Fill ( + IN ULONG X_TopLeft, + IN ULONG Y_TopLeft, + IN ULONG X_BottomRight, + IN ULONG Y_BottomRight, + IN COLOR Color + ); + + // + // GetImageLocation(): + // + // Get the location into the image buffer for a specific X/Y location. + // This also sets the synthesizer's default cursor to the position + // LocX, LocY. + // + virtual PUCHAR + GetImageLocation ( + ULONG LocX, + ULONG LocY + ) = 0; + + // + // SetImageSize(): + // + // Set the image size of the synthesis buffer. + // + void + SetImageSize ( + ULONG Width, + ULONG Height + ) + { + m_Width = Width; + m_Height = Height; + } + + // + // SetBuffer(): + // + // Set the buffer the synthesizer generates images to. + // + void + SetBuffer ( + PUCHAR SynthesisBuffer + ) + { + m_SynthesisBuffer = SynthesisBuffer; + } + + // + // SynthesizeBars(): + // + // Synthesize EIA-189-A standard color bars. + // + void + SynthesizeBars ( + ); + + // + // OverlayText(): + // + // Overlay a text string onto the image. + // + void + OverlayText ( + _In_ ULONG LocX, + _In_ ULONG LocY, + _In_ ULONG Scaling, + _In_ LPSTR Text, + _In_ COLOR BgColor, + _In_ COLOR FgColor + ); + + // + // DEFAULT CONSTRUCTOR + // + CImageSynthesizer ( + ) : + m_Width (0), + m_Height (0), + m_SynthesisBuffer (NULL) + { + } + + // + // CONSTRUCTOR: + // + CImageSynthesizer ( + ULONG Width, + ULONG Height + ) : + m_Width (Width), + m_Height (Height), + m_SynthesisBuffer (NULL) + { + } + + // + // DESTRUCTOR: + // + virtual + ~CImageSynthesizer ( + ) + { + } + +}; + +/************************************************* + + CRGB24Synthesizer + + Image synthesizer for RGB24 format. + +*************************************************/ + +class CRGB24Synthesizer : public CImageSynthesizer { + +private: + + const static UCHAR Colors [MAX_COLOR][3]; + + BOOLEAN m_FlipVertical; + +public: + + // + // PutPixel(): + // + // Place a pixel at a specific cursor location. *ImageLocation must + // reside within the synthesis buffer. + // + virtual void + PutPixel ( + PUCHAR *ImageLocation, + COLOR Color + ) + { + if (Color != TRANSPARENT) { + *(*ImageLocation)++ = Colors [(ULONG)Color][0]; + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + *(*ImageLocation)++ = Colors [(ULONG)Color][2]; + } else { + *ImageLocation += 3; + } + } + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual void + PutPixel ( + COLOR Color + ) + { + if (Color != TRANSPARENT) { + *m_Cursor++ = Colors [(ULONG)Color][0]; + *m_Cursor++ = Colors [(ULONG)Color][1]; + *m_Cursor++ = Colors [(ULONG)Color][2]; + } else { + m_Cursor += 3; + } + } + + virtual PUCHAR + GetImageLocation ( + ULONG LocX, + ULONG LocY + ) + { + if (m_FlipVertical) { + return (m_Cursor = + (m_SynthesisBuffer + 3 * + (LocX + (m_Height - 1 - LocY) * m_Width)) + ); + } else { + return (m_Cursor = + (m_SynthesisBuffer + 3 * (LocX + LocY * m_Width)) + ); + } + } + + // + // DEFAULT CONSTRUCTOR: + // + CRGB24Synthesizer ( + BOOLEAN FlipVertical + ) : + m_FlipVertical (FlipVertical) + { + } + + // + // CONSTRUCTOR: + // + CRGB24Synthesizer ( + BOOLEAN FlipVertical, + ULONG Width, + ULONG Height + ) : + CImageSynthesizer (Width, Height), + m_FlipVertical (FlipVertical) + { + } + + // + // DESTRUCTOR: + // + virtual + ~CRGB24Synthesizer ( + ) + { + } + +}; + +/************************************************* + + CYUVSynthesizer + + Image synthesizer for YUV format. + +*************************************************/ + +class CYUVSynthesizer : public CImageSynthesizer { + +private: + + const static UCHAR Colors [MAX_COLOR][3]; + + BOOLEAN m_Parity; + +public: + + // + // PutPixel(): + // + // Place a pixel at a specific cursor location. *ImageLocation must + // reside within the synthesis buffer. + // + virtual void + PutPixel ( + PUCHAR *ImageLocation, + COLOR Color + ) + { + + BOOLEAN Parity = (((*ImageLocation - m_SynthesisBuffer) & 0x2) != 0); + +#if DBG + // + // Check that the current pixel points to a valid start pixel + // in the UYVY buffer. + // + BOOLEAN Odd = (((*ImageLocation - m_SynthesisBuffer) & 0x1) != 0); + NT_ASSERT ((m_Parity && Odd) || (!m_Parity && !Odd)); +#endif // DBG + + if (Color != TRANSPARENT) { + if (Parity) { + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + } else { + *(*ImageLocation)++ = Colors [(ULONG)Color][0]; + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + *(*ImageLocation)++ = Colors [(ULONG)Color][2]; + } + } else { + *ImageLocation += (Parity ? 1 : 3); + } + + } + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual void + PutPixel ( + COLOR Color + ) + + { + + if (Color != TRANSPARENT) { + if (m_Parity) { + *m_Cursor++ = Colors [(ULONG)Color][1]; + } else { + *m_Cursor++ = Colors [(ULONG)Color][0]; + *m_Cursor++ = Colors [(ULONG)Color][1]; + *m_Cursor++ = Colors [(ULONG)Color][2]; + } + } else { + m_Cursor += (m_Parity ? 1 : 3); + } + + m_Parity = !m_Parity; + + } + + virtual PUCHAR + GetImageLocation ( + ULONG LocX, + ULONG LocY + ) + { + + m_Cursor = m_SynthesisBuffer + ((LocX + LocY * m_Width) << 1); + if (m_Parity = ((LocX & 1) != 0)) + m_Cursor++; + + return m_Cursor; + } + + // + // DEFAULT CONSTRUCTOR: + // + CYUVSynthesizer ( + ) + { + } + + // + // CONSTRUCTOR: + // + CYUVSynthesizer ( + ULONG Width, + ULONG Height + ) : + CImageSynthesizer (Width, Height) + { + } + + // + // DESTRUCTOR: + // + virtual + ~CYUVSynthesizer ( + ) + { + } + +}; + diff --git a/AVStream/avssamp/purecall.c b/AVStream/avssamp/purecall.c new file mode 100644 index 00000000..efb3cb49 --- /dev/null +++ b/AVStream/avssamp/purecall.c @@ -0,0 +1,50 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + purecall.c + + Abstract: + + This file contains the _purecall stub necessary for virtual function + usage in drivers on 98 gold. + + History: + + created 9/16/02 + +**************************************************************************/ + +/************************************************* + + Function: + + _purecall + + Description: + + _purecall stub for virtual function usage + + Arguments: + + None + + Return Value: + + 0 + +*************************************************/ +#pragma warning (disable : 4100 4131) +int __cdecl +_purecall ( + VOID + ) + +{ + return 0; +} + 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 + }; + diff --git a/AVStream/avssamp/video.h b/AVStream/avssamp/video.h new file mode 100644 index 00000000..36b9dbe5 --- /dev/null +++ b/AVStream/avssamp/video.h @@ -0,0 +1,221 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + video.h + + Abstract: + + This file contains the video capture pin header. + + History: + + created 6/11/01 + +**************************************************************************/ + +class CVideoCapturePin : + public CCapturePin { + +private: + + // + // A scratch buffer to write into. Due to the fact that we're likely + // sitting upstream of the VMR and getting video memory, I don't want the + // image synthesizer writing to video memory a single byte at a time. + // This will be the buffer that the image synth uses. After a synthesis, + // the buffer will get copied into the data buffers for capture. + // + PUCHAR m_SynthesisBuffer; + + // + // The captured video info header. The settings for image synthesis will + // be based off this header. + // + PKS_VIDEOINFOHEADER m_VideoInfoHeader; + + // + // The image synthesizer. This object is used to construct synthesized + // image data in the video format specified by the connection format. + // + CImageSynthesizer *m_ImageSynth; + + // + // CaptureVideoInfoHeader(): + // + // This routine stashes the video info header set on the pin connection + // in the CVideoCapturePin object. This is used to determine necessary + // variables for image synthesis, etc... + // + PKS_VIDEOINFOHEADER + CaptureVideoInfoHeader ( + ); + +protected: + +public: + + // + // CVideoCapturePin(): + // + // Construct a new video capture pin. + // + CVideoCapturePin ( + IN PKSPIN Pin + ) : + CCapturePin (Pin) + { + } + + // + // ~CVideoCapturePin(): + // + // Destruct a video capture pin. + // + virtual + ~CVideoCapturePin ( + ) + { + } + + // + // CaptureFrame(): + // + // Called from the filter processing routine to indicate that the pin + // should attempt to trigger capture of a video frame. This routine + // will copy synthesized image data into the frame buffer and complete + // the frame buffer. + // + virtual + NTSTATUS + CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ); + + // + // Pause(): + // + // Called when the video capture pin is transitioning into the pause + // state. This will instruct the capture filter to start the timer DPC's + // at the interval demanded by the video info header in the connection + // format. + // + virtual + NTSTATUS + Pause ( + IN KSSTATE FromState + ); + + // + // Acquire(): + // + // Called when the video capture pin is transitioning into the acquire + // state. This will create the necessary image synthesizer to begin + // synthesizing frame capture data when the pin transitions to the + // appropriate state. + // + virtual + NTSTATUS + Acquire ( + IN KSSTATE FromState + ); + + // + // Stop(): + // + // Called when the video capture pin is transitioning into a stop state. + // This simply destroys the image synthesizer in preparation for creating + // a new one next acquire. + // + virtual + NTSTATUS + Stop ( + IN KSSTATE FromState + ); + + /************************************************* + + Dispatch Functions + + *************************************************/ + + // + // DispatchCreate(): + // + // This is the creation dispatch for the video capture pin on the filter. + // It creates the CVideoCapturePin, associates it with the AVStream pin + // object and bags the class object for automatic cleanup when the + // pin is closed. + // + static + NTSTATUS + DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ); + + // + // DispatchSetFormat(): + // + // This is the set data format dispatch for the pin. This will be called + // BEFORE pin creation to validate that a data format selected is a match + // for the range pulled out of our range list. It will also be called + // for format changes. + // + // If OldFormat is NULL, this is an indication that it's the initial + // call and not a format change. Even fixed format pins get this call + // once. + // + static + NTSTATUS + DispatchSetFormat ( + IN PKSPIN Pin, + IN PKSDATAFORMAT OldFormat OPTIONAL, + IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL, + IN const KSDATARANGE *DataRange, + IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL + ); + + // + // IntersectHandler(): + // + // This is the data intersection handler for the capture pin. This + // determines an optimal format in the intersection of two ranges, + // one local and one possibly foreign. If there is no compatible format, + // STATUS_NO_MATCH is returned. + // + static + NTSTATUS + 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 + ); + + // + // CleanupSynth(): + // + // Called when the Image Synthesizer is removed from the object bag + // to be cleaned up. We simply delete the image synth. + // + static + void + CleanupSynth ( + IN CImageSynthesizer *ImageSynth + ) + { + delete ImageSynth; + } + +}; + diff --git a/AVStream/avssamp/wave.cpp b/AVStream/avssamp/wave.cpp new file mode 100644 index 00000000..7af2f60a --- /dev/null +++ b/AVStream/avssamp/wave.cpp @@ -0,0 +1,598 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + wave.cpp + + Abstract: + + Wave object implementation. + + History: + + Created 6/28/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CWaveObject:: +~CWaveObject ( + ) + +/*++ + +Routine Description: + + Destroy a wave object. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + if (m_WaveData) { + ExFreePool (m_WaveData); + } + +} + +/*************************************************/ + + +NTSTATUS +CWaveObject:: +ParseForBlock ( + IN HANDLE FileHandle, + IN ULONG BlockHeader, + IN OUT PLARGE_INTEGER BlockPosition, + OUT PULONG BlockSize + ) + +/*++ + +Routine Description: + + Given that BlockPosition points to the offset of the start of a RIFF block, + continue parsing the specified file until a block with the header of + BlockHeader is found. Return the position of the block data and the size + of the block. + +Arguments: + + FileHandle - + Handle to the file to parse + + BlockHeader - + The block header to scan for + + BlockPosition - + INPUT : Points to the block header to start at + OUTPUT: If successful, points to the block data for the sought block + If unsuccessful, unchanged + + BlockSize - + On output, if successful -- the size of the sought block will be + placed here + +Return Value: + + Success / Failure of the search + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status; + ULONG FmtBlockSize = 0; + LARGE_INTEGER ReadPos = *BlockPosition; + IO_STATUS_BLOCK iosb; + + while (1) { + ULONG BlockHeaderData [2]; + + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + BlockHeaderData, + sizeof (BlockHeaderData), + &ReadPos, + NULL + ); + + if (NT_SUCCESS (Status)) { + if (BlockHeaderData [0] == BlockHeader) { + FmtBlockSize = BlockHeaderData [1]; + ReadPos.QuadPart += 0x8; + break; + } else { + // + // This isn't a format block. Just ignore it. All we + // care about is the format block and the PCM data. + // + ReadPos.QuadPart += BlockHeaderData [1] + 0x8; + } + } else { + break; + } + + } + + if (FmtBlockSize == 0) { + Status = STATUS_NOT_FOUND; + } else { + *BlockPosition = ReadPos; + *BlockSize = FmtBlockSize; + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CWaveObject:: +ParseAndRead ( + ) + +/*++ + +Routine Description: + + Parse the wave file and read the data into an internally allocated + buffer. This prepares to synthesize audio data from the wave + object. + +Arguments: + + None + +Return Value: + + Success / Failure + + If the wave is unrecognized, unparsable, or insufficient memory + exists to allocate the internal buffer, an error code will + be returned and the object will be incapable of synthesizing + audio data based on the wave. + +--*/ + +{ + + PAGED_CODE(); + + IO_STATUS_BLOCK iosb; + UNICODE_STRING FileName; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE FileHandle = NULL; + FILE_OBJECT *FileObj; + + RtlInitUnicodeString (&FileName, m_FileName); + + InitializeObjectAttributes ( + &ObjectAttributes, + &FileName, + (OBJ_CASE_INSENSITIVE | + OBJ_KERNEL_HANDLE), + NULL, + NULL + ); + + Status = ZwCreateFile ( + &FileHandle, + GENERIC_READ | SYNCHRONIZE, + &ObjectAttributes, + &iosb, + 0, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + + if (NT_SUCCESS (Status)) { + ULONG RiffWaveHeader [3]; + + // + // Read the header: RIFF size WAVE + // + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + RiffWaveHeader, + sizeof (RiffWaveHeader), + NULL, + NULL + ); + + // + // Ensure that this is a RIFF file and it's a WAVE. + // + if (NT_SUCCESS (Status)) { + + if (RiffWaveHeader [0] != 'FFIR' || + RiffWaveHeader [2] != 'EVAW') { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + // + // Find the wave format block and ensure it's WAVEFORMATEX and PCM + // data. Otherwise, this can't parse the wave. + // + LARGE_INTEGER ReadPos; + ReadPos.QuadPart = 0xc; + ULONG FmtBlockSize = 0; + + if (NT_SUCCESS (Status)) { + Status = ParseForBlock (FileHandle, ' tmf', &ReadPos, &FmtBlockSize); + } + + // + // If the format block was not found, the file cannot be parsed. If the + // format block is unrecognized, the file cannot be parsed. + // + if (FmtBlockSize >= sizeof (m_WaveFormat)) { + Status = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS (Status)) { + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + &m_WaveFormat, + FmtBlockSize, + &ReadPos, + NULL + ); + } + + if (NT_SUCCESS (Status)) { + if (m_WaveFormat.wFormatTag != WAVE_FORMAT_PCM) { + Status = STATUS_INVALID_PARAMETER; + } + } + + ReadPos.QuadPart += FmtBlockSize; + + // + // Find the data block and read it in. + // + ULONG DataBlockSize; + if (NT_SUCCESS (Status)) { + Status = ParseForBlock (FileHandle, 'atad', &ReadPos, &DataBlockSize); + } + + // + // Perform a slight validation. + // + if (NT_SUCCESS (Status) && + (DataBlockSize == 0 || + (DataBlockSize & (m_WaveFormat.nBlockAlign - 1)))) { + + Status = STATUS_INVALID_PARAMETER; + } + + // + // If we're okay so far, allocate memory for the wave data. + // + if (NT_SUCCESS (Status)) { + m_WaveData = reinterpret_cast <PUCHAR> ( + ExAllocatePoolWithTag (NonPagedPool, DataBlockSize, AVSSMP_POOLTAG) + ); + + if (!m_WaveData) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // + // Read the wave data in. + // + if (NT_SUCCESS (Status)) { + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + m_WaveData, + DataBlockSize, + &ReadPos, + NULL + ); + + m_WaveSize = DataBlockSize; + } + + // + // If we failed, clean up. + // + if (!NT_SUCCESS (Status)) { + if (m_WaveData) { + ExFreePool (m_WaveData); + m_WaveData = NULL; + } + } + + if (FileHandle) { + ZwClose (FileHandle); + } + + return Status; +} + +/*************************************************/ + + +void +CWaveObject:: +WriteRange ( + OUT PKSDATARANGE_AUDIO DataRange + ) + +/*++ + +Routine Description: + + Fill out the extended portion of the audio data range at DataRange. This + includes the channel, bps, and frequency fields. + +Arguments: + + DataRange - + The data range to fill out + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + DataRange -> MaximumChannels = m_WaveFormat.nChannels; + DataRange -> MinimumBitsPerSample = + DataRange -> MaximumBitsPerSample = + m_WaveFormat.wBitsPerSample; + DataRange -> MinimumSampleFrequency = + DataRange -> MaximumSampleFrequency = + m_WaveFormat.nSamplesPerSec; + + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +void +CWaveObject:: +SkipFixed ( + IN LONGLONG TimeDelta + ) + +/*++ + +Routine Description: + + Skip ahead a specific time delta within the wave. + +Arguments: + + TimeDelta - + The amount of time to skip ahead. + +--*/ + +{ + if (TimeDelta > 0) { + + // + // Compute the number of bytes of audio data necessary to move the + // stream forward TimeDelta time. Remember that TimeDelta is in + // units of 100nS. + // + ULONG Samples = (ULONG)( + (m_WaveFormat.nSamplesPerSec * TimeDelta) / 10000000 + ); + + ULONG Bytes = Samples * (m_WaveFormat.wBitsPerSample / 8) * + m_WaveFormat.nChannels; + + m_WavePointer = (m_WavePointer + Bytes) % m_WaveSize; + + m_SynthesisTime += TimeDelta; + + } + +} + + +ULONG +CWaveObject:: +SynthesizeFixed ( + IN LONGLONG TimeDelta, + IN PVOID Buffer, + IN ULONG BufferSize + ) + +/*++ + +Routine Description: + + Copy wave data from our wave block in order to synthesize forward in time + TimeDelta (in 100nS units). + +Arguments: + + TimeDelta - + The amount of time to move the stream (in 100nS increments) + + Buffer - + The buffer to synthesize into + + BufferSize - + The size of the buffer + +Return Value: + + Number of bytes synthesized. + +--*/ + +{ + + // + // If there is no time delta, return 0. + // + if (TimeDelta < 0) + return 0; + + // + // Compute the number of bytes of audio data necessary to move the stream + // forward TimeDelta time. Remember that TimeDelta is in units of 100nS. + // + ULONG Samples = (ULONG)( + (m_WaveFormat.nSamplesPerSec * TimeDelta) / 10000000 + ); + + ULONG Bytes = Samples * (m_WaveFormat.wBitsPerSample / 8) * + m_WaveFormat.nChannels; + + // + // Now that we have a specified number of bytes, we determine how many + // to really copy based on the Size of the buffer. + // + if (Bytes > BufferSize) Bytes = BufferSize; + + // + // Because the buffer is looping, this may multiple distinct copies. For + // large wave files, this may be two chunks. For small wave files, this + // may be MANY distinct chunks. + // + ULONG BytesRemaining = Bytes; + PUCHAR DataCopy = reinterpret_cast <PUCHAR> (Buffer); + + while (BytesRemaining) { + ULONG ChunkCount = m_WaveSize - m_WavePointer; + if (ChunkCount > BytesRemaining) ChunkCount = BytesRemaining; + + RtlCopyMemory ( + DataCopy, + m_WaveData + m_WavePointer, + ChunkCount + ); + + m_WavePointer += ChunkCount; + if (m_WavePointer >= m_WaveSize) m_WavePointer -= m_WaveSize; + + BytesRemaining -= ChunkCount; + DataCopy += ChunkCount; + + } + + // + // Consider that we have synthesized up to the specified time. If the + // buffer was not large enough to do this, we'll end up falling behind + // the synthesis time. This does not skip samples. + // + m_SynthesisTime += TimeDelta; + + return Bytes; + +} + + +ULONG +CWaveObject:: +SynthesizeTo ( + IN LONGLONG StreamTime, + IN PVOID Buffer, + IN ULONG BufferSize + ) + +/*++ + +Routine Description: + + Copy wave data from our wave block in order to synthesize the stream + up to the specified stream time. If the buffers are not large enough, + this will fall behind on synthesis. + +Arguments: + + StreamTime - + The time to synthesize up to + + Buffer - + The buffer to copy synthesized wave data into + + BufferSize - + The size of the buffer + +Return Value: + + The number of bytes used. + +--*/ + +{ + + LONGLONG TimeDelta = StreamTime - m_SynthesisTime; + + return SynthesizeFixed (TimeDelta, Buffer, BufferSize); + + +} + diff --git a/AVStream/avssamp/wave.h b/AVStream/avssamp/wave.h new file mode 100644 index 00000000..d8546e3e --- /dev/null +++ b/AVStream/avssamp/wave.h @@ -0,0 +1,192 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + wave.h + + Abstract: + + Wave object header. + + History: + + Created 6/28/01 + +**************************************************************************/ + +// +// The CWaveObject is a class which will parse PCM wave files, read the +// data, and expose the data in a loop. This allows the sample to "synthesize" +// audio data by using any PCM wave file the user wishes. +// +class CWaveObject { + +private: + + // + // The wave format. + // + WAVEFORMATEX m_WaveFormat; + + // + // The wave data. + // + PUCHAR m_WaveData; + + // + // The size of the wave data. + // + ULONG m_WaveSize; + + // + // The filename for the wave file. This string must be constant and + // static over the lifetime of the wave object. + // + PWCHAR m_FileName; + + // + // The time we have synthesized to. + // + LONGLONG m_SynthesisTime; + + // + // The pointer into the wave data that we have synthesized to. + // + ULONG m_WavePointer; + + // + // ParseBlock(): + // + // Parse the wave file, starting at the specified location, until the + // specified block has been found. The pointer will be updated to + // point to the block data and the amount of data in the block will + // be returned in a variable. + // + NTSTATUS + ParseForBlock ( + IN HANDLE FileHandle, + IN ULONG BlockHeader, + IN OUT PLARGE_INTEGER BlockPointer, + OUT PULONG BlockSize + ); + +public: + + // + // CWaveObject(): + // + // Construct a new wave object using the specified file name. + // + CWaveObject ( + _In_ LPWSTR FileName + ) : + m_FileName (FileName) + { + m_WaveData = NULL; + } + + // + // ~CWaveObject(): + // + // Destroy a wave object. + // + ~CWaveObject ( + ); + + // + // ParseAndRead(): + // + // Parse the wave file and read it into an internally allocated buffer + // inside the wave object. This is preparation to synthesize looped + // audio based on the wave. + // + NTSTATUS + ParseAndRead ( + ); + + // + // WriteRange(): + // + // Given the address of a KSDATARANGE_AUDIO, write out a range which + // matches exactly the specifications of the wave we're using to + // synthesize audio data. + // + // The GUIDs must be filled out already. This only fills out the + // channel, bps, and freq fields. + // + void + WriteRange ( + PKSDATARANGE_AUDIO AudioRange + ); + + // + // SynthesizeTo(): + // + // Given a specific stream time, synthesize from the current stream time + // (assume 0) to the supplied stream time. + // + ULONG + SynthesizeTo ( + IN LONGLONG StreamTime, + IN PVOID Data, + IN ULONG BufferSize + ); + + // + // SynthesizeFixed(): + // + // Given a specific amount of time, synthesize forward in time that + // particular amount. Units expressed in 100nS increments. + // + ULONG + SynthesizeFixed ( + IN LONGLONG TimeDelta, + IN PVOID Data, + IN ULONG BufferSize + ); + + // + // SkipFixed(): + // + // Given a specific amount of time, skip forward in time that + // particular amount. Units expressed in 100nS increments. + // + void + SkipFixed ( + IN LONGLONG TimeDelta + ); + + // + // Reset(): + // + // Reset the synthesis time and block pointers. This will cause the + // clock with respect to this wave object to go to zero. + // + void + Reset ( + ) + { + m_WavePointer = 0; + m_SynthesisTime = 0; + } + + // + // Cleanup(): + // + // This is a bag cleanup callback. It merely deletes the wave object + // instead of letting the default of ExFreePool free it. + // + static + void + Cleanup ( + IN CWaveObject *This + ) + { + delete This; + } + +}; |
