[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