909 lines
33 KiB
C++
909 lines
33 KiB
C++
/**************************************************************************
|
|
**
|
|
** THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
** KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
** IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
** PURPOSE.
|
|
**
|
|
** Copyright (c) 2000-2001 Microsoft Corporation. All Rights Reserved.
|
|
**
|
|
**************************************************************************/
|
|
|
|
//
|
|
// The pin descriptors (static structures) are in filter.cpp
|
|
//
|
|
|
|
//
|
|
// Every debug output has "Modulname text"
|
|
//
|
|
static char STR_MODULENAME[] = "GFX pin: ";
|
|
|
|
#include "common.h"
|
|
#include <msgfx.h>
|
|
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::ValidateDataFormat
|
|
*****************************************************************************
|
|
* Checks if the passed data format is in the data range passed in. The data
|
|
* range is one of our own data ranges that we defined for the pin and the
|
|
* data format is the requested data format for creating a stream or changing
|
|
* the data format (SetDataFormat).
|
|
*/
|
|
NTSTATUS CGFXPin::ValidateDataFormat
|
|
(
|
|
IN PKSDATAFORMAT dataFormat,
|
|
IN PKSDATARANGE dataRange
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (dataFormat);
|
|
|
|
DOUT (DBG_PRINT, ("[ValidateDataFormat]"));
|
|
|
|
//
|
|
// KSDATAFORMAT contains three GUIDs to support extensible format. The
|
|
// first two GUIDs identify the type of data. The third indicates the
|
|
// type of specifier used to indicate format specifics.
|
|
// KS makes sure that it doesn't call the driver with any data format
|
|
// that doesn't match the GUIDs in the data range of the pin. That
|
|
// means we don't have to check this here again.
|
|
//
|
|
|
|
PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(dataFormat + 1);
|
|
PKSDATARANGE_AUDIO audioDataRange = (PKSDATARANGE_AUDIO)dataRange;
|
|
|
|
//
|
|
// We are only supporting PCM audio formats that use WAVEFORMATEX.
|
|
//
|
|
// If the size doesn't match, then something is messed up.
|
|
//
|
|
if (dataFormat->FormatSize < (sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[ValidateDataFormat] Invalid FormatSize!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Print the information.
|
|
//
|
|
if (waveFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
|
|
{
|
|
DOUT (DBG_STREAM, ("[ValidateDataFormat] PCMEX - Frequency: %d, Channels: %d, bps: %d, ChannelMask: %X",
|
|
waveFormat->Format.nSamplesPerSec, waveFormat->Format.nChannels,
|
|
waveFormat->Format.wBitsPerSample, waveFormat->dwChannelMask));
|
|
}
|
|
else
|
|
{
|
|
DOUT (DBG_STREAM, ("[ValidateDataFormat] PCM - Frequency: %d, Channels: %d, bps: %d",
|
|
waveFormat->Format.nSamplesPerSec, waveFormat->Format.nChannels,
|
|
waveFormat->Format.wBitsPerSample));
|
|
}
|
|
|
|
//
|
|
// Compare the data format with the data range.
|
|
// Check the bits per sample.
|
|
//
|
|
if ((waveFormat->Format.wBitsPerSample < audioDataRange->MinimumBitsPerSample) ||
|
|
(waveFormat->Format.wBitsPerSample > audioDataRange->MaximumBitsPerSample))
|
|
{
|
|
DOUT (DBG_PRINT, ("[ValidateDataFormat] No match for Bits Per Sample!"));
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
//
|
|
// Check the number of channels.
|
|
//
|
|
if ((waveFormat->Format.nChannels < 1) ||
|
|
(waveFormat->Format.nChannels > audioDataRange->MaximumChannels))
|
|
{
|
|
DOUT (DBG_PRINT, ("[ValidateDataFormat] No match for Number of Channels!"));
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
//
|
|
// Check the sample frequency.
|
|
//
|
|
if ((waveFormat->Format.nSamplesPerSec < audioDataRange->MinimumSampleFrequency) ||
|
|
(waveFormat->Format.nSamplesPerSec > audioDataRange->MaximumSampleFrequency))
|
|
{
|
|
DOUT (DBG_PRINT, ("[ValidateDataFormat] No match for Sample Frequency!"));
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
//
|
|
// We support WaveFormatPCMEX (=WAVEFORMATEXTENSIBLE) or WaveFormatPCM.
|
|
// In case of WaveFormatPCMEX we need to check the speaker config too.
|
|
//
|
|
if ((waveFormat->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) &&
|
|
(waveFormat->Format.wFormatTag != WAVE_FORMAT_PCM))
|
|
{
|
|
DOUT (DBG_WARNING, ("[ValidateDataFormat] Invalid Format Tag!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Make additional checks for the WAVEFORMATEXTENSIBLE
|
|
//
|
|
if (waveFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
|
|
{
|
|
//
|
|
// If the size doesn't match, then something is messed up.
|
|
//
|
|
if (dataFormat->FormatSize < (sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[ValidateDataFormat] Invalid FormatSize!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check also the subtype (PCM) and the size of the extended data.
|
|
//
|
|
if (!IsEqualGUIDAligned (waveFormat->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) ||
|
|
(waveFormat->Format.cbSize < 22))
|
|
{
|
|
DOUT (DBG_WARNING, ("[ValidateDataFormat] Unsupported WAVEFORMATEXTENSIBLE!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check the channel mask. We support 1 or 2 channels.
|
|
//
|
|
if (((waveFormat->Format.nChannels == 1) &&
|
|
(waveFormat->dwChannelMask != KSAUDIO_SPEAKER_MONO)) ||
|
|
((waveFormat->Format.nChannels == 2) &&
|
|
(waveFormat->dwChannelMask != KSAUDIO_SPEAKER_STEREO)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[ValidateDataFormat] Unsupported Channel Mask!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::Create
|
|
*****************************************************************************
|
|
* This function is called once a pin gets opened.
|
|
*/
|
|
NTSTATUS CGFXPin::Create
|
|
(
|
|
IN PKSPIN pin,
|
|
IN PIRP irp
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
PGFXPIN gfxPin;
|
|
|
|
DOUT (DBG_PRINT, ("[Create]"));
|
|
|
|
//
|
|
// The pin context is the filter's context. We overwrite it with
|
|
// the pin object.
|
|
//
|
|
gfxPin = new (NonPagedPool, GFXSWAP_POOL_TAG) GFXPIN;
|
|
if (gfxPin == NULL)
|
|
{
|
|
DOUT (DBG_ERROR, ("[Create] couldn't allocate gfx pin object."));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Attach it to the pin structure.
|
|
//
|
|
pin->Context = (PVOID)gfxPin;
|
|
DOUT (DBG_PRINT, ("[Create] gfxPin %08x", gfxPin));
|
|
|
|
//
|
|
// Initialize the CGFXPin object variables.
|
|
//
|
|
ExInitializeFastMutex (&gfxPin->pinQueueSync);
|
|
|
|
//
|
|
// Get the OS version info
|
|
//
|
|
RTL_OSVERSIONINFOEXW version;
|
|
version.dwOSVersionInfoSize = sizeof (RTL_OSVERSIONINFOEXW);
|
|
RtlGetVersion ((PRTL_OSVERSIONINFOW)&version);
|
|
|
|
//
|
|
// If we are running under the first release of Windows XP,
|
|
// KsPinGetAvailableByteCount has a bug so that we can't use it.
|
|
// We only use this function in SetDataFormat, so we just reject
|
|
// all data format changes. Otherwise, if a service pack is installed
|
|
// or Windows .NET or a later version of Windows XP we can use the
|
|
// function.
|
|
//
|
|
if (version.dwBuildNumber > 2600)
|
|
gfxPin->rejectDataFormatChange = FALSE;
|
|
else
|
|
{
|
|
if (version.wServicePackMajor > 0)
|
|
gfxPin->rejectDataFormatChange = FALSE;
|
|
else
|
|
gfxPin->rejectDataFormatChange = TRUE;
|
|
}
|
|
DOUT (DBG_SYSTEM,
|
|
("[Create] OS build number: %d, version: %d.%d, service pack: %d.%d",
|
|
version.dwBuildNumber, version.dwMajorVersion, version.dwMinorVersion,
|
|
version.wServicePackMajor, version.wServicePackMinor));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::Close
|
|
*****************************************************************************
|
|
* This routine is called when a pin is closed. It deletes the
|
|
* client pin object attached to the pin structure.
|
|
*
|
|
* Arguments:
|
|
* pin - Contains a pointer to the pin structure.
|
|
* pIrp - Contains a pointer to the close request.
|
|
*
|
|
* Return Value:
|
|
* STATUS_SUCCESS.
|
|
*/
|
|
NTSTATUS CGFXPin::Close
|
|
(
|
|
IN PKSPIN pin,
|
|
IN PIRP irp
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
DOUT (DBG_PRINT, ("[Close] gfxPin %08x", pin->Context));
|
|
|
|
// delete is safe with NULL pointers.
|
|
delete (PGFXPIN)pin->Context;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::SetDataFormat
|
|
*****************************************************************************
|
|
* This function is called on the pin everytime the data format should change.
|
|
* It is also called just before the pin gets created with the new data format.
|
|
* Therefore, we don't need to have a pin Create dispatch function just to
|
|
* check the data format.
|
|
* Since we need to have both pins running at the same data format, we need
|
|
* to pass down the request to change the pin's data format to the lower
|
|
* driver, which would be the audio driver. If the audio driver fails to change
|
|
* the data format, we will do so too.
|
|
*/
|
|
NTSTATUS CGFXPin::SetDataFormat
|
|
(
|
|
IN PKSPIN pin,
|
|
IN PKSDATAFORMAT oldFormat,
|
|
IN PKSMULTIPLE_ITEM oldAttributeList,
|
|
IN const KSDATARANGE *dataRange,
|
|
IN const KSATTRIBUTE_LIST *attributeRange
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (pin);
|
|
|
|
NTSTATUS ntStatus;
|
|
PKSFILTER filter;
|
|
PKSPIN otherPin;
|
|
PGFXPIN gfxPin = NULL;
|
|
|
|
DOUT (DBG_PRINT, ("[GFXPinSetDataFormat]"));
|
|
|
|
//
|
|
// First validate if the requested data format is valid.
|
|
//
|
|
ntStatus = ValidateDataFormat (pin->ConnectionFormat, (PKSDATARANGE)dataRange);
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// We need to have the same data format on both pins.
|
|
// That means we need to get to the other pin and if this pin is created
|
|
// make sure that the lower level driver (audio driver) gets a SetDataFormat
|
|
// too.
|
|
//
|
|
|
|
//
|
|
// We hold the filter control mutex already.
|
|
//
|
|
filter = KsPinGetParentFilter (pin);
|
|
|
|
//
|
|
// Now get to the other pin. If this property was called on the sink
|
|
// pin, then we get the source pin and continue. If it was called on
|
|
// the source pin we go to the sink pin and continue.
|
|
// To check if the pin really exists you look at the OldFormat which
|
|
// is passed in. If it's a creation of the pin the OldFormat will be
|
|
// NULL.
|
|
// If the other pin doesn't exist we accept the format since it passed
|
|
// the format check.
|
|
//
|
|
if (pin->Id == GFX_SINK_PIN)
|
|
{
|
|
otherPin = KsFilterGetFirstChildPin (filter, GFX_SOURCE_PIN);
|
|
if (oldFormat)
|
|
gfxPin = (PGFXPIN)pin->Context;
|
|
}
|
|
else // It's a source pin
|
|
{
|
|
otherPin = KsFilterGetFirstChildPin (filter, GFX_SINK_PIN);
|
|
if (otherPin)
|
|
gfxPin = (PGFXPIN)otherPin->Context;
|
|
}
|
|
|
|
//
|
|
// If there is no other pin open, accept the data format.
|
|
//
|
|
if (!otherPin)
|
|
{
|
|
DOUT (DBG_PRINT, ("[GFXPinSetDataFormat] data format accepted."));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Check if the data format if equal for both pins.
|
|
// We cannot just compare the memory of the data format structure
|
|
// since one could be WAVEFORMATEX and the other one WAVEFORMATPCMEX,
|
|
// but we also know that these are the only formats that we accept,
|
|
// so compare their values now.
|
|
//
|
|
PWAVEFORMATEX thisWaveFmt = (PWAVEFORMATEX)(pin->ConnectionFormat + 1);
|
|
PWAVEFORMATEX otherWaveFmt = (PWAVEFORMATEX)(otherPin->ConnectionFormat + 1);
|
|
|
|
if ((thisWaveFmt->nChannels == otherWaveFmt->nChannels) &&
|
|
(thisWaveFmt->nSamplesPerSec == otherWaveFmt->nSamplesPerSec) &&
|
|
(thisWaveFmt->wBitsPerSample == otherWaveFmt->wBitsPerSample))
|
|
{
|
|
//
|
|
// We have a match right here.
|
|
//
|
|
DOUT (DBG_PRINT, ("[GFXPinSetDataFormat] data format accepted."));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// We don't have a match. We need to change the data format of the otherPin
|
|
// now and if that succeeds we can continue, otherwise we need to fail.
|
|
//
|
|
// Before we pass down the property however, we need to make sure that all
|
|
// buffers on the sink pin are processed (since they were sampled with
|
|
// the old data format).
|
|
//
|
|
LONG bytesQueuedUp = 0;
|
|
do
|
|
{
|
|
//
|
|
// We need to synchronize the call to KsPinGetAvailableByteCount
|
|
// with changes in the pin state (using the fast mutex) only on
|
|
// the sink pin.
|
|
//
|
|
if (gfxPin)
|
|
{
|
|
ExAcquireFastMutex (&gfxPin->pinQueueSync);
|
|
//
|
|
// In case we are not in STOP state, the pin queue should be there,
|
|
// otherwise it is destroyed (or in the process of destroying) and
|
|
// therefore we assume no buffers are waiting on the pin.
|
|
//
|
|
if (gfxPin->pinQueueValid)
|
|
{
|
|
//
|
|
// If we are running on a system without the KS fix, we
|
|
// need to reject the SetDataFormat because we want to
|
|
// prevent an unprocessed buffer from playing at the
|
|
// wrong sample frequency.
|
|
//
|
|
if (gfxPin->rejectDataFormatChange)
|
|
{
|
|
ExReleaseFastMutex (&gfxPin->pinQueueSync);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
KsPinGetAvailableByteCount (pin, &bytesQueuedUp, NULL);
|
|
}
|
|
else
|
|
bytesQueuedUp = 0;
|
|
ExReleaseFastMutex (&gfxPin->pinQueueSync);
|
|
}
|
|
|
|
//
|
|
// If we got some bytes queued on the sink pin yield for 1ms.
|
|
//
|
|
if (bytesQueuedUp)
|
|
{
|
|
LARGE_INTEGER timeToWait;
|
|
|
|
DOUT (DBG_STREAM, ("[GFXPinSetDataFormat] %d Bytes left to process.\n", bytesQueuedUp));
|
|
timeToWait.QuadPart = -10000; // one ms
|
|
KeDelayExecutionThread (KernelMode, FALSE, &timeToWait);
|
|
}
|
|
} while (bytesQueuedUp);
|
|
|
|
//
|
|
// Now that every data frame on the sink pin is processed and passed
|
|
// down the stack we can call down with the property too.
|
|
//
|
|
KSPROPERTY property;
|
|
PIKSCONTROL pIKsControl;
|
|
ULONG cbReturned;
|
|
|
|
property.Set = KSPROPSETID_Connection;
|
|
property.Id = KSPROPERTY_CONNECTION_DATAFORMAT;
|
|
property.Flags = KSPROPERTY_TYPE_SET;
|
|
|
|
//
|
|
// Get a control interface to the pin that is connected with otherPin.
|
|
//
|
|
ntStatus = KsPinGetConnectedPinInterface (otherPin, &IID_IKsControl, (PVOID*)&pIKsControl);
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("[GFXPinSetDataFormat] Could not get pin interface."));
|
|
return ntStatus;
|
|
}
|
|
|
|
// Always release the mutex before calling down.
|
|
KsFilterReleaseControl (filter);
|
|
|
|
//
|
|
// Call the interface with KSPROPERTY_CONNECTION_DATAFORMAT.
|
|
// Pass in our pin data format as the data format.
|
|
//
|
|
ntStatus = pIKsControl->KsProperty (&property, sizeof(property),
|
|
pin->ConnectionFormat, pin->ConnectionFormat->FormatSize,
|
|
&cbReturned);
|
|
|
|
// Get the control of the filter back!
|
|
KsFilterAcquireControl (filter);
|
|
|
|
//
|
|
// We don't need this interface anymore.
|
|
//
|
|
pIKsControl->Release();
|
|
|
|
//
|
|
// Return the error code from the KsProperty call. If the connected pin
|
|
// changed seccessfully the data format then we can accept this data
|
|
// format too.
|
|
//
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::SetDeviceState
|
|
*****************************************************************************
|
|
* This function is called on the pin everytime the device state changes.
|
|
*/
|
|
NTSTATUS CGFXPin::SetDeviceState
|
|
(
|
|
IN PKSPIN pin,
|
|
IN KSSTATE toState,
|
|
IN KSSTATE fromState
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (pin);
|
|
ASSERT (pin->Context);
|
|
|
|
PKSFILTER filter;
|
|
PGFXFILTER gfxFilter;
|
|
PGFXPIN gfxPin = (PGFXPIN)pin->Context;
|
|
|
|
DOUT (DBG_PRINT, ("[GFXPinSetDeviceState]"));
|
|
|
|
//
|
|
// We hold the filter control mutex already. Get the filter and that
|
|
// way to the bytesProcessed variable.
|
|
//
|
|
filter = KsPinGetParentFilter (pin);
|
|
gfxFilter = (PGFXFILTER)filter->Context;
|
|
|
|
//
|
|
// We only need to reset the byte counter on STOP.
|
|
// In addition, for synchronization with the set data format handler,
|
|
// we need to set pinQueueValid variable.
|
|
//
|
|
ExAcquireFastMutex (&gfxPin->pinQueueSync);
|
|
if (toState == KSSTATE_STOP)
|
|
{
|
|
gfxFilter->bytesProcessed = 0;
|
|
gfxPin->pinQueueValid = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gfxPin->pinQueueValid = TRUE;
|
|
}
|
|
ExReleaseFastMutex (&gfxPin->pinQueueSync);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::IntersectDataRanges
|
|
*****************************************************************************
|
|
* This routine performs a data range intersection between 2 specific formats.
|
|
* It assumes that it can always return a WAVEFORMATPCMEX structure, that
|
|
* means that the data ranges of this filter cannot be anything else than
|
|
* KSDATAFORMAT_SPECIFIER_WAVEFORMATEX.
|
|
* This function will return STATUS_NO_MATCH if there is no intersection
|
|
* between the 2 data ranges and it will return the "highest" data format if
|
|
* the client's data range contains wildcards.
|
|
*
|
|
* Arguments:
|
|
* clientDataRange - 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.
|
|
* myDataRange - 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
|
|
* clientDataRange.
|
|
* ResultantFormat - pointer to the buffer to contain the data format
|
|
* structure representing the best format in the
|
|
* intersection of the two data ranges. The buffer is
|
|
* big enough to hold a WAVEFORMATPCMEX structure.
|
|
* ReturnedBytes - pointer to ULONG containing the number of bytes
|
|
* that this routine will write into ResultantFormat.
|
|
*
|
|
* Return Value:
|
|
* STATUS_SUCCESS if there is an intersection or STATUS_NO_MATCH.
|
|
*/
|
|
NTSTATUS CGFXPin::IntersectDataRanges
|
|
(
|
|
IN PKSDATARANGE clientDataRange,
|
|
IN PKSDATARANGE myDataRange,
|
|
OUT PVOID ResultantFormat,
|
|
OUT PULONG ReturnedBytes
|
|
)
|
|
{
|
|
DOUT (DBG_PRINT, ("[GFXPinIntersectDataRanges]"));
|
|
|
|
//
|
|
// Handle the wildcards. KS checked that the GUIDS will match either with
|
|
// a wildcard or exactly.
|
|
//
|
|
if (IsEqualGUIDAligned (clientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD))
|
|
{
|
|
//
|
|
// If there is a wildcard passed in and all the other fields fit, then we can
|
|
// return the best format in the current data range.
|
|
//
|
|
|
|
// First copy the GUIDs
|
|
*(PKSDATAFORMAT)ResultantFormat = *myDataRange;
|
|
|
|
//
|
|
// Append the WAVEFORMATPCMEX structure.
|
|
//
|
|
PWAVEFORMATPCMEX WaveFormat = (PWAVEFORMATPCMEX)((PKSDATAFORMAT)ResultantFormat + 1);
|
|
|
|
// We want a WAFEFORMATEXTENSIBLE which is equal to WAVEFORMATPCMEX.
|
|
WaveFormat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
// Set the number of channels
|
|
WaveFormat->Format.nChannels = (WORD)((PKSDATARANGE_AUDIO)myDataRange)->MaximumChannels;
|
|
// Set the sample frequency
|
|
WaveFormat->Format.nSamplesPerSec = ((PKSDATARANGE_AUDIO)myDataRange)->MaximumSampleFrequency;
|
|
// Set the bits per sample
|
|
WaveFormat->Format.wBitsPerSample = (WORD)((PKSDATARANGE_AUDIO)myDataRange)->MaximumBitsPerSample;
|
|
// Calculate one sample block (a frame).
|
|
WaveFormat->Format.nBlockAlign = (WaveFormat->Format.wBitsPerSample * WaveFormat->Format.nChannels) / 8;
|
|
// That is played in a sec.
|
|
WaveFormat->Format.nAvgBytesPerSec = WaveFormat->Format.nSamplesPerSec * WaveFormat->Format.nBlockAlign;
|
|
// WAVEFORMATPCMEX
|
|
WaveFormat->Format.cbSize = 22;
|
|
// We have as many valid bits as the bit depth is.
|
|
WaveFormat->Samples.wValidBitsPerSample = WaveFormat->Format.wBitsPerSample;
|
|
// Set the channel mask
|
|
ASSERT (WaveFormat->dwChannelMask == 2);
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
|
// Here we specify the subtype of the WAVEFORMATEXTENSIBLE.
|
|
WaveFormat->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
|
|
//
|
|
// Modify the size of the data format structure to fit the WAVEFORMATPCMEX
|
|
// structure.
|
|
//
|
|
((PKSDATAFORMAT)ResultantFormat)->FormatSize =
|
|
sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
|
|
//
|
|
// Now overwrite also the sample size in the KSDATAFORMAT structure.
|
|
//
|
|
((PKSDATAFORMAT)ResultantFormat)->SampleSize = WaveFormat->Format.nBlockAlign;
|
|
|
|
//
|
|
// That we will return.
|
|
//
|
|
*ReturnedBytes = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check the passed data range format.
|
|
//
|
|
if (clientDataRange->FormatSize < sizeof(KSDATARANGE_AUDIO))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// Verify that we have an intersection with the specified data range and
|
|
// our audio data range.
|
|
//
|
|
if ((((PKSDATARANGE_AUDIO)clientDataRange)->MinimumSampleFrequency >
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MaximumSampleFrequency) ||
|
|
(((PKSDATARANGE_AUDIO)clientDataRange)->MaximumSampleFrequency <
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MinimumSampleFrequency) ||
|
|
(((PKSDATARANGE_AUDIO)clientDataRange)->MinimumBitsPerSample >
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MaximumBitsPerSample) ||
|
|
(((PKSDATARANGE_AUDIO)clientDataRange)->MaximumBitsPerSample <
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MinimumBitsPerSample))
|
|
{
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
//
|
|
// Since we have a match now, build the data format for our buddy.
|
|
//
|
|
|
|
// First copy the GUIDs
|
|
*(PKSDATAFORMAT)ResultantFormat = *myDataRange;
|
|
|
|
//
|
|
// Append the WAVEFORMATPCMEX structure.
|
|
//
|
|
PWAVEFORMATPCMEX WaveFormat = (PWAVEFORMATPCMEX)((PKSDATAFORMAT)ResultantFormat + 1);
|
|
|
|
// We want a WAFEFORMATEXTENSIBLE which is equal to WAVEFORMATPCMEX.
|
|
WaveFormat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
// Set the number of channels
|
|
WaveFormat->Format.nChannels = (WORD)
|
|
min (((PKSDATARANGE_AUDIO)clientDataRange)->MaximumChannels,
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MaximumChannels);
|
|
// Set the sample frequency
|
|
WaveFormat->Format.nSamplesPerSec =
|
|
min (((PKSDATARANGE_AUDIO)clientDataRange)->MaximumSampleFrequency,
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MaximumSampleFrequency);
|
|
// Set the bits per sample
|
|
WaveFormat->Format.wBitsPerSample = (WORD)
|
|
min (((PKSDATARANGE_AUDIO)clientDataRange)->MaximumBitsPerSample,
|
|
((PKSDATARANGE_AUDIO)myDataRange)->MaximumBitsPerSample);
|
|
// Calculate one sample block (a frame).
|
|
WaveFormat->Format.nBlockAlign = (WaveFormat->Format.wBitsPerSample * WaveFormat->Format.nChannels) / 8;
|
|
// That is played in a sec.
|
|
WaveFormat->Format.nAvgBytesPerSec = WaveFormat->Format.nSamplesPerSec * WaveFormat->Format.nBlockAlign;
|
|
// WAVEFORMATPCMEX
|
|
WaveFormat->Format.cbSize = 22;
|
|
// We have as many valid bits as the bit depth is.
|
|
WaveFormat->Samples.wValidBitsPerSample = WaveFormat->Format.wBitsPerSample;
|
|
// Set the channel mask
|
|
if (WaveFormat->Format.nChannels == 1)
|
|
{
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_MONO;
|
|
}
|
|
else
|
|
{
|
|
// We can have only 1 or 2 channels in this sample.
|
|
ASSERT (WaveFormat->Format.nChannels == 2);
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
|
}
|
|
// Here we specify the subtype of the WAVEFORMATEXTENSIBLE.
|
|
WaveFormat->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
|
|
//
|
|
// Modify the size of the data format structure to fit the WAVEFORMATPCMEX
|
|
// structure.
|
|
//
|
|
((PKSDATAFORMAT)ResultantFormat)->FormatSize =
|
|
sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
|
|
//
|
|
// Now overwrite also the sample size in the KSDATAFORMAT structure.
|
|
//
|
|
((PKSDATAFORMAT)ResultantFormat)->SampleSize = WaveFormat->Format.nBlockAlign;
|
|
|
|
//
|
|
// That we will return.
|
|
//
|
|
*ReturnedBytes = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CGFXPin::DataRangeIntersection
|
|
*****************************************************************************
|
|
* This routine handles pin data intersection queries by determining the
|
|
* intersection between two data ranges.
|
|
*
|
|
* Arguments:
|
|
* Filter - void pointer to the filter structure.
|
|
* Irp - pointer to the data intersection property request.
|
|
* PinInstance - pointer to a structure indicating the pin in question.
|
|
* CallerDataRange - 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.
|
|
* OurDataRange - 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 - size in bytes of the buffer pointed to by the Data
|
|
* argument. For size queries, this value will be zero.
|
|
* Data - optionall. 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 - 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.
|
|
*/
|
|
NTSTATUS CGFXPin::DataRangeIntersection
|
|
(
|
|
IN PVOID Filter,
|
|
IN PIRP Irp,
|
|
IN PKSP_PIN PinInstance,
|
|
IN PKSDATARANGE CallerDataRange,
|
|
IN PKSDATARANGE OurDataRange,
|
|
IN ULONG BufferSize,
|
|
OUT PVOID Data OPTIONAL,
|
|
OUT PULONG DataSize
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PKSFILTER filter = (PKSFILTER) Filter;
|
|
PKSPIN pin;
|
|
NTSTATUS ntStatus;
|
|
|
|
DOUT (DBG_PRINT, ("[DataRangeIntersection]"));
|
|
|
|
ASSERT(Filter);
|
|
ASSERT(Irp);
|
|
ASSERT(PinInstance);
|
|
ASSERT(CallerDataRange);
|
|
ASSERT(OurDataRange);
|
|
ASSERT(DataSize);
|
|
|
|
//
|
|
// We need to have the same data format on both pins. So, first look if
|
|
// the other pin is already open, then return the data format of that
|
|
// pin instance.
|
|
// If the other pin is not open, do a real data range intersection.
|
|
//
|
|
if (PinInstance->PinId == GFX_SINK_PIN)
|
|
{
|
|
pin = KsFilterGetFirstChildPin (filter, GFX_SOURCE_PIN);
|
|
}
|
|
else
|
|
{
|
|
pin = KsFilterGetFirstChildPin (filter, GFX_SINK_PIN);
|
|
}
|
|
|
|
if (!pin)
|
|
{
|
|
//
|
|
// Do the data range instersection here. The returned data format
|
|
// will always be a KSDATAFORMAT_WAVEFORMATPCMEX for now.
|
|
//
|
|
|
|
//
|
|
// Validate return buffer size, if the request is only for the
|
|
// size of the resultant structure, return it now.
|
|
//
|
|
if (!BufferSize)
|
|
{
|
|
*DataSize = sizeof (KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
ntStatus = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if (BufferSize < (sizeof (KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX)))
|
|
{
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if there is a match.
|
|
//
|
|
ntStatus = IntersectDataRanges (CallerDataRange, OurDataRange, Data, DataSize);
|
|
|
|
if (NT_SUCCESS (ntStatus))
|
|
{
|
|
PWAVEFORMATEX pWvFmt = (PWAVEFORMATEX)((PKSDATAFORMAT)Data + 1);
|
|
DOUT (DBG_PRINT, ("[DataRangeIntersection] Intersection returns %d Hz, %d ch, %d bits.",
|
|
pWvFmt->nSamplesPerSec, (DWORD)pWvFmt->nChannels, (DWORD)pWvFmt->wBitsPerSample));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Validate that the current wave format is part of the data range.
|
|
//
|
|
PWAVEFORMATEX pWvFmt = (PWAVEFORMATEX)(pin->ConnectionFormat + 1);
|
|
if (IsEqualGUIDAligned (CallerDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
|
|
{
|
|
//
|
|
// Check the passed data range format.
|
|
//
|
|
if (CallerDataRange->FormatSize < sizeof(KSDATARANGE_AUDIO))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// Check the range of channels, frequency & bit depth.
|
|
//
|
|
if ((((PKSDATARANGE_AUDIO)CallerDataRange)->MinimumSampleFrequency >
|
|
pWvFmt->nSamplesPerSec) ||
|
|
(((PKSDATARANGE_AUDIO)CallerDataRange)->MaximumSampleFrequency <
|
|
pWvFmt->nSamplesPerSec) ||
|
|
(((PKSDATARANGE_AUDIO)CallerDataRange)->MinimumBitsPerSample >
|
|
pWvFmt->wBitsPerSample) ||
|
|
(((PKSDATARANGE_AUDIO)CallerDataRange)->MaximumBitsPerSample <
|
|
pWvFmt->wBitsPerSample) ||
|
|
(((PKSDATARANGE_AUDIO)CallerDataRange)->MaximumChannels <
|
|
pWvFmt->nChannels))
|
|
{
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsEqualGUIDAligned (CallerDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD))
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate return buffer size, if the request is only for the
|
|
// size of the resultant structure, return it now.
|
|
//
|
|
if (!BufferSize)
|
|
{
|
|
*DataSize = pin->ConnectionFormat->FormatSize;
|
|
ntStatus = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if (BufferSize < pin->ConnectionFormat->FormatSize)
|
|
{
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
DOUT (DBG_PRINT, ("[DataRangeIntersection] Returning pin's data format."));
|
|
DOUT (DBG_PRINT, ("[DataRangeIntersection] pin->ConnectionFormat: %P.",
|
|
pin->ConnectionFormat));
|
|
|
|
if (pin->ConnectionFormat->FormatSize >= sizeof (KSDATAFORMAT_WAVEFORMATEX))
|
|
{
|
|
DOUT (DBG_PRINT, ("[DataRangeIntersection] Which is %d Hz, %d ch, %d bits.",
|
|
pWvFmt->nSamplesPerSec, (DWORD)pWvFmt->nChannels, (DWORD)pWvFmt->wBitsPerSample));
|
|
}
|
|
|
|
*DataSize = pin->ConnectionFormat->FormatSize;
|
|
RtlCopyMemory (Data, pin->ConnectionFormat, *DataSize);
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|