diff --git a/audiopassthru/include/sndDevices.h b/audiopassthru/include/sndDevices.h index f87c48ed..b70c2f89 100644 --- a/audiopassthru/include/sndDevices.h +++ b/audiopassthru/include/sndDevices.h @@ -427,6 +427,9 @@ struct sndDevicesHdlType { // Module common status flag, set by functions that can't complete their requestion operation int function_status; + wchar_t reconnectedDeviceGuid[PT_MAX_GENERIC_STRLEN]; + BOOL hasReconnectedDevice; + void (*deviceChangeCallback)(); }; diff --git a/audiopassthru/src/sndDevices/sndDevicesDeviceCallbacks.cpp b/audiopassthru/src/sndDevices/sndDevicesDeviceCallbacks.cpp index 645b687a..d339533b 100644 --- a/audiopassthru/src/sndDevices/sndDevicesDeviceCallbacks.cpp +++ b/audiopassthru/src/sndDevices/sndDevicesDeviceCallbacks.cpp @@ -200,13 +200,43 @@ HRESULT STDMETHODCALLTYPE CsndDevicesMMNotificationClient::OnDeviceStateChanged( { struct sndDevicesHdlType *cast_handle; int type; - + IMMDevice *pDevice = NULL; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMEndpoint *pEndpoint = NULL; + EDataFlow flow; + HRESULT hr; + cast_handle = (struct sndDevicesHdlType *)g_sndDevicesCallbacks_hdl; if (cast_handle == NULL) return(S_OK); - //SLOUT_FIRST_LINE(L"CsndDevicesMMNotificationClient::OnDeviceStateChanged() enters"); + // Only react to render (playback) device changes, not capture (input) devices + if (pwstrDeviceId != NULL) + { + hr = CoCreateInstance(cast_handle->CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, cast_handle->IID_IMMDeviceEnumerator, (void**)&pEnumerator); + if (SUCCEEDED(hr) && pEnumerator != NULL) + { + hr = pEnumerator->GetDevice(pwstrDeviceId, &pDevice); + if (SUCCEEDED(hr) && pDevice != NULL) + { + hr = pDevice->QueryInterface(__uuidof(IMMEndpoint), (void**)&pEndpoint); + if (SUCCEEDED(hr) && pEndpoint != NULL) + { + hr = pEndpoint->GetDataFlow(&flow); + if (pEndpoint) pEndpoint->Release(); + if (SUCCEEDED(hr) && flow != eRender) + { + if (pDevice) pDevice->Release(); + if (pEnumerator) pEnumerator->Release(); + return S_OK; + } + } + if (pDevice) pDevice->Release(); + } + if (pEnumerator) pEnumerator->Release(); + } + } switch (dwNewState) { @@ -224,14 +254,22 @@ HRESULT STDMETHODCALLTYPE CsndDevicesMMNotificationClient::OnDeviceStateChanged( break; } - // Ignore identical callbacks from the same guid. Since device must be disconnected after being connected, - // we should never miss a connect or disconnect event. if( pwstrDeviceId != NULL ) - if( (wcscmp(pwstrDeviceId, cast_handle->lastDeviceAddCallbackGuid) == 0) && ( dwNewState == cast_handle->lastDeviceAddCallbackGuidtype ) ) - { - SLOUT_FIRST_LINE(L"CsndDevicesMMNotificationClient::OnDeviceStateChanged() ignoring identical callback"); - return(S_OK); - } + { + // Track reconnected device: when state changes to ACTIVE from a non-active state, + // this is a device reconnection (e.g., Bluetooth headset connects after being paired/disconnected) + if (dwNewState == DEVICE_STATE_ACTIVE) + { + wcscpy(cast_handle->reconnectedDeviceGuid, pwstrDeviceId); + cast_handle->hasReconnectedDevice = TRUE; + } + + // Ignore identical callbacks from the same guid (prevents callback storms) + if( (wcscmp(pwstrDeviceId, cast_handle->lastDeviceAddCallbackGuid) == 0) && ( dwNewState == cast_handle->lastDeviceAddCallbackGuidtype ) ) + { + return(S_OK); + } + } if( pwstrDeviceId == NULL ) wcscpy(cast_handle->lastDeviceAddCallbackGuid, L""); diff --git a/audiopassthru/src/sndDevices/sndDevicesImplementDeviceRules.cpp b/audiopassthru/src/sndDevices/sndDevicesImplementDeviceRules.cpp index fc7cc168..11004d24 100644 --- a/audiopassthru/src/sndDevices/sndDevicesImplementDeviceRules.cpp +++ b/audiopassthru/src/sndDevices/sndDevicesImplementDeviceRules.cpp @@ -231,6 +231,42 @@ int PT_DECLSPEC sndDevicesImplementDeviceRules(PT_HANDLE *hp_sndDevices, int *ip } } + // Check if a previously disconnected device has reconnected (state transitioned to ACTIVE). + // This handles the case where a paired Bluetooth device was in UNPLUGGED state and now connects: + // the device count stays the same (it was already enumerated), so the count-based check above + // would miss it. The reconnection is instead detected via OnDeviceStateChanged callback. + if (cast_handle->hasReconnectedDevice) + { + cast_handle->hasReconnectedDevice = FALSE; + for (i = 0; i < cast_handle->numRealDevices; i++) + { + if (wcscmp(cast_handle->reconnectedDeviceGuid, cast_handle->pwszIDRealDevices[i]) == 0) + { + if (SND_DEVICES_MONO_BUG_SKIP_MONO_DEVICES) + { + if (sndDevicesGetNumberOfChannelsFromID(hp_sndDevices, cast_handle->pwszIDRealDevices[i], &i_numChannels, &i_resultFlag) != OKAY) + return(NOT_OKAY); + + if (i_numChannels < 2) + { + break; + } + } + + if (sndDevices_UtilsGetIndexFromID(hp_sndDevices, cast_handle->pwszIDRealDevices[i], &deviceIndex) != OKAY) + return(NOT_OKAY); + + if (deviceIndex != SND_DEVICES_DEVICE_NOT_PRESENT) + { + cast_handle->playbackDeviceNum = deviceIndex; + WritePreviousDefault = 1; + goto PlaybackDeviceIsSelected; + } + break; + } + } + } + // If we got here we have more than one real playback device and either the user hasn't manually selected one or // the selected device is not present. Use rules to select the playback device based on the default device setting and history. // First check to see if the current default device is not one of the DFX devices. diff --git a/audiopassthru/src/sndDevices/sndDevicesReInit.cpp b/audiopassthru/src/sndDevices/sndDevicesReInit.cpp index d5ed12c9..ef0be6a4 100644 --- a/audiopassthru/src/sndDevices/sndDevicesReInit.cpp +++ b/audiopassthru/src/sndDevices/sndDevicesReInit.cpp @@ -81,6 +81,9 @@ int PT_DECLSPEC sndDevicesReInit(PT_HANDLE *hp_sndDevices, int i_initType, int * wcscpy(cast_handle->lastDeviceAddCallbackGuid, L""); cast_handle->lastDeviceAddCallbackGuidtype = 0; + + wcscpy(cast_handle->reconnectedDeviceGuid, L""); + cast_handle->hasReconnectedDevice = FALSE; cast_handle->noBufferCount = 0; cast_handle->MeasuredVirtualSilentBufferCount = 0; @@ -375,7 +378,7 @@ int PT_DECLSPEC sndCheckDeviceChanges(PT_HANDLE* hp_sndDevices, BOOL* bp_deviceC { deviceFound = TRUE; - // Device ID matched — now check if its state has changed + // Device ID matched � now check if its state has changed DWORD currentState = 0; hr = pDevice->GetState(¤tState); if (SUCCEEDED(hr) && currentState != cast_handle->deviceState[j])