[vlc-devel] [PATCH 4/4] audiotrack: Fix deadlock with Android 4.4.2, 4.4.3 and 4.4.4

Thomas Guillem thomas at gllm.fr
Thu Mar 5 19:03:33 CET 2015


see https://code.google.com/p/android/issues/detail?id=65807
see https://code.google.com/p/android/issues/detail?id=70877
---
 modules/audio_output/audiotrack.c | 42 +++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/modules/audio_output/audiotrack.c b/modules/audio_output/audiotrack.c
index a8eb020..a2419ac 100644
--- a/modules/audio_output/audiotrack.c
+++ b/modules/audio_output/audiotrack.c
@@ -65,6 +65,7 @@ struct aout_sys_t {
     uint32_t i_max_audiotrack_samples;
     mtime_t i_play_time; /* time when play was called */
     bool b_audiotrack_exception; /* true if audiotrack throwed an exception */
+    int i_audiotrack_stuck_count;
 
     /* JNIThread control */
     vlc_mutex_t mutex;
@@ -265,10 +266,8 @@ InitJNIFields( audio_output_t *p_aout )
     } else
         GET_ID( GetMethodID, AudioTrack.write, "write", "([BII)I", true );
 
-#ifdef AUDIOTRACK_USE_TIMESTAMP
     GET_ID( GetMethodID, AudioTrack.getTimestamp,
             "getTimestamp", "(Landroid/media/AudioTimestamp;)Z", false );
-#endif
     GET_ID( GetMethodID, AudioTrack.getPlaybackHeadPosition,
             "getPlaybackHeadPosition", "()I", true );
 
@@ -617,6 +616,7 @@ JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
         goto error;
     }
 
+#ifdef AUDIOTRACK_USE_TIMESTAMP
     if( jfields.AudioTimestamp.clazz )
     {
         /* create AudioTimestamp object */
@@ -630,6 +630,7 @@ JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
         if( !p_sys->p_audioTimestamp )
             goto error;
     }
+#endif
 
     p_sys->fmt.i_rate = i_rate;
 
@@ -696,7 +697,25 @@ JNIThread_Write( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer )
 
     /* check if audiotrack buffer is not full before writing on it. */
     if( i_samples_pending >= p_sys->i_max_audiotrack_samples )
-        return 0;
+    {
+
+        /* HACK: AudioFlinger can drop frames without notifying us and there is
+         * no way to know it. It it happens, i_audiotrack_pos won't move and
+         * the current code will be stuck because it'll assume that audiotrack
+         * internal buffer is full when it's not. It can happen only after
+         * Android 4.4.2 if we send frames too quickly. This HACK is just an
+         * other precaution since it shouldn't happen anymore thanks to the
+         * HACK in JNIThread_Play */
+
+        p_sys->i_audiotrack_stuck_count++;
+        if( p_sys->i_audiotrack_stuck_count > 100 )
+        {
+            msg_Warn( p_aout, "AudioFlinger underrun, force write" );
+            i_samples_pending = 0;
+            p_sys->i_audiotrack_stuck_count = 0;
+        }
+    } else
+        p_sys->i_audiotrack_stuck_count = 0;
     i_samples = __MIN( p_sys->i_max_audiotrack_samples - i_samples_pending,
                        p_buffer->i_nb_samples );
 
@@ -732,10 +751,8 @@ JNIThread_Write( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer )
 }
 
 /**
- * JNIThread_Write doesn't always work on Lollipop. Probably because audiotrack
- * buffer size is bigger than the one we pass in ctor arguments (and there is
- * no way to know it). It's not an issue since there is a new write method that
- * is non blocking.
+ * Non blocking write function for Lollipop and after.
+ * It calls a new write method with WRITE_NON_BLOCKING flags.
  */
 static int
 JNIThread_WriteV21( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer )
@@ -823,8 +840,17 @@ JNIThread_Play( JNIEnv *env, audio_output_t *p_aout,
         p_buffer->i_nb_samples -= i_samples;
         if( p_buffer->i_buffer == 0 )
             *pp_buffer = NULL;
-    }
 
+        /* HACK: There is a known issue in audiotrack, "due to an internal
+         * timeout within the AudioTrackThread". It happens after android
+         * 4.4.2, it's not a problem for Android 5.0 since we use an other way
+         * to write samples. A working hack is to wait a little between each
+         * write. This hack is done only for API 19 (AudioTimestamp was added
+         * in API 19). */
+
+        if( jfields.AudioTimestamp.clazz && !jfields.AudioTrack.writeV21 )
+            *p_wait = FRAMES_TO_US( i_samples ) / 2;
+    }
     return i_ret >= 0 ? VLC_SUCCESS : VLC_EGENERIC;
 }
 
-- 
2.1.3




More information about the vlc-devel mailing list