1246 lines
33 KiB
C++
1246 lines
33 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// Module: sni.cpp
|
|
//
|
|
// Description:
|
|
//
|
|
// Start Node Instance
|
|
//
|
|
//@@BEGIN_MSINTERNAL
|
|
// Development Team:
|
|
// Mike McLaughlin
|
|
//
|
|
// History: Date Author Comment
|
|
//
|
|
// To Do: Date Author Comment
|
|
//
|
|
//@@END_MSINTERNAL
|
|
//
|
|
// 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) 1996-1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "common.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
WAVEFORMATEX aWaveFormatEx[] = {
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
44100, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
44100, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
44100, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
44100, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
48000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
48000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
48000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
48000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
32000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
32000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
32000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
32000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
22050, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
22050, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
22050, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
22050, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
16000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
16000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
16000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
16000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
11025, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
11025, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
11025, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
11025, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
8000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
8000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
16, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
2, // nChannels
|
|
8000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
{
|
|
WAVE_FORMAT_PCM, // wFormatTag
|
|
1, // nChannels
|
|
8000, // nSamplesPerSec
|
|
0, // nAvgBytesPerSec
|
|
0, // nBlockAlign
|
|
8, // wBitsPerSample
|
|
0, // cbSize
|
|
},
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::Create(
|
|
PPIN_INSTANCE pPinInstance,
|
|
PSTART_NODE pStartNode,
|
|
PKSPIN_CONNECT pPinConnect,
|
|
PWAVEFORMATEX pWaveFormatExRequested,
|
|
PWAVEFORMATEX pWaveFormatExRegistry
|
|
)
|
|
{
|
|
PSTART_NODE_INSTANCE pStartNodeInstance = NULL;
|
|
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
Assert(pPinInstance);
|
|
Assert(pStartNode);
|
|
Assert(pStartNode->pPinNode);
|
|
|
|
DPF3(90, "CSNI::Create SN %08x #%d %s",
|
|
pStartNode,
|
|
pStartNode->pPinNode->pPinInfo->PinId,
|
|
pStartNode->pPinNode->pPinInfo->pFilterNode->DumpName());
|
|
|
|
#ifdef DEBUG
|
|
DumpDataRange(95, (PKSDATARANGE_AUDIO)pStartNode->pPinNode->pDataRange);
|
|
#endif
|
|
|
|
if(!CompareIdentifier(
|
|
pStartNode->pPinNode->pMedium,
|
|
&pPinConnect->Medium)) {
|
|
Trap();
|
|
DPF1(90, "CSNI::Create: Medium %08X", pStartNode);
|
|
ASSERT(Status == STATUS_INVALID_DEVICE_REQUEST);
|
|
goto exit;
|
|
}
|
|
|
|
if(!CompareIdentifier(
|
|
pStartNode->pPinNode->pInterface,
|
|
&pPinConnect->Interface)) {
|
|
DPF1(90, "CSNI::Create: Interface %08X", pStartNode);
|
|
ASSERT(Status == STATUS_INVALID_DEVICE_REQUEST);
|
|
goto exit;
|
|
}
|
|
|
|
if(!CompareDataRangeGuids(
|
|
pStartNode->pPinNode->pDataRange,
|
|
(PKSDATARANGE)(pPinConnect + 1))) {
|
|
DPF1(90, "CSNI::Create: DataRange GUID %08X", pStartNode);
|
|
ASSERT(Status == STATUS_INVALID_DEVICE_REQUEST);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// VOICE MANAGEMENT and HW ACCELARATION
|
|
// For HW accelarated pins we are not relying on local sysaudio
|
|
// instance counts. PinCreate request will be sent down to the driver.
|
|
// It is upto the driver to reject the request based on its capabilities.
|
|
//
|
|
if ((pStartNode->pPinNode->pPinInfo->pFilterNode->GetType() & FILTER_TYPE_RENDERER) &&
|
|
(KSPIN_DATAFLOW_IN == pStartNode->pPinNode->pPinInfo->DataFlow) &&
|
|
(KSPIN_COMMUNICATION_SINK == pStartNode->pPinNode->pPinInfo->Communication)) {
|
|
|
|
DPF(20,"StartInfo::IsPinInstances return TRUE for HW");
|
|
}
|
|
else {
|
|
if(!pStartNode->IsPinInstances()) {
|
|
DPF1(90, "CSNI::Create: no instances SN %08X", pStartNode);
|
|
Status = STATUS_DEVICE_BUSY;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
pStartNodeInstance = new START_NODE_INSTANCE(pPinInstance, pStartNode);
|
|
if(pStartNodeInstance == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
#ifndef UNDER_NT
|
|
//
|
|
// Try the format from the registry
|
|
//
|
|
|
|
if(pWaveFormatExRegistry != NULL) {
|
|
DPF3(90, "CSNI::Create: Registry SR %d CH %d BPS %d",
|
|
pWaveFormatExRegistry->nSamplesPerSec,
|
|
pWaveFormatExRegistry->nChannels,
|
|
pWaveFormatExRegistry->wBitsPerSample);
|
|
|
|
Status = pStartNodeInstance->Connect(
|
|
pPinInstance->pFilterInstance->GetDeviceNode(),
|
|
pPinConnect,
|
|
pWaveFormatExRegistry,
|
|
NULL);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If capture pin, try some intelligent variations of requested format
|
|
//
|
|
|
|
if(!NT_SUCCESS(Status) &&
|
|
pWaveFormatExRequested != NULL &&
|
|
pStartNode->pPinNode->pPinInfo->DataFlow == KSPIN_DATAFLOW_OUT) {
|
|
|
|
DPF(90, "CSNI::Create: IntelligentConnect");
|
|
|
|
Status = pStartNodeInstance->IntelligentConnect(
|
|
pPinInstance->pFilterInstance->GetDeviceNode(),
|
|
pPinConnect,
|
|
pWaveFormatExRequested);
|
|
|
|
//
|
|
// If the graph contains only splitter and capturer, only the
|
|
// requested format can succeed.
|
|
// So exit here.
|
|
//
|
|
if (pStartNodeInstance->pStartNode->IsCaptureFormatStrict()) {
|
|
DPF1(50, "CSNI::Create: CaptureFormatStrict Bailing Out: Status %X", Status);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If capture pin and if aec is included, negotiate format between
|
|
// aec and capture device.
|
|
//
|
|
if(!NT_SUCCESS(Status) &&
|
|
pStartNode->IsAecIncluded() &&
|
|
pStartNode->pPinNode->pPinInfo->DataFlow == KSPIN_DATAFLOW_OUT) {
|
|
|
|
PKSPIN_CONNECT pPinConnectDirect = NULL;
|
|
|
|
DPF(90, "CSNI::Create: AecConnection");
|
|
|
|
Status = pStartNodeInstance->AecConnectionFormat(
|
|
pPinInstance->pFilterInstance->GetDeviceNode(),
|
|
&pPinConnectDirect);
|
|
|
|
//
|
|
// Try mono/stereo formats first.
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
for (WORD i = 1; i <= 2; i++) {
|
|
ModifyPinConnect(pPinConnectDirect, i);
|
|
|
|
Status = pStartNodeInstance->Connect(
|
|
pPinInstance->pFilterInstance->GetDeviceNode(),
|
|
pPinConnect,
|
|
NULL,
|
|
pPinConnectDirect);
|
|
if (NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pPinConnectDirect) {
|
|
delete pPinConnectDirect;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Try pin data intersection
|
|
//
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF(90, "CSNI::Create: Data Intersection");
|
|
|
|
Status = pStartNodeInstance->Connect(
|
|
pPinInstance->pFilterInstance->GetDeviceNode(),
|
|
pPinConnect,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
int i;
|
|
|
|
//
|
|
// Try each waveformatex limit until success
|
|
//
|
|
|
|
for(i = 0; i < SIZEOF_ARRAY(aWaveFormatEx); i++) {
|
|
|
|
DPF3(90, "CSNI::Create: Array SR %d CH %d BPS %d",
|
|
aWaveFormatEx[i].nSamplesPerSec,
|
|
aWaveFormatEx[i].nChannels,
|
|
aWaveFormatEx[i].wBitsPerSample);
|
|
|
|
Status = pStartNodeInstance->Connect(
|
|
pPinInstance->pFilterInstance->GetDeviceNode(),
|
|
pPinConnect,
|
|
&aWaveFormatEx[i],
|
|
NULL);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
Status = pStartNodeInstance->CreateTopologyTable(
|
|
pPinInstance->pFilterInstance->pGraphNodeInstance);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
ASSERT(pStartNodeInstance->CurrentState == KSSTATE_STOP);
|
|
#ifdef DEBUG
|
|
pStartNodeInstance->pPinConnect = (PKSPIN_CONNECT)
|
|
new BYTE[sizeof(KSPIN_CONNECT) +
|
|
((PKSDATARANGE)(pPinConnect + 1))->FormatSize];
|
|
|
|
if(pStartNodeInstance->pPinConnect != NULL) {
|
|
memcpy(
|
|
pStartNodeInstance->pPinConnect,
|
|
pPinConnect,
|
|
sizeof(KSPIN_CONNECT) +
|
|
((PKSDATARANGE)(pPinConnect + 1))->FormatSize);
|
|
}
|
|
#endif
|
|
DPF1(90, "CSNI::Create: SUCCESS %08x", pStartNodeInstance);
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF1(90, "CSNI::Create: FAIL %08x", Status);
|
|
delete pStartNodeInstance;
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
CStartNodeInstance::CStartNodeInstance(
|
|
PPIN_INSTANCE pPinInstance,
|
|
PSTART_NODE pStartNode
|
|
)
|
|
{
|
|
this->pStartNode = pStartNode;
|
|
pStartNode->AddPinInstance();
|
|
this->pPinInstance = pPinInstance;
|
|
pPinInstance->pStartNodeInstance = this;
|
|
AddList(
|
|
&pPinInstance->pFilterInstance->pGraphNodeInstance->lstStartNodeInstance);
|
|
}
|
|
|
|
CStartNodeInstance::~CStartNodeInstance(
|
|
)
|
|
{
|
|
PINSTANCE pInstance;
|
|
|
|
ASSERT(this != NULL);
|
|
Assert(this);
|
|
Assert(pPinInstance);
|
|
DPF1(95, "~CSNI: %08x", this);
|
|
RemoveList();
|
|
|
|
SetState(KSSTATE_STOP, SETSTATE_FLAG_IGNORE_ERROR);
|
|
pStartNode->RemovePinInstance();
|
|
|
|
pPinNodeInstance->Destroy(); // also see CSNI::CleanUp
|
|
pFilterNodeInstance->Destroy(); //
|
|
|
|
delete papFileObjectTopologyTable;
|
|
delete pVirtualNodeData;
|
|
#ifdef DEBUG
|
|
delete pPinConnect;
|
|
#endif
|
|
pPinInstance->pStartNodeInstance = NULL;
|
|
pPinInstance->ParentInstance.Invalidate();
|
|
}
|
|
|
|
VOID
|
|
CStartNodeInstance::CleanUp(
|
|
)
|
|
{
|
|
Assert(this);
|
|
ASSERT(papFileObjectTopologyTable == NULL);
|
|
ASSERT(pVirtualNodeData == NULL);
|
|
ASSERT(CurrentState == KSSTATE_STOP);
|
|
|
|
pPinNodeInstance->Destroy();
|
|
pPinNodeInstance = NULL;
|
|
pFilterNodeInstance->Destroy();
|
|
pFilterNodeInstance = NULL;
|
|
lstConnectNodeInstance.DestroyList();
|
|
}
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::IntelligentConnect(
|
|
PDEVICE_NODE pDeviceNode,
|
|
PKSPIN_CONNECT pPinConnect,
|
|
PWAVEFORMATEX pWaveFormatEx
|
|
)
|
|
{
|
|
PWAVEFORMATEXTENSIBLE pWaveFormatExtensible;
|
|
NTSTATUS Status;
|
|
BOOL Continue;
|
|
WORD NumChannels, BitWidth;
|
|
PBYTE pWaveFormat = NULL;
|
|
ULONG RegionAllocSize, RegionCopySize;
|
|
BOOL IsFloat = FALSE;
|
|
WORD MaxBitWidth, MinBitWidth, MaxChannels, MinChannels;
|
|
|
|
//
|
|
// First copy the user requested format into a local structure
|
|
// (because we will tamper it later for different params)
|
|
//
|
|
if (pWaveFormatEx->wFormatTag == WAVE_FORMAT_PCM) {
|
|
RegionAllocSize = sizeof(WAVEFORMATEX);
|
|
RegionCopySize = sizeof(PCMWAVEFORMAT);
|
|
}
|
|
else {
|
|
RegionAllocSize = sizeof(WAVEFORMATEX) + pWaveFormatEx->cbSize;
|
|
RegionCopySize = RegionAllocSize;
|
|
}
|
|
|
|
pWaveFormat = new(BYTE[RegionAllocSize]);
|
|
if (!pWaveFormat) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
RtlCopyMemory(pWaveFormat, pWaveFormatEx, RegionCopySize);
|
|
|
|
//
|
|
// cast for convenient access
|
|
//
|
|
pWaveFormatExtensible = (PWAVEFORMATEXTENSIBLE) pWaveFormat;
|
|
if (pWaveFormatExtensible->Format.wFormatTag == WAVE_FORMAT_PCM) {
|
|
pWaveFormatExtensible->Format.cbSize = 0;
|
|
}
|
|
|
|
DPF3(90, "CSNI::Create: Client SR %d CH %d BPS %d",
|
|
pWaveFormatExtensible->Format.nSamplesPerSec,
|
|
pWaveFormatExtensible->Format.nChannels,
|
|
pWaveFormatExtensible->Format.wBitsPerSample);
|
|
|
|
//
|
|
// and try the requested format first
|
|
//
|
|
Status = this->Connect(
|
|
pDeviceNode,
|
|
pPinConnect,
|
|
(PWAVEFORMATEX)pWaveFormatEx,
|
|
NULL);
|
|
|
|
//
|
|
// If the graph contains only splitter and capturer, only the
|
|
// requested format can succeed.
|
|
// So exit here.
|
|
//
|
|
if (pStartNode->IsCaptureFormatStrict()) {
|
|
goto exit;
|
|
}
|
|
|
|
|
|
if (pWaveFormatExtensible->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
|
IsFloat = TRUE;
|
|
}
|
|
|
|
if (pWaveFormatExtensible->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
if (IsEqualGUID(&pWaveFormatExtensible->SubFormat,&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
|
|
IsFloat = TRUE;
|
|
}
|
|
}
|
|
|
|
if (IsFloat == FALSE) {
|
|
if (pWaveFormatExtensible->Format.wFormatTag != WAVE_FORMAT_PCM) {
|
|
if (pWaveFormatExtensible->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
|
|
goto exit;
|
|
}
|
|
else {
|
|
if (!IsEqualGUID(&pWaveFormatExtensible->SubFormat,&KSDATAFORMAT_SUBTYPE_PCM)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
MaxBitWidth = (pWaveFormatExtensible->Format.wBitsPerSample>16)?pWaveFormatExtensible->Format.wBitsPerSample:16;
|
|
MinBitWidth = 8;
|
|
}
|
|
else {
|
|
MaxBitWidth = MinBitWidth = pWaveFormatEx->wBitsPerSample;
|
|
}
|
|
|
|
//
|
|
// MaxChannels = (pWaveFormatExtensible->nChannels > 2) ? pWaveFormatExtensible->nChannels:2;
|
|
// We can do this, what would be the channle mask for WaveFormatExtensible?
|
|
//
|
|
MaxChannels = 2;
|
|
MinChannels = 1;
|
|
|
|
//
|
|
// If that failed with the same sample rate try different
|
|
// combinations of numchannels & bitwidth
|
|
//
|
|
// Tries 4 combinations of STEREO/MONO & 8/16 bits
|
|
// More intelligence can be built based upon device capability
|
|
// (also does not check whether we tried a combination earlier)
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
Continue = TRUE;
|
|
for (NumChannels = MaxChannels; (NumChannels >= MinChannels) && Continue; NumChannels--) {
|
|
for (BitWidth = MaxBitWidth;
|
|
(BitWidth >= MinBitWidth) && Continue;
|
|
BitWidth=(BitWidth%8)?((BitWidth/8)*8):(BitWidth-8)) {
|
|
|
|
pWaveFormatExtensible->Format.nChannels = NumChannels;
|
|
pWaveFormatExtensible->Format.wBitsPerSample = BitWidth;
|
|
pWaveFormatExtensible->Format.nBlockAlign = (NumChannels * BitWidth)/8;
|
|
|
|
pWaveFormatExtensible->Format.nAvgBytesPerSec = pWaveFormatExtensible->Format.nSamplesPerSec *
|
|
pWaveFormatExtensible->Format.nBlockAlign;
|
|
if (pWaveFormatExtensible->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
pWaveFormatExtensible->Samples.wValidBitsPerSample = BitWidth;
|
|
if (NumChannels == 1) {
|
|
pWaveFormatExtensible->dwChannelMask = SPEAKER_FRONT_CENTER;
|
|
}
|
|
else {
|
|
pWaveFormatExtensible->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
|
}
|
|
}
|
|
|
|
DPF3(90, "CSNI::Create: Client SR %d CH %d BPS %d",
|
|
pWaveFormatExtensible->Format.nSamplesPerSec,
|
|
pWaveFormatExtensible->Format.nChannels,
|
|
pWaveFormatExtensible->Format.wBitsPerSample);
|
|
|
|
Status = this->Connect(pDeviceNode,
|
|
pPinConnect,
|
|
(PWAVEFORMATEX)pWaveFormatExtensible,
|
|
NULL);
|
|
if (NT_SUCCESS(Status)) {
|
|
Continue = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exit:
|
|
delete [] pWaveFormat;
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::AecConnectionFormat(
|
|
PDEVICE_NODE pDeviceNode,
|
|
PKSPIN_CONNECT *ppPinConnect)
|
|
{
|
|
PCLIST_ITEM pListItem;
|
|
PCONNECT_NODE_INSTANCE pConnectNodeInstance;
|
|
PCONNECT_NODE_INSTANCE pBottomConnection;
|
|
PCONNECT_NODE_INSTANCE pAecConnection = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
*ppPinConnect = NULL;
|
|
|
|
Status = CConnectNodeInstance::Create(this, pDeviceNode);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get Aec Source and Capture Sink pins.
|
|
//
|
|
|
|
pListItem = lstConnectNodeInstance.GetListLast();
|
|
pBottomConnection = lstConnectNodeInstance.GetListData(pListItem);
|
|
|
|
FOR_EACH_LIST_ITEM_BACKWARD(&lstConnectNodeInstance, pConnectNodeInstance) {
|
|
if (pConnectNodeInstance->pConnectNode->pPinNodeSource->
|
|
pPinInfo->pFilterNode->GetType() & FILTER_TYPE_AEC) {
|
|
|
|
pAecConnection = pConnectNodeInstance;
|
|
break;
|
|
}
|
|
} END_EACH_LIST_ITEM
|
|
|
|
if (NULL == pAecConnection || NULL == pBottomConnection) {
|
|
DPF(5, "CSNI::AecConnectionFormat: Cannot find Aec or Capture");
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto exit;
|
|
}
|
|
|
|
DPF3(20, "Aec : %X %d %s",
|
|
pAecConnection,
|
|
pAecConnection->pConnectNode->pPinNodeSource->pPinInfo->PinId,
|
|
pAecConnection->pConnectNode->pPinNodeSource->pPinInfo->pFilterNode->DumpName());
|
|
|
|
DPF3(20, "Capture : %X %d %s",
|
|
pBottomConnection,
|
|
pBottomConnection->pConnectNode->pPinNodeSink->pPinInfo->PinId,
|
|
pBottomConnection->pConnectNode->pPinNodeSink->pPinInfo->pFilterNode->DumpName());
|
|
|
|
//
|
|
// Find the intersection between kmixer source and capture sink.
|
|
//
|
|
Status = CreatePinIntersection(
|
|
ppPinConnect,
|
|
pBottomConnection->pConnectNode->pPinNodeSink,
|
|
pAecConnection->pConnectNode->pPinNodeSource,
|
|
pBottomConnection->pFilterNodeInstanceSink,
|
|
pAecConnection->pFilterNodeInstanceSource);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF(5, "CSNI::AecConnectionFormat: No intersection found");
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
DumpDataFormat(20, (PKSDATAFORMAT) (*ppPinConnect + 1));
|
|
#endif
|
|
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF2(90, "CSNI::AecConnectionFormat: %08x FAIL %08x", this, Status);
|
|
|
|
if (*ppPinConnect) {
|
|
ExFreePool(*ppPinConnect);
|
|
*ppPinConnect = NULL;
|
|
}
|
|
}
|
|
|
|
CleanUp();
|
|
return(Status);
|
|
} // AecConnectionFormat
|
|
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::Connect(
|
|
PDEVICE_NODE pDeviceNode,
|
|
PKSPIN_CONNECT pPinConnect,
|
|
PWAVEFORMATEX pWaveFormatEx,
|
|
PKSPIN_CONNECT pPinConnectDirect
|
|
)
|
|
{
|
|
PCONNECT_NODE_INSTANCE pConnectNodeInstance;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Status = CConnectNodeInstance::Create(this, pDeviceNode);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Do all the bottom up connecting
|
|
//
|
|
|
|
FOR_EACH_LIST_ITEM_BACKWARD(&lstConnectNodeInstance, pConnectNodeInstance) {
|
|
|
|
if(!pConnectNodeInstance->IsTopDown()) {
|
|
|
|
//
|
|
// For Aec sink pin do intersection, no matter what the format is.
|
|
//
|
|
if (pConnectNodeInstance->pFilterNodeInstanceSink->
|
|
pFilterNode->GetType() & FILTER_TYPE_AEC) {
|
|
|
|
Status = pConnectNodeInstance->Connect(NULL, NULL);
|
|
}
|
|
else {
|
|
Status = pConnectNodeInstance->Connect(
|
|
pWaveFormatEx,
|
|
pPinConnectDirect);
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
} END_EACH_LIST_ITEM
|
|
|
|
pPinConnect->PinToHandle = NULL;
|
|
|
|
Status = CPinNodeInstance::Create(
|
|
&pPinNodeInstance,
|
|
pFilterNodeInstance,
|
|
pStartNode->pPinNode,
|
|
pPinConnect,
|
|
(pStartNode->fRender)
|
|
#ifdef FIX_SOUND_LEAK
|
|
,lstConnectNodeInstance.IsLstEmpty()
|
|
#endif
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Do all the top down connecting
|
|
//
|
|
|
|
FOR_EACH_LIST_ITEM(&lstConnectNodeInstance, pConnectNodeInstance) {
|
|
|
|
if(pConnectNodeInstance->IsTopDown()) {
|
|
//
|
|
// Rely on DataIntersection for all Topdown connections
|
|
//
|
|
Status = pConnectNodeInstance->Connect(NULL, NULL);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
DPF1(90, "CSNI::Connect: %08x SUCCESS", this);
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF2(90, "CSNI::Connect: %08x FAIL %08x", this, Status);
|
|
CleanUp();
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::CreateTopologyTable(
|
|
PGRAPH_NODE_INSTANCE pGraphNodeInstance
|
|
)
|
|
{
|
|
PCONNECT_NODE_INSTANCE pConnectNodeInstance;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PFILTER_NODE pFilterNode = NULL;
|
|
ULONG n;
|
|
|
|
Assert(this);
|
|
Assert(pGraphNodeInstance);
|
|
|
|
if(pGraphNodeInstance->Topology.TopologyNodesCount != 0) {
|
|
|
|
ASSERT(papFileObjectTopologyTable == NULL);
|
|
|
|
papFileObjectTopologyTable =
|
|
new PFILE_OBJECT[pGraphNodeInstance->Topology.TopologyNodesCount];
|
|
|
|
if(papFileObjectTopologyTable == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
}
|
|
for(n = 0; n < pGraphNodeInstance->Topology.TopologyNodesCount; n++) {
|
|
|
|
// if filter node is the same as last time, no need to search
|
|
if(pFilterNode == pGraphNodeInstance->papTopologyNode[n]->pFilterNode) {
|
|
ASSERT(n != 0);
|
|
ASSERT(pFilterNode != NULL);
|
|
papFileObjectTopologyTable[n] = papFileObjectTopologyTable[n - 1];
|
|
continue;
|
|
}
|
|
pFilterNode = pGraphNodeInstance->papTopologyNode[n]->pFilterNode;
|
|
Assert(pFilterNode);
|
|
//
|
|
// Now find a filter instance and a pin instance in this graph
|
|
// instance for this filter node.
|
|
//
|
|
Assert(pPinNodeInstance);
|
|
|
|
if(pPinNodeInstance->pPinNode->pPinInfo->pFilterNode == pFilterNode) {
|
|
papFileObjectTopologyTable[n] = pPinNodeInstance->pFileObject;
|
|
continue;
|
|
}
|
|
|
|
FOR_EACH_LIST_ITEM_BACKWARD( // Top Down
|
|
&lstConnectNodeInstance,
|
|
pConnectNodeInstance) {
|
|
|
|
Assert(pConnectNodeInstance);
|
|
Assert(pConnectNodeInstance->pPinNodeInstanceSink);
|
|
Assert(pConnectNodeInstance->pPinNodeInstanceSink->pPinNode);
|
|
Assert(
|
|
pConnectNodeInstance->pPinNodeInstanceSink->pPinNode->pPinInfo);
|
|
|
|
//
|
|
// Use the sink pin handle for now. This should be fine until
|
|
// Sysaudio supports a spliter.
|
|
//
|
|
|
|
if(pConnectNodeInstance->pPinNodeInstanceSink->
|
|
pPinNode->pPinInfo->pFilterNode == pFilterNode) {
|
|
papFileObjectTopologyTable[n] =
|
|
pConnectNodeInstance->pPinNodeInstanceSink->pFileObject;
|
|
break;
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
}
|
|
DPF1(90, "CreatePinInstanceTopologyTable PI: %08x",
|
|
papFileObjectTopologyTable);
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::GetTopologyNodeFileObject(
|
|
OUT PFILE_OBJECT *ppFileObject,
|
|
IN ULONG NodeId
|
|
)
|
|
{
|
|
PGRAPH_NODE_INSTANCE pGraphNodeInstance;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if(this == NULL) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
goto exit;
|
|
}
|
|
Assert(this);
|
|
ASSERT(pPinInstance != NULL);
|
|
|
|
Status = pPinInstance->pFilterInstance->GetGraphNodeInstance(
|
|
&pGraphNodeInstance);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
Assert(pGraphNodeInstance);
|
|
|
|
if(NodeId >= pGraphNodeInstance->cTopologyNodes) {
|
|
Trap();
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto exit;
|
|
}
|
|
|
|
if(papFileObjectTopologyTable == NULL ||
|
|
papFileObjectTopologyTable[NodeId] == NULL) {
|
|
|
|
Status = pGraphNodeInstance->GetTopologyNodeFileObject(
|
|
ppFileObject,
|
|
NodeId);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
else {
|
|
*ppFileObject = papFileObjectTopologyTable[NodeId];
|
|
}
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::SetState(
|
|
KSSTATE NewState,
|
|
ULONG ulFlags
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LONG State;
|
|
|
|
Assert(this);
|
|
|
|
if(NewState < KSSTATE_STOP || NewState >= MAX_STATES) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if(CurrentState == NewState) {
|
|
ASSERT(NT_SUCCESS(Status));
|
|
goto exit;
|
|
}
|
|
if(CurrentState < NewState) {
|
|
for(State = CurrentState + 1; State <= NewState; State++) {
|
|
|
|
Status = SetStateTopDown(
|
|
(KSSTATE)State,
|
|
CurrentState,
|
|
ulFlags | SETSTATE_FLAG_SINK | SETSTATE_FLAG_SOURCE);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
CurrentState = (KSSTATE)State;
|
|
}
|
|
}
|
|
else {
|
|
for(State = CurrentState - 1; State >= NewState; State--) {
|
|
|
|
Status = SetStateBottomUp(
|
|
(KSSTATE)State,
|
|
CurrentState,
|
|
ulFlags | SETSTATE_FLAG_SINK | SETSTATE_FLAG_SOURCE);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
CurrentState = (KSSTATE)State;
|
|
}
|
|
}
|
|
ASSERT(CurrentState == NewState);
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::SetStateTopDown(
|
|
KSSTATE NewState,
|
|
KSSTATE PreviousState,
|
|
ULONG ulFlags
|
|
)
|
|
{
|
|
PCONNECT_NODE_INSTANCE pConnectNodeInstance;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if(this != NULL) {
|
|
Assert(this);
|
|
|
|
if(ulFlags & SETSTATE_FLAG_SINK) {
|
|
Status = pPinNodeInstance->SetState(
|
|
NewState,
|
|
PreviousState,
|
|
ulFlags);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
FOR_EACH_LIST_ITEM(
|
|
&lstConnectNodeInstance,
|
|
pConnectNodeInstance) {
|
|
|
|
Status = pConnectNodeInstance->SetStateTopDown(
|
|
NewState,
|
|
PreviousState,
|
|
ulFlags);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
}
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CStartNodeInstance::SetStateBottomUp(
|
|
KSSTATE NewState,
|
|
KSSTATE PreviousState,
|
|
ULONG ulFlags
|
|
)
|
|
{
|
|
PCONNECT_NODE_INSTANCE pConnectNodeInstance;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if(this != NULL) {
|
|
Assert(this);
|
|
|
|
FOR_EACH_LIST_ITEM_BACKWARD(
|
|
&lstConnectNodeInstance,
|
|
pConnectNodeInstance) {
|
|
|
|
Status = pConnectNodeInstance->SetStateBottomUp(
|
|
NewState,
|
|
PreviousState,
|
|
ulFlags);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
if(ulFlags & SETSTATE_FLAG_SINK) {
|
|
Status = pPinNodeInstance->SetState(
|
|
NewState,
|
|
PreviousState,
|
|
ulFlags);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef DEBUG
|
|
|
|
ENUMFUNC
|
|
CStartNodeInstance::Dump(
|
|
)
|
|
{
|
|
PCONNECT_NODE_INSTANCE pConnectNodeInstance;
|
|
extern PSZ apszStates[];
|
|
|
|
if(this == NULL) {
|
|
return(STATUS_CONTINUE);
|
|
}
|
|
if(ulDebugFlags & (DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_OBJECT)) {
|
|
dprintf("SNI: %08x SN %08x PI %08x FNI %08x VND %08x papFO %08x\n",
|
|
this,
|
|
pStartNode,
|
|
pPinInstance,
|
|
pFilterNodeInstance,
|
|
pVirtualNodeData,
|
|
papFileObjectTopologyTable);
|
|
dprintf(" State: %08x %s\n",
|
|
CurrentState,
|
|
apszStates[CurrentState]);
|
|
if(ulDebugFlags & DEBUG_FLAGS_INSTANCE) {
|
|
if(pPinNodeInstance != NULL) {
|
|
pPinNodeInstance->Dump();
|
|
}
|
|
}
|
|
if(pPinConnect != NULL) {
|
|
DumpPinConnect(MAXULONG, pPinConnect);
|
|
}
|
|
}
|
|
else {
|
|
dprintf(" To: ");
|
|
if(pPinNodeInstance != NULL) {
|
|
pPinNodeInstance->Dump();
|
|
}
|
|
else {
|
|
dprintf("NULL\n");
|
|
}
|
|
}
|
|
if(ulDebugFlags & DEBUG_FLAGS_TOPOLOGY) {
|
|
PGRAPH_NODE_INSTANCE pGraphNodeInstance;
|
|
pGraphNodeInstance = pPinInstance->pFilterInstance->pGraphNodeInstance;
|
|
if(pGraphNodeInstance != NULL) {
|
|
Assert(pGraphNodeInstance);
|
|
for(ULONG i = 0;
|
|
i < pGraphNodeInstance->Topology.TopologyNodesCount;
|
|
i++) {
|
|
if(papFileObjectTopologyTable[i] != NULL) {
|
|
dprintf(" %02x FO %08x %s\n",
|
|
i,
|
|
papFileObjectTopologyTable[i],
|
|
pGraphNodeInstance->papTopologyNode[i]->pFilterNode->
|
|
DumpName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(ulDebugFlags & DEBUG_FLAGS_INSTANCE) {
|
|
FOR_EACH_LIST_ITEM(&lstConnectNodeInstance, pConnectNodeInstance) {
|
|
pConnectNodeInstance->Dump();
|
|
} END_EACH_LIST_ITEM
|
|
}
|
|
dprintf("\n");
|
|
return(STATUS_CONTINUE);
|
|
}
|
|
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|