iOS AudioSessionSetActive() bloqueando el hilo principal?
multithreading uiview (1)
en mi aplicación de iOS, estoy tratando de implementar "ducking": mientras mi aplicación reproduce un sonido corto parecido al de un comando, cualquier música de fondo debería reducirse en volumen. Después de haber reproducido el sonido, el volumen de la música debería volver a su valor original.
Según lo implementado, ducking básicamente funciona como se esperaba. Sin embargo, cuando llamo AudioSessionSetActive (NO) en audioPlayerDidFinishPlaying: para terminar con ducking, hay una pequeña pausa en las actualizaciones de UI que ocurren en este momento. Esto implica dibujo personalizado, así como para ex. desplazamiento automático de texto, etc.
Ahora, aquí está la pregunta:
¿Es este un problema conocido en iOS6? Estoy ejecutando el mismo código en un iPod / iOS5, donde no veo este comportamiento. ¿O me estoy perdiendo algo del código? Tal vez uno de ustedes ya se encontró con el mismo problema y encontró una solución viable.
Muchas gracias por su amable apoyo,
Goetz
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//...
NSError *err = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&err];
//...
}
- (void) playSound {
// Enable ducking of music playing in the background (code taken from the Breadcrumb iOS Sample)
UInt32 value = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(value), &value);
// Required if using kAudioSessionCategory_MediaPlayback
value = YES;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(value), &value);
UInt32 isOtherAudioPlaying = 0;
UInt32 size = sizeof(isOtherAudioPlaying);
AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &size, &isOtherAudioPlaying);
if (isOtherAudioPlaying) {
AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(value), &value);
}
AudioSessionSetActive(YES);
// Initialization of the AVAudioPlayer
NSString *soundFileName = [[NSBundle mainBundle] pathForResource:@"Beep" ofType:@"caf"];
NSURL *soundFileURL = [[NSURL alloc] initFileURLWithPath:soundFileURL];
self.soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
[self.soundPlayer setDelegate:self];
[self.soundPlayer setVolume:[80.0/100.0];
[self.soundPlayer play];
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// Callback coming from the AVAudioPlayer
// This will block the main thread; however, it is necessary to disable ducking again
AudioSessionSetActive(NO);
}
Después de algunos rasguños y depuración, encontré la respuesta a esta pregunta. Como dice la pregunta original, para volver a subir el nivel de audio después de agacharse, debe desactivar la sesión de audio.
El problema es que la desactivación de la sesión provoca un retraso de .5 segundos cuando se reproduce el audio, lo que bloquea el subproceso de IU y hace que la aplicación no responda (en mi caso, un temporizador pierde 0,5 segundos y tartamudea).
Para resolver el problema, realizo mi llamada para desactivar el temporizador en un hilo separado. Esto resuelve el problema de bloqueo de la interfaz de usuario y permite que el audio se agache como se esperaba. El siguiente código muestra la solución. Tenga en cuenta que este es el código C # porque estoy usando Xamarin, pero podría traducirse fácilmente a Objective-C o Swift para el mismo resultado:
private void ActivateAudioSession()
{
var session = AVAudioSession.SharedInstance();
session.SetCategory(AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers);
session.SetActive(true);
}
private void DeactivateAudioSession()
{
new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
var session = AVAudioSession.SharedInstance();
session.SetActive(false);
})).Start();
}
Llamo a ActivateAudioSession antes de conectar mi AVAudioPlayer y una vez que mi reproductor termina de reproducirse, llamo DeactivateAudioSession (que es necesario para volver a subir el nivel de audio). Iniciar la desactivación en un nuevo subproceso hace retroceder el nivel de audio, pero no bloquea la interfaz de usuario.