SDL icon indicating copy to clipboard operation
SDL copied to clipboard

Failed to open audio recording device [name]. SDL Error: CoreAudio error (AudioQueueStart): -66681 [MacOS]

Open mail2mhossain opened this issue 1 year ago • 11 comments

When using our app, if an audio input or output device is unplugged, I can switch to another available device on all platforms (Windows, MacOS, Linux). However, after plugging the device back in, the app can only switch back to it on Windows.

On MacOS, I encounter the following error: "Failed to open audio recording device [name]. SDL Error: CoreAudio error (AudioQueueStart): -66681".

This indicates that the unplugged device is still in use or blocked by SDL2 or MacOS. I have tried the following code in the "InitRecordingDevice" method after calling "CloseAudio()", but it still does not work:

SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_InitSubSystem(SDL_INIT_AUDIO);

I have also added "SDL_ClearQueuedAudio(dev)" in the "CloseAudio" method before calling "CloseAudioRecordingDevice", but this has not resolved the issue either.

Are there any solutions for this problem?

mail2mhossain avatar Jul 31 '24 02:07 mail2mhossain

Are there any solutions for this problem?

This sounds like a bug in SDL; I'll try to reproduce it here tomorrow morning.

icculus avatar Jul 31 '24 06:07 icculus

This reproduces for me with testaudiohotplug on both SDL2 and SDL3 (just unplug USB headphones and plug them back in while the test program is running). Digging deeper.

icculus avatar Jul 31 '24 14:07 icculus

Looks like (in SDL3, at least) device->currently_opened is zero when DeviceAliveNotification is fired...so when we tell SDL that the device is disconnected, it doesn't try to close it, so all sorts of stuff is still dangling.

Not sure why yet, looking into it still.

icculus avatar Jul 31 '24 15:07 icculus

Wait, no, that was the recording half of the USB headset. But I'm also noticing the testaudiohotplug doesn't close the device at all, so I bet that's what's happening.

icculus avatar Jul 31 '24 18:07 icculus

Nope, that didn't fix it.

I'm starting to believe this is a macOS bug; the internet (since Mac OS X 10.5, apparently) has been asking "why does this error pop up some times?" and no one seems to know.

Notably, it seems to hang all the other playing devices too, for several seconds, when replugging the USB headset. If I start the device as unplugged and plug it in after everything else, it picks it up fine and works (but then, an unplug/replug after will still cause failure).

I added a 5 second delay to see if it just needed a little time to get itself together on hotplug; didn't fix it.

Feels like the CoreAudio framework is holding some state from the previous device connection that confuses it on replugging. I could be wrong. I'll ask our Apple contact.

icculus avatar Jul 31 '24 19:07 icculus

Okay, I've moved the SDL coreaudio code out to a standalone program that still reproduces the issue, which is to say this could still be our bug, since it's still our code, but now I can hand this to Apple as a ~500 line Objective-C program without any external dependencies, including SDL itself:

https://gist.github.com/icculus/b021a110eb1bcdfa72f169a620e2d46e

Stay tuned!

icculus avatar Aug 05 '24 02:08 icculus

Any Update

mail2mhossain avatar Sep 17 '24 03:09 mail2mhossain

I never got a reply from Apple. :(

icculus avatar Sep 29 '24 07:09 icculus

I filed this with Apple, outside of the usual contact.

rdar://15576779

icculus avatar Oct 24 '24 04:10 icculus

Moving to 3.x, but if Apple moves on the bug report, we can revisit.

icculus avatar Oct 24 '24 15:10 icculus

Hi, I am also seeing a crash that I believe is related to this. If I try to re-open the default device after seeing the first -66681 error message, I get this crash with the following trace:

frame #0: 0x0000000101385430 tlplay`SDL_AtomicSet_REAL(a=0x000000000000006c, v=1) at SDL_atomic.c:194:12
    frame #1: 0x000000010148d330 tlplay`default_device_changed(inObjectID=1, inNumberAddresses=1, inAddresses=0x000060000000ded0, inUserData=0x000060000332d9a0) at SDL_coreaudio.m:675:5
    frame #2: 0x000000018fd17a04 CoreAudio`HALObject::PropertiesChanged(unsigned int, AudioObjectPropertyAddress const*) + 1564
    frame #3: 0x000000018fb9c228 CoreAudio`HALSystem::PropertiesChanged(unsigned int, AudioObjectPropertyAddress const*) + 412
    frame #4: 0x000000018fc6bce8 CoreAudio`HALC_ShellPlugIn::ProxyObject_PropertiesChanged(unsigned int, unsigned int, AudioObjectPropertyAddress const*) + 1060
    frame #5: 0x000000018fcc31cc CoreAudio`HALC_ProxyNotifications::CallListener_f(void*) + 92
    frame #6: 0x000000018d81c400 libdispatch.dylib`_dispatch_client_callout + 20
    frame #7: 0x000000018d823a88 libdispatch.dylib`_dispatch_lane_serial_drain + 668
    frame #8: 0x000000018d82462c libdispatch.dylib`_dispatch_lane_invoke + 436
    frame #9: 0x000000018d8258e8 libdispatch.dylib`_dispatch_workloop_invoke + 1764
    frame #10: 0x000000018d82f244 libdispatch.dylib`_dispatch_workloop_worker_thread + 648
    frame #11: 0x000000018d9c8074 libsystem_pthread.dylib`_pthread_wqthread + 288

In default_device_changed() it looks like this->hidden is null. If I change the code to add a check for null the crash seems to be fixed, does this seem like a reasonable change?

    if (this->hidden)
    {
        SDL_AtomicSet(&this->hidden->device_change_flag, 1); /* let the audioqueue thread pick up on this when safe to do so. */
    }

This is on macOS 13.7 with SDL2 2.30.9. I am triggering the error by turning on/off a bluetooth headset.

I am also seeing these errors in lldb when turning on/off the bluetooth headset:

2024-11-04 14:15:58.569647-0800 tlplay[29868:35257908]   HALC_ProxyObjectMap.cpp:153    HALC_ProxyObjectMap::_CopyObjectByObjectID: failed to create the local object
2024-11-04 14:15:58.569670-0800 tlplay[29868:35257908]      HALC_ShellDevice.cpp:2606   HALC_ShellDevice::RebuildControlList: couldn't find the control object
2024-11-04 14:15:58.632811-0800 tlplay[29868:35258610] AudioHardware-mac-imp.cpp:660    AudioObjectGetPropertyData: no object with given ID 0
2024-11-04 14:15:58.772863-0800 tlplay[29868:35258812] [aqsrv]              AQServer.cpp:80    Exception caught in AudioQueueInternalNotifyRunning - error -66671
2024-11-04 14:15:58.796104-0800 tlplay[29868:35258812] [aqsrv]              AQServer.cpp:80    Exception caught in AudioQueueInternalNotifyRunning - error -66671
2024-11-04 14:15:59.016756-0800 tlplay[29868:35258812] [aqsrv]              AQServer.cpp:80    Exception caught in AudioQueueInternalStop_Sync - error -66671
2024-11-04 14:15:59.039934-0800 tlplay[29868:35257955] [aqsrv]              AQServer.cpp:80    Exception caught in AudioQueueInternalStop_Sync - error -66671

darbyjohnston avatar Nov 04 '24 22:11 darbyjohnston

In default_device_changed() it looks like this->hidden is null. If I change the code to add a check for null the crash seems to be fixed, does this seem like a reasonable change?

Sorry for the delay in response, I just saw this comment.

Looking at the latest SDL2 code, it seems like it shouldn't be able to hit this with this->hidden == NULL, unless we've already closed the device (and either macOS's free() implementation zero'd out this's memory block, or the memory block was reallocated to something else already that happens to be NULL), since this->hidden is allocated at the start of COREAUDIO_OpenDevice and doesn't change or get free'd until COREAUDIO_CloseDevice.

I think it's more likely we have a weird condition that the default_device_changed callback fires unexpectedly after we've closed the SDL audio device.

icculus avatar Jan 03 '25 21:01 icculus

If I try to re-open the default device after seeing the first -66681 error message,

Actually, my guess is that once you see this, everything has gone haywire at the CoreAudio layer, because something hangs up in there for a long long time, so we're probably getting this callback long after we thought we cleaned everything up.

I could be wrong, but we really need a fix from Apple for the original problem, because it's probably also causing this crash secondhand.

icculus avatar Jan 03 '25 21:01 icculus

Thanks for the response; I created a new issue with a small test program to reproduce the crash: https://github.com/libsdl-org/SDL/issues/11836

For now I've reverted to version 2.0.12 which seem to be working OK. Is there any more information/testing I can provide that would help with the Apple ticket?

darbyjohnston avatar Jan 03 '25 23:01 darbyjohnston

I added some code to SDL2 to make sure that the device pointer is still valid before accessing it, which hopefully will resolve the crash you're seeing. It won't help with CoreAudio getting confused by the missing bluetooth device, though.

As for the apple bug report, it's still sitting there open, so I don't know what to say about it; I wrote them a small standalone reproduction case, so I can't think of anything more to be added to the report. Hopefully they'll do something with it eventually.

icculus avatar Jan 04 '25 05:01 icculus

Do we know why 2.0.12 is working okay?

slouken avatar Jan 04 '25 05:01 slouken

We didn't track default device changes at all in 2.0.12, so the crashing code didn't exist yet. I assume the bluetooth disconnect hang still happens, though.

icculus avatar Jan 04 '25 06:01 icculus

The bluetooth connect/disconnect seems to be working fine in 2.0.12, I have been testing it quite a bit. I'm tracking the default device by polling for device changes and closing and re-opening the device.

darbyjohnston avatar Jan 04 '25 17:01 darbyjohnston