vlc/vlc-3.0 | branch: master | Carola Nitz <[email protected]> | Mon Dec 11 10:52:37 2017 +0100| [4793c4ff4c449e6dccd0b52d351f8402c70368a9] | committer: Thomas Guillem
audiounit_ios: fix deadlock on interruption Add ca_setAliveState() that sets the b_paused to true and unblock ca_Play() or ca_Flush() that could wait for ca_Render(). (cherry picked from commit 7e4c7f35ec3f713832ae51fde62f52990938842f) Signed-off-by: Thomas Guillem <[email protected]> > http://git.videolan.org/gitweb.cgi/vlc/vlc-3.0.git/?a=commit;h=4793c4ff4c449e6dccd0b52d351f8402c70368a9 --- modules/audio_output/audiounit_ios.m | 26 ++++++++++++++++++-- modules/audio_output/coreaudio_common.c | 43 +++++++++++++++++++++++++++++++-- modules/audio_output/coreaudio_common.h | 3 +++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/modules/audio_output/audiounit_ios.m b/modules/audio_output/audiounit_ios.m index a53a15f93d..c87f3572b9 100644 --- a/modules/audio_output/audiounit_ios.m +++ b/modules/audio_output/audiounit_ios.m @@ -127,6 +127,23 @@ enum port_type aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT); } +- (void)handleInterruption:(NSNotification *)notification +{ + audio_output_t *p_aout = [self aout]; + NSDictionary *userInfo = notification.userInfo; + if (!userInfo || !userInfo[AVAudioSessionInterruptionTypeKey]) { + return; + } + + NSUInteger interruptionType = [userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; + + if (interruptionType == AVAudioSessionInterruptionTypeBegan) { + ca_SetAliveState(p_aout, false); + } else if (interruptionType == AVAudioSessionInterruptionTypeEnded + && [userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue] == AVAudioSessionInterruptionOptionShouldResume) { + ca_SetAliveState(p_aout, true); + } +} @end static void @@ -467,8 +484,13 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt) Pause(p_aout, true, 0); [[NSNotificationCenter defaultCenter] addObserver:p_sys->aoutWrapper - selector:@selector(audioSessionRouteChange:) - name:AVAudioSessionRouteChangeNotification object:nil]; + selector:@selector(audioSessionRouteChange:) + name:AVAudioSessionRouteChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:p_sys->aoutWrapper + selector:@selector(handleInterruption:) + name:AVAudioSessionInterruptionNotification + object:nil]; free(layout); fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP; diff --git a/modules/audio_output/coreaudio_common.c b/modules/audio_output/coreaudio_common.c index d2e846587a..d113975432 100644 --- a/modules/audio_output/coreaudio_common.c +++ b/modules/audio_output/coreaudio_common.c @@ -51,6 +51,7 @@ ca_Open(audio_output_t *p_aout) atomic_init(&p_sys->b_paused, false); atomic_init(&p_sys->b_do_flush, false); vlc_sem_init(&p_sys->flush_sem, 0); + vlc_mutex_init(&p_sys->lock); p_aout->play = ca_Play; p_aout->pause = ca_Pause; @@ -64,6 +65,7 @@ ca_Close(audio_output_t *p_aout) struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys; vlc_sem_destroy(&p_sys->flush_sem); + vlc_mutex_destroy(&p_sys->lock); } /* Called from render callbacks. No lock, wait, and IO here */ @@ -134,6 +136,12 @@ ca_Flush(audio_output_t *p_aout, bool wait) while (TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes) != NULL) { + if (atomic_load(&p_sys->b_paused)) + { + TPCircularBufferClear(&p_sys->circular_buffer); + return; + } + /* Calculate the duration of the circular buffer, in order to wait * for the render thread to play it all */ const mtime_t i_frame_us = @@ -144,9 +152,23 @@ ca_Flush(audio_output_t *p_aout, bool wait) } else { - /* Request the renderer to flush, and wait for an ACK */ + /* Request the renderer to flush, and wait for an ACK. + * b_do_flush and b_paused need to be locked together in order to not + * get stuck here when b_paused is being set after reading. This can + * happen when setAliveState() is called from any thread through an + * interrupt notification */ + + vlc_mutex_lock(&p_sys->lock); assert(!atomic_load(&p_sys->b_do_flush)); + if (atomic_load(&p_sys->b_paused)) + { + vlc_mutex_unlock(&p_sys->lock); + TPCircularBufferClear(&p_sys->circular_buffer); + return; + } + atomic_store_explicit(&p_sys->b_do_flush, true, memory_order_release); + vlc_mutex_unlock(&p_sys->lock); vlc_sem_wait(&p_sys->flush_sem); } } @@ -259,11 +281,28 @@ void ca_Uninitialize(audio_output_t *p_aout) { struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys; - /* clean-up circular buffer */ TPCircularBufferCleanup(&p_sys->circular_buffer); } +void +ca_SetAliveState(audio_output_t *p_aout, bool alive) +{ + struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys; + + vlc_mutex_lock(&p_sys->lock); + atomic_store(&p_sys->b_paused, !alive); + + bool expected = true; + if (!alive && atomic_compare_exchange_strong(&p_sys->b_do_flush, &expected, false)) + { + TPCircularBufferClear(&p_sys->circular_buffer); + /* Signal that the renderer is flushed */ + vlc_sem_post(&p_sys->flush_sem); + } + vlc_mutex_unlock(&p_sys->lock); +} + AudioUnit au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type) { diff --git a/modules/audio_output/coreaudio_common.h b/modules/audio_output/coreaudio_common.h index 6719db6580..75b50a53d0 100644 --- a/modules/audio_output/coreaudio_common.h +++ b/modules/audio_output/coreaudio_common.h @@ -56,6 +56,7 @@ struct aout_sys_common atomic_bool b_paused; atomic_bool b_do_flush; vlc_sem_t flush_sem; + vlc_mutex_t lock; int i_rate; unsigned int i_bytes_per_frame; unsigned int i_frame_length; @@ -84,6 +85,8 @@ int ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt, void ca_Uninitialize(audio_output_t *p_aout); +void ca_SetAliveState(audio_output_t *p_aout, bool alive); + AudioUnit au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type); int au_Initialize(audio_output_t *p_aout, AudioUnit au, _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
