Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions audiopassthru/include/sndDevices.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)();
};

Expand Down
56 changes: 47 additions & 9 deletions audiopassthru/src/sndDevices/sndDevicesDeviceCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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"");
Expand Down
36 changes: 36 additions & 0 deletions audiopassthru/src/sndDevices/sndDevicesImplementDeviceRules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion audiopassthru/src/sndDevices/sndDevicesReInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(&currentState);
if (SUCCEEDED(hr) && currentState != cast_handle->deviceState[j])
Expand Down