[vlc-commits] audiounit_ios: fix deadlock on interruption
Carola Nitz
git at videolan.org
Tue Dec 12 15:16:26 CET 2017
vlc | branch: master | Carola Nitz <nitz.carola at googlemail.com> | Mon Dec 11 10:52:37 2017 +0100| [7e4c7f35ec3f713832ae51fde62f52990938842f] | 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().
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=7e4c7f35ec3f713832ae51fde62f52990938842f
---
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,
More information about the vlc-commits
mailing list