// ------------------------------------------------------------------------------ // // Copyright (C) Microsoft. All rights reserved. // // Module Name: // // EndpointPropertyHelpers.cpp // // Abstract: // // Implementation for common helpers of endpoint property // // ------------------------------------------------------------------------------- #include "PreComp.h" #include #include #include "PropertyHelper.h" /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetConnectorId // // Retrieve the connector id from the property store and checks if the connector exists // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetConnectorId ( IMMDevice* pDevice, EndpointConnectorType eConnectorType, bool* hasConnector, UINT* pConnectorId ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetEndpointFriendlyName // // Retrieve the endpoint friendly name from the property store // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetEndpointFriendlyName ( IMMDevice* pDevice, LPWSTR* ppwszEndpointName ) { HRESULT hr = S_OK; wil::com_ptr_nothrow spPropertyStore; wil::unique_prop_variant var; wil::unique_cotaskmem_string pwszName; *ppwszEndpointName = NULL; // Retrieve the endpoint friendly name from the propertey store if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) { return hr; } if (!VERIFY_SUCCEEDED(hr = spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var))) { return hr; } if (!VERIFY_IS_TRUE(var.vt == VT_LPWSTR)) { hr = E_FAIL; return hr; } pwszName = wil::make_cotaskmem_string_nothrow(var.pwszVal); if (!VERIFY_IS_TRUE(pwszName.get() && wcslen(pwszName.get()) != 0)) { hr = E_FAIL; return hr; } *ppwszEndpointName = pwszName.release(); return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetAudioFilterAsDevice // // Get the audio filter from MMDevice. This method is called to activate IKsControl, IKsFormatSupport or IKsGetProposedFormat. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetAudioFilterAsDevice ( IMMDevice* pDevice, IMMDevice** ppAudioFilterAsDevice ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetCachedProcessingModes // // Read the processing mode characteristics from the property store and get all processing modes. This method applies to host and keyword detector pins only. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetCachedProcessingModes ( IMMDevice* pDevice, EndpointConnectorType eConnectorType, ULONG *pCount, AUDIO_SIGNALPROCESSINGMODE **ppModes ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetProcessingModes // // Query for connector's processing modes property // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetProcessingModes ( IMMDevice* pDevice, UINT pinId, ULONG *pCount, AUDIO_SIGNALPROCESSINGMODE **ppModes ) { HRESULT hr = S_OK; wil::com_ptr_nothrow spAudioFilter; wil::com_ptr_nothrow spKsControl; KSP_PIN pinProp; ULONG cbNeeded = 0; ULONG cbProperty; PKSMULTIPLE_ITEM pProperty = NULL; ULONG cbReturned = 0; AUDIO_SIGNALPROCESSINGMODE* pModes = NULL; CComHeapPtr spModes; *pCount = 0; *ppModes = NULL; // Get the audio filter if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) { return hr; } // Activate IKsControl if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**)&spKsControl))) { return hr; } // Read the audio signal processing mode property pinProp.Property.Set = KSPROPSETID_AudioSignalProcessing; pinProp.Property.Id = KSPROPERTY_AUDIOSIGNALPROCESSING_MODES; pinProp.Property.Flags = KSPROPERTY_TYPE_GET; pinProp.PinId = pinId & PARTID_MASK; pinProp.Reserved = 0; // Determined the needed size if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), NULL, 0, &cbNeeded))) { return hr; } if (!VERIFY_IS_TRUE(cbNeeded > 0) || !VERIFY_IS_TRUE((cbNeeded - sizeof(KSMULTIPLE_ITEM)) % sizeof(GUID) == 0)) { hr = E_NOTFOUND; return hr; } cbProperty = cbNeeded; pProperty = (PKSMULTIPLE_ITEM)CoTaskMemAlloc(cbNeeded); if (!VERIFY_IS_NOT_NULL(pProperty)) { hr = E_OUTOFMEMORY; cbProperty = 0; return hr; } // Query the processing mode property if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), pProperty, cbNeeded, &cbReturned))) { goto Exit; } if (!VERIFY_IS_NOT_NULL(pProperty)) { hr = E_NOTFOUND; goto Exit; } if (!VERIFY_IS_TRUE(cbProperty >= (sizeof(KSMULTIPLE_ITEM) + pProperty->Count * sizeof(GUID)))) { hr = E_NOTFOUND; goto Exit; } pModes = reinterpret_cast(pProperty + 1); if (!VERIFY_IS_TRUE(spModes.Allocate(pProperty->Count))) { hr = E_OUTOFMEMORY; goto Exit; } memcpy(spModes, pModes, sizeof(AUDIO_SIGNALPROCESSINGMODE)*(pProperty->Count)); *pCount = pProperty->Count; *ppModes = spModes.Detach(); Exit: if (pProperty != NULL) { CoTaskMemFree(pProperty); } cbProperty = 0; return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetCachedSupportedFormatRecords // // Read the processing mode characteristics from the property store and get all supported formats for processing mode. This method applies to host and keyword detector pins only. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetCachedSupportedFormatRecords ( IMMDevice* pDevice, EndpointConnectorType eConnectorType, AUDIO_SIGNALPROCESSINGMODE mode, ULONG *pCount, FORMAT_RECORD **ppFormatRecords ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetSupportedFormatRecords // // For pins do not have cached supported formats information, such as offload pin, we provide our predefined format list and check whether each is supported by the pin. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetSupportedFormatRecords ( IMMDevice* pDevice, UINT pinId, EndpointConnectorType eConnectorType, AUDIO_SIGNALPROCESSINGMODE mode, STACKWISE_DATAFLOW dataFlow, ULONG *pCount, FORMAT_RECORD **ppFormatRecords ) { HRESULT hr = S_OK; FORMAT_RECORD *pFormatRecords; UINT cFormatRecords = 0; CComHeapPtr spFormatRecords; BOOL isSupported = false; WAVEFORMATEX *pWfx; UINT32 u32DefaultPeriodicityInFrames = 0; UINT32 u32FundamentalPeriodicityInFrames = 0; UINT32 u32MinPeriodicityInFrames = 0; UINT32 u32MaxPeriodicityInFrames = 0; UINT32 u32MaxPeriodicityInFramesExtended = 0; // Init return *pCount = 0; *ppFormatRecords = NULL; pFormatRecords = new FORMAT_RECORD[COUNT_FORMATS]; if (!VERIFY_IS_NOT_NULL(pFormatRecords)) { hr = E_OUTOFMEMORY; return hr; } memset(pFormatRecords, 0, sizeof(FORMAT_RECORD)*COUNT_FORMATS); for (ULONG i = 0; i < COUNT_FORMATS; i++) { pWfx = (WAVEFORMATEX*)&ListofFormats[i]; // Check if format is supported if (!VERIFY_SUCCEEDED(hr = IsFormatSupported(pDevice, pinId, pWfx, &isSupported))) { return hr; } // If the format is supported, we discover the periodicity characteristics for the format and store them along with the format in our FORMAT_RECORD struct. if (isSupported) { if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pWfx, dataFlow, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) { return hr; } pFormatRecords[cFormatRecords].defaultPeriodInFrames = u32DefaultPeriodicityInFrames; pFormatRecords[cFormatRecords].fundamentalPeriodInFrames = u32FundamentalPeriodicityInFrames; pFormatRecords[cFormatRecords].minPeriodInFrames = u32MinPeriodicityInFrames; pFormatRecords[cFormatRecords].maxPeriodInFrames = u32MaxPeriodicityInFrames; pFormatRecords[cFormatRecords].maxPeriodInFramesExtended = u32MaxPeriodicityInFramesExtended; memcpy(&pFormatRecords[cFormatRecords].wfxEx, &ListofFormats[i], sizeof(WAVEFORMATEX) + ListofFormats[i].Format.cbSize); cFormatRecords++; } } if (!VERIFY_IS_TRUE(spFormatRecords.Allocate(cFormatRecords))) { hr = E_OUTOFMEMORY; return hr; } memcpy(spFormatRecords, pFormatRecords, sizeof(FORMAT_RECORD)*cFormatRecords); delete[] pFormatRecords; *pCount = cFormatRecords; *ppFormatRecords = spFormatRecords.Detach(); return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // IsFormatSupported // // Wrap up the IKsFormatSupport::IsFormatSupported method. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT IsFormatSupported ( IMMDevice* pDevice, UINT pinId, WAVEFORMATEX *pWfx, BOOL* pbSupported ) { HRESULT hr = S_OK; wil::com_ptr_nothrow spAudioFilter; wil::com_ptr_nothrow spAudioFilterTopology; wil::com_ptr_nothrow spPart; wil::com_ptr_nothrow spFormatSupport; PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pKsRequestedFormat = NULL; PKSDATAFORMAT pKsFormat = NULL; KSDATAFORMAT_WAVEFORMATEXTENSIBLE KsWfex = {}; DWORD cbKsFormat = 0; // Init return *pbSupported = FALSE; // Get the audio filter if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) { return hr; } // Activate an IDeviceTopology on the filter if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spAudioFilterTopology))) { return hr; } // Get the connector if (!VERIFY_SUCCEEDED(hr = spAudioFilterTopology->GetPartById(pinId, &spPart))) { return hr; } // Activate IKsFormatSupport interface if (!VERIFY_SUCCEEDED(hr = spPart->Activate(CLSCTX_ALL, __uuidof(IKsFormatSupport), (void**)&spFormatSupport))) { return hr; } // Convert to KSDATAFORMAT_WAVEFORMATEX if (!VERIFY_SUCCEEDED(hr = CreateKSFormatFromWFXFormat(pWfx, (KSDATAFORMAT_WAVEFORMATEX**)&pKsRequestedFormat))) { return hr; } if (pKsRequestedFormat->DataFormat.FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)) { memcpy(&KsWfex, pKsRequestedFormat, pKsRequestedFormat->DataFormat.FormatSize); pKsFormat = reinterpret_cast(&KsWfex); cbKsFormat = sizeof(KsWfex); } else { pKsFormat = reinterpret_cast(pKsRequestedFormat); cbKsFormat = pKsRequestedFormat->DataFormat.FormatSize; } // Expect that pbFormat is really a KSDATAFORMAT if (!VERIFY_IS_TRUE(cbKsFormat >= sizeof(KSDATAFORMAT))) { hr = E_INVALIDARG; return hr; } // validate return pointers (this is an externally callable method) if (!VERIFY_IS_TRUE(pKsFormat)) { hr = E_POINTER; return hr; } if (!VERIFY_SUCCEEDED(hr = spFormatSupport->IsFormatSupported(pKsFormat, cbKsFormat, pbSupported))) { return hr; } return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // DiscoverPeriodicityCharacteristicsForFormat // // Discover the periodicity characteristics for given format. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT DiscoverPeriodicityCharacteristicsForFormat ( IMMDevice* pDevice, EndpointConnectorType eConnectorType, AUDIO_SIGNALPROCESSINGMODE mode, WAVEFORMATEX *pWfx, STACKWISE_DATAFLOW dataFlow, UINT32 *pDefaultPeriodicityInFrames, UINT32 *pFundamentalPeriodicityInFrames, UINT32 *pMinPeriodicityInFrames, UINT32 *pMaxPeriodicityInFrames, UINT32 *pMaxPeriodicityInFramesExtended ) { HRESULT hr = S_OK; HNSTIME hnsDefaultPeriod = kSystemDefaultPeriod; UINT32 ActualPeriodicityInFrames; // Verify format is not null if (!VERIFY_IS_NOT_NULL(pWfx)) { hr = E_INVALIDARG; return hr; } *pDefaultPeriodicityInFrames = *pFundamentalPeriodicityInFrames = *pMinPeriodicityInFrames = *pMaxPeriodicityInFrames = HNSTIME_TO_FRAMES_DOUBLE(hnsDefaultPeriod, pWfx->nSamplesPerSec); // Check to see whether the endpoint supports the default periodicity and return the actual periodicity if (!VERIFY_SUCCEEDED(hr = CheckConnectorSupportForPeriodicity(pDevice, eConnectorType, mode, pWfx, dataFlow, hnsDefaultPeriod, &ActualPeriodicityInFrames))) { return hr; } // Use what we get as the default, max, min and fundamental periodicity *pDefaultPeriodicityInFrames = *pFundamentalPeriodicityInFrames = *pMinPeriodicityInFrames = *pMaxPeriodicityInFrames = ActualPeriodicityInFrames; // Max periodicity is capped to the default periodicity for all clients except extended periodicity clients *pMaxPeriodicityInFramesExtended = *pMaxPeriodicityInFrames; if (*pMaxPeriodicityInFrames > *pDefaultPeriodicityInFrames) { *pMaxPeriodicityInFrames = *pDefaultPeriodicityInFrames; } return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // CheckConnectorSupportForPeriodicity // // Check to see if the connector supports the specified periodicity and return the actual periodicity. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT CheckConnectorSupportForPeriodicity ( IMMDevice* pDevice, EndpointConnectorType eConnectorType, AUDIO_SIGNALPROCESSINGMODE mode, WAVEFORMATEX *pWfx, STACKWISE_DATAFLOW dataFlow, HNSTIME RequestedPeriodicity, UINT32 *pActualPeriodicityInFrames ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetCachedDefaultFormat // // Read the default device format from the property store. Use audio engine device format for all other pin types. For Keyword Detector pin, it has a different default device format. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetCachedDefaultFormat ( IMMDevice* pDevice, EndpointConnectorType eConnectorType, WAVEFORMATEX** ppDefaultFormat ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetProposedFormatForProcessingMode // // Wrap up the IKsGetProposedFormat::GetProposedFormatForMode method. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetProposedFormatForProcessingMode ( IMMDevice* pDevice, UINT pinId, AUDIO_SIGNALPROCESSINGMODE mode, WAVEFORMATEX **ppProposedFormat ) { HRESULT hr = S_OK; wil::com_ptr_nothrow spAudioFilter; wil::com_ptr_nothrow spAudioFilterTopology; wil::com_ptr_nothrow spPart; PKSDATAFORMAT pKsProposedFormat = NULL; WAVEFORMATEXTENSIBLE* pWfxEx = NULL; // Init return *ppProposedFormat = NULL; if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) { return hr; } // Activate an IDeviceTopology on the filter if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spAudioFilterTopology))) { return hr; } // Get the connector if (!VERIFY_SUCCEEDED(hr = spAudioFilterTopology->GetPartById(pinId, &spPart))) { return hr; } // Retrieve the proposed format for a specific processing mode and store it in pKsProposedFormat... // Allocate memory for ppOffloadDefaultFormat and copy pKsDataDeviceFormat to ppOffloadDefaultFormat pWfxEx = (WAVEFORMATEXTENSIBLE *)CoTaskMemAlloc(sizeof(WAVEFORMATEX) + ((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx.cbSize); if (!VERIFY_IS_NOT_NULL(pWfxEx)) { CoTaskMemFree(pKsProposedFormat); hr = E_OUTOFMEMORY; return hr; } memcpy(pWfxEx, &(((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx), sizeof(WAVEFORMATEX) + ((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx.cbSize); *ppProposedFormat = (WAVEFORMATEX*)pWfxEx; return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GetCurrentAvailiablePinInstanceCount // // Query for the connector's pin instance count property. // /////////////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT GetAvailiablePinInstanceCount ( IMMDevice* pDevice, UINT pinId, UINT32* pAvailablePinInstanceCount ) { HRESULT hr = S_OK; wil::com_ptr_nothrow spAudioFilter; wil::com_ptr_nothrow spKsControl; KSP_PIN pinProp; KSPIN_CINSTANCES pinInstances; ULONG cbReturned = 0; *pAvailablePinInstanceCount = 0; if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) { return hr; } if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**)&spKsControl))) { return hr; } // Read the pin instance count pinProp.Property.Set = KSPROPSETID_Pin; pinProp.Property.Id = KSPROPERTY_PIN_CINSTANCES; pinProp.Property.Flags = KSPROPERTY_TYPE_GET; pinProp.PinId = pinId & PARTID_MASK; pinProp.Reserved = 0; if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), &pinInstances, sizeof(KSPIN_CINSTANCES), &cbReturned))) { return hr; } *pAvailablePinInstanceCount = pinInstances.PossibleCount - pinInstances.CurrentCount; return hr; }