Fixed Bluetooth audio output on Apple TV

This commit is contained in:
Sam Lantinga 2020-06-04 12:26:57 -07:00
parent cced5eb937
commit ff53521bc6
1 changed files with 67 additions and 64 deletions

View File

@ -291,34 +291,34 @@ static BOOL session_active = NO;
static void pause_audio_devices() static void pause_audio_devices()
{ {
int i; int i;
if (!open_devices) { if (!open_devices) {
return; return;
} }
for (i = 0; i < num_open_devices; ++i) { for (i = 0; i < num_open_devices; ++i) {
SDL_AudioDevice *device = open_devices[i]; SDL_AudioDevice *device = open_devices[i];
if (device->hidden->audioQueue && !device->hidden->interrupted) { if (device->hidden->audioQueue && !device->hidden->interrupted) {
AudioQueuePause(device->hidden->audioQueue); AudioQueuePause(device->hidden->audioQueue);
} }
} }
} }
static void resume_audio_devices() static void resume_audio_devices()
{ {
int i; int i;
if (!open_devices) { if (!open_devices) {
return; return;
} }
for (i = 0; i < num_open_devices; ++i) { for (i = 0; i < num_open_devices; ++i) {
SDL_AudioDevice *device = open_devices[i]; SDL_AudioDevice *device = open_devices[i];
if (device->hidden->audioQueue && !device->hidden->interrupted) { if (device->hidden->audioQueue && !device->hidden->interrupted) {
AudioQueueStart(device->hidden->audioQueue, NULL); AudioQueueStart(device->hidden->audioQueue, NULL);
} }
} }
} }
static void interruption_begin(_THIS) static void interruption_begin(_THIS)
@ -406,26 +406,29 @@ static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrec
if (category == AVAudioSessionCategoryPlayAndRecord) { if (category == AVAudioSessionCategoryPlayAndRecord) {
options |= AVAudioSessionCategoryOptionDefaultToSpeaker; options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
} }
#endif
if (category == AVAudioSessionCategoryRecord || if (category == AVAudioSessionCategoryRecord ||
category == AVAudioSessionCategoryPlayAndRecord) { category == AVAudioSessionCategoryPlayAndRecord) {
options |= AVAudioSessionCategoryOptionAllowBluetooth; /* AVAudioSessionCategoryOptionAllowBluetooth isn't available in the SDK for
Apple TV but is still needed in order to output to Bluetooth devices.
*/
options |= 0x4; /* AVAudioSessionCategoryOptionAllowBluetooth; */
} }
#endif
if (category == AVAudioSessionCategoryPlayAndRecord) { if (category == AVAudioSessionCategoryPlayAndRecord) {
options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP | options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP |
AVAudioSessionCategoryOptionAllowAirPlay; AVAudioSessionCategoryOptionAllowAirPlay;
} }
if (category == AVAudioSessionCategoryPlayback || if (category == AVAudioSessionCategoryPlayback ||
category == AVAudioSessionCategoryPlayAndRecord) { category == AVAudioSessionCategoryPlayAndRecord) {
options |= AVAudioSessionCategoryOptionDuckOthers; options |= AVAudioSessionCategoryOptionDuckOthers;
} }
if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) { if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) {
if (![session.category isEqualToString:category] || session.categoryOptions != options) { if (![session.category isEqualToString:category] || session.categoryOptions != options) {
/* Stop the current session so we don't interrupt other application audio */ /* Stop the current session so we don't interrupt other application audio */
pause_audio_devices(); pause_audio_devices();
[session setActive:NO error:nil]; [session setActive:NO error:nil];
session_active = NO; session_active = NO;
if (![session setCategory:category mode:mode options:options error:&err]) { if (![session setCategory:category mode:mode options:options error:&err]) {
NSString *desc = err.description; NSString *desc = err.description;
@ -435,10 +438,10 @@ static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrec
} }
} else { } else {
if (![session.category isEqualToString:category]) { if (![session.category isEqualToString:category]) {
/* Stop the current session so we don't interrupt other application audio */ /* Stop the current session so we don't interrupt other application audio */
pause_audio_devices(); pause_audio_devices();
[session setActive:NO error:nil]; [session setActive:NO error:nil];
session_active = NO; session_active = NO;
if (![session setCategory:category error:&err]) { if (![session setCategory:category error:&err]) {
NSString *desc = err.description; NSString *desc = err.description;
@ -459,12 +462,12 @@ static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrec
SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String); SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
return NO; return NO;
} }
session_active = YES; session_active = YES;
resume_audio_devices(); resume_audio_devices();
} else if (!open_playback_devices && !open_capture_devices && session_active) { } else if (!open_playback_devices && !open_capture_devices && session_active) {
pause_audio_devices(); pause_audio_devices();
[session setActive:NO error:nil]; [session setActive:NO error:nil];
session_active = NO; session_active = NO;
} }
if (open) { if (open) {
@ -492,12 +495,12 @@ static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrec
this->hidden->interruption_listener = CFBridgingRetain(listener); this->hidden->interruption_listener = CFBridgingRetain(listener);
} else { } else {
SDLInterruptionListener *listener = nil; SDLInterruptionListener *listener = nil;
listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener); listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
[center removeObserver:listener]; [center removeObserver:listener];
@synchronized (listener) { @synchronized (listener) {
listener.device = NULL; listener.device = NULL;
} }
} }
} }
@ -671,7 +674,7 @@ static void
COREAUDIO_CloseDevice(_THIS) COREAUDIO_CloseDevice(_THIS)
{ {
const SDL_bool iscapture = this->iscapture; const SDL_bool iscapture = this->iscapture;
int i; int i;
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */ /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */ /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
@ -691,19 +694,19 @@ COREAUDIO_CloseDevice(_THIS)
update_audio_session(this, SDL_FALSE, SDL_TRUE); update_audio_session(this, SDL_FALSE, SDL_TRUE);
#endif #endif
for (i = 0; i < num_open_devices; ++i) { for (i = 0; i < num_open_devices; ++i) {
if (open_devices[i] == this) { if (open_devices[i] == this) {
--num_open_devices; --num_open_devices;
if (i < num_open_devices) { if (i < num_open_devices) {
SDL_memmove(&open_devices[i], &open_devices[i+1], sizeof(open_devices[i])*(num_open_devices - i)); SDL_memmove(&open_devices[i], &open_devices[i+1], sizeof(open_devices[i])*(num_open_devices - i));
} }
break; break;
} }
} }
if (num_open_devices == 0) { if (num_open_devices == 0) {
SDL_free(open_devices); SDL_free(open_devices);
open_devices = NULL; open_devices = NULL;
} }
/* if callback fires again, feed silence; don't call into the app. */ /* if callback fires again, feed silence; don't call into the app. */
SDL_AtomicSet(&this->paused, 1); SDL_AtomicSet(&this->paused, 1);
@ -1009,7 +1012,7 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
AudioStreamBasicDescription *strdesc; AudioStreamBasicDescription *strdesc;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
int valid_datatype = 0; int valid_datatype = 0;
SDL_AudioDevice **new_open_devices; SDL_AudioDevice **new_open_devices;
/* Initialize all variables that we clean on shutdown */ /* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *) this->hidden = (struct SDL_PrivateAudioData *)
@ -1027,11 +1030,11 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
open_playback_devices++; open_playback_devices++;
} }
new_open_devices = (SDL_AudioDevice **)SDL_realloc(open_devices, sizeof(open_devices[0]) * (num_open_devices + 1)); new_open_devices = (SDL_AudioDevice **)SDL_realloc(open_devices, sizeof(open_devices[0]) * (num_open_devices + 1));
if (new_open_devices) { if (new_open_devices) {
open_devices = new_open_devices; open_devices = new_open_devices;
open_devices[num_open_devices++] = this; open_devices[num_open_devices++] = this;
} }
#if !MACOSX_COREAUDIO #if !MACOSX_COREAUDIO
if (!update_audio_session(this, SDL_TRUE, SDL_TRUE)) { if (!update_audio_session(this, SDL_TRUE, SDL_TRUE)) {