[vlc-devel] [PATCH 2/4] android/audio: split device from stream handling

Thomas Guillem thomas at gllm.fr
Wed Dec 2 15:44:46 CET 2020


Like mmdevice with wasapi/directsound.

To get support for multiple audio tracks playback at once, all aout
modules need to be split between device and stream. This is the first
step in that direction (second step is to add aout_stream_t in the CORE).

This will allow a single aout module to choose between audiotrack,
opensles and the future aaudio plugin depending on the audio
format/codec. Indeed, only AudioTrack can handle pass-through, and PCM
playback should be done by aaudio in priority.
---
 modules/audio_output/Makefile.am          |   6 +-
 modules/audio_output/android/audiotrack.c | 320 ++++++----------------
 modules/audio_output/android/device.c     | 301 ++++++++++++++++++++
 modules/audio_output/android/device.h     |  63 +++++
 4 files changed, 447 insertions(+), 243 deletions(-)
 create mode 100644 modules/audio_output/android/device.c
 create mode 100644 modules/audio_output/android/device.h

diff --git a/modules/audio_output/Makefile.am b/modules/audio_output/Makefile.am
index 930dea93a48..3c6713f32ca 100644
--- a/modules/audio_output/Makefile.am
+++ b/modules/audio_output/Makefile.am
@@ -4,12 +4,12 @@ aout_LTLIBRARIES =
 libopensles_android_plugin_la_SOURCES = audio_output/android/opensles.c
 libopensles_android_plugin_la_LIBADD = $(LIBDL) $(LIBM)
 
-libandroid_audiotrack_plugin_la_SOURCES = audio_output/android/audiotrack.c \
+libandroid_audio_plugin_la_SOURCES = audio_output/android/audiotrack.c \
+	audio_output/android/device.c audio_output/android/device.h \
 	video_output/android/utils.c video_output/android/utils.h
-libandroid_audiotrack_plugin_la_CFLAGS = $(AM_CFLAGS)
 
 if HAVE_ANDROID
-aout_LTLIBRARIES += libandroid_audiotrack_plugin.la libopensles_android_plugin.la
+aout_LTLIBRARIES += libandroid_audio_plugin.la libopensles_android_plugin.la
 endif
 
 libadummy_plugin_la_SOURCES = audio_output/adummy.c
diff --git a/modules/audio_output/android/audiotrack.c b/modules/audio_output/android/audiotrack.c
index a1b9aee602c..56d4699d608 100644
--- a/modules/audio_output/android/audiotrack.c
+++ b/modules/audio_output/android/audiotrack.c
@@ -34,6 +34,7 @@
 #include <vlc_plugin.h>
 #include <vlc_aout.h>
 #include "../video_output/android/utils.h"
+#include "device.h"
 
 #define SMOOTHPOS_SAMPLE_COUNT 10
 #define SMOOTHPOS_INTERVAL_US VLC_TICK_FROM_MS(30) // 30ms
@@ -42,49 +43,16 @@
 
 #define AUDIOTRACK_MAX_BUFFER_US VLC_TICK_FROM_MS(750) // 750ms
 
-static int  Open( vlc_object_t * );
-static void Close( vlc_object_t * );
-static void Stop( audio_output_t * );
-static int Start( audio_output_t *, audio_sample_format_t * );
+static void Stop( aout_stream_t * );
+static void Play( aout_stream_t *, block_t *, vlc_tick_t );
+static void Flush( aout_stream_t * );
+static void Pause( aout_stream_t *, bool, vlc_tick_t );
+static void VolumeSet( aout_stream_t *, float );
+static void MuteSet( aout_stream_t *, bool );
 static void *AudioTrack_Thread( void * );
 
-/* There is an undefined behavior when configuring AudioTrack with SPDIF or
- * more than 2 channels when there is no HDMI out. It may succeed and the
- * Android ressampler will be used to downmix to stereo. It may fails cleanly,
- * and this module will be able to recover and fallback to stereo. Finally, in
- * some rare cases, it may crash during init or while ressampling. Because of
- * the last case we don't try up to 8 channels and we use AT_DEV_STEREO device
- * per default */
-enum at_dev {
-    AT_DEV_STEREO = 0,
-    AT_DEV_PCM,
-    AT_DEV_ENCODED,
-};
-#define AT_DEV_DEFAULT AT_DEV_STEREO
-#define AT_DEV_MAX_CHANNELS 8
-
-static const struct {
-    const char *id;
-    const char *name;
-    enum at_dev at_dev;
-} at_devs[] = {
-    { "stereo", "Up to 2 channels (compat mode).", AT_DEV_STEREO },
-    { "pcm", "Up to 8 channels.", AT_DEV_PCM },
-
-    /* With "encoded", the module will try to play every audio codecs via
-     * passthrough.
-     *
-     * With "encoded:ENCODING_FLAGS_MASK", the module will try to play only
-     * codecs specified by ENCODING_FLAGS_MASK. This extra value is a long long
-     * that contains binary-shifted AudioFormat.ENCODING_* values. */
-    { "encoded", "Up to 8 channels, passthrough if available.", AT_DEV_ENCODED },
-    {  NULL, NULL, AT_DEV_DEFAULT },
-};
-
 typedef struct
 {
-    enum at_dev at_dev;
-
     jobject p_audiotrack; /* AudioTrack ref */
     jobject p_dp;
     float volume;
@@ -179,21 +147,6 @@ typedef struct
  * will be done by VLC */
 #define AUDIOTRACK_NATIVE_SAMPLERATE
 
-#define AUDIOTRACK_SESSION_ID_TEXT " Id of audio session the AudioTrack must be attached to"
-
-vlc_module_begin ()
-    set_shortname( "AudioTrack" )
-    set_description( "Android AudioTrack audio output" )
-    set_capability( "audio output", 180 )
-    set_category( CAT_AUDIO )
-    set_subcategory( SUBCAT_AUDIO_AOUT )
-    add_integer( "audiotrack-session-id", 0,
-            AUDIOTRACK_SESSION_ID_TEXT, NULL, true )
-        change_private()
-    add_shortcut( "audiotrack" )
-    set_callbacks( Open, Close )
-vlc_module_end ()
-
 #define THREAD_NAME "android_audiotrack"
 #define GET_ENV() android_getEnv( VLC_OBJECT(p_aout), THREAD_NAME )
 
@@ -296,14 +249,17 @@ static struct
 
 /* init all jni fields.
  * Done only one time during the first initialisation */
-static bool
-InitJNIFields( audio_output_t *p_aout, JNIEnv* env )
+int
+AudioTrack_InitJNI( audio_output_t *p_aout )
 {
     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
     static int i_init_state = -1;
-    bool ret;
     jclass clazz;
     jfieldID field;
+    JNIEnv *env = GET_ENV();
+
+    if( env == NULL )
+        return VLC_EGENERIC;
 
     vlc_mutex_lock( &lock );
 
@@ -527,15 +483,12 @@ InitJNIFields( audio_output_t *p_aout, JNIEnv* env )
 
     i_init_state = 1;
 end:
-    ret = i_init_state == 1;
-    if( !ret )
-        msg_Err( p_aout, "AudioTrack jni init failed" );
     vlc_mutex_unlock( &lock );
-    return ret;
+    return i_init_state == 1 ? VLC_SUCCESS : VLC_EGENERIC;
 }
 
 static inline bool
-check_exception( JNIEnv *env, audio_output_t *p_aout,
+check_exception( JNIEnv *env, aout_stream_t *p_aout,
                  const char *class, const char *method )
 {
     if( (*env)->ExceptionCheck( env ) )
@@ -608,7 +561,7 @@ us_to_frames( aout_sys_t *p_sys, vlc_tick_t i_us )
  * true for all devices or Android versions.
  */
 static uint64_t
-AudioTrack_getPlaybackHeadPosition( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_getPlaybackHeadPosition( JNIEnv *env, aout_stream_t *p_aout )
 {
     /* Android doc:
      * getPlaybackHeadPosition: Returns the playback head position expressed in
@@ -639,7 +592,7 @@ AudioTrack_getPlaybackHeadPosition( JNIEnv *env, audio_output_t *p_aout )
  * Called after flush, or start
  */
 static void
-AudioTrack_ResetWrapCount( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_ResetWrapCount( JNIEnv *env, aout_stream_t *p_aout )
 {
     (void) env;
     aout_sys_t *p_sys = p_aout->sys;
@@ -655,7 +608,7 @@ AudioTrack_ResetWrapCount( JNIEnv *env, audio_output_t *p_aout )
  * Reset AudioTrack SmoothPosition and TimestampPosition
  */
 static void
-AudioTrack_ResetPositions( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_ResetPositions( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
     VLC_UNUSED( env );
@@ -674,7 +627,7 @@ AudioTrack_ResetPositions( JNIEnv *env, audio_output_t *p_aout )
  * Reset all AudioTrack positions and internal state
  */
 static void
-AudioTrack_Reset( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_Reset( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
 
@@ -684,7 +637,7 @@ AudioTrack_Reset( JNIEnv *env, audio_output_t *p_aout )
 }
 
 static vlc_tick_t
-AudioTrack_GetLatencyUs( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_GetLatencyUs( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
 
@@ -713,7 +666,7 @@ AudioTrack_GetLatencyUs( JNIEnv *env, audio_output_t *p_aout )
  * precision (+/- 20ms on old devices).
  */
 static vlc_tick_t
-AudioTrack_GetSmoothPositionUs( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_GetSmoothPositionUs( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
     uint64_t i_audiotrack_us;
@@ -750,7 +703,7 @@ bailout:
 }
 
 static vlc_tick_t
-AudioTrack_GetTimestampPositionUs( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_GetTimestampPositionUs( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
     vlc_tick_t i_now;
@@ -819,7 +772,7 @@ AudioTrack_GetTimestampPositionUs( JNIEnv *env, audio_output_t *p_aout )
 }
 
 static int
-TimeGet( audio_output_t *p_aout, vlc_tick_t *restrict p_delay )
+TimeGet( aout_stream_t *p_aout, vlc_tick_t *restrict p_delay )
 {
     aout_sys_t *p_sys = p_aout->sys;
     vlc_tick_t i_audiotrack_us;
@@ -912,7 +865,7 @@ AudioTrack_GetChanOrder( uint16_t i_physical_channels, uint32_t p_chans_out[] )
 }
 
 static jobject
-AudioTrack_New21( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,
+AudioTrack_New21( JNIEnv *env, aout_stream_t *p_aout, unsigned int i_rate,
                   int i_channel_config, int i_format, int i_size,
                   jint session_id )
 {
@@ -994,7 +947,7 @@ del_local_refs:
 }
 
 static jobject
-AudioTrack_NewLegacy( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,
+AudioTrack_NewLegacy( JNIEnv *env, aout_stream_t *p_aout, unsigned int i_rate,
                       int i_channel_config, int i_format, int i_size,
                       jint session_id )
 {
@@ -1009,7 +962,7 @@ AudioTrack_NewLegacy( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,
  * returns -1 on error, 0 on success.
  */
 static int
-AudioTrack_New( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,
+AudioTrack_New( JNIEnv *env, aout_stream_t *p_aout, unsigned int i_rate,
                 int i_channel_config, int i_format, int i_size )
 {
     aout_sys_t *p_sys = p_aout->sys;
@@ -1065,7 +1018,7 @@ AudioTrack_New( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,
  * returns -1 on error, 0 on success.
  */
 static int
-AudioTrack_Recreate( JNIEnv *env, audio_output_t *p_aout )
+AudioTrack_Recreate( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
 
@@ -1093,7 +1046,7 @@ AudioTrack_Recreate( JNIEnv *env, audio_output_t *p_aout )
  * returns -1 on configuration error, 0 on success.
  */
 static int
-AudioTrack_Create( JNIEnv *env, audio_output_t *p_aout,
+AudioTrack_Create( JNIEnv *env, aout_stream_t *p_aout,
                    unsigned int i_rate,
                    int i_format,
                    uint16_t i_physical_channels )
@@ -1151,13 +1104,11 @@ AudioTrack_Create( JNIEnv *env, audio_output_t *p_aout,
     return 0;
 }
 
-static bool
-AudioTrack_HasEncoding( audio_output_t *p_aout, vlc_fourcc_t i_format )
+bool
+AudioTrack_HasEncoding( long long encoding_flags, vlc_fourcc_t i_format )
 {
-    aout_sys_t *p_sys = p_aout->sys;
-
 #define MATCH_ENCODING_FLAG(x) jfields.AudioFormat.has_##x && \
-    ( p_sys->i_encoding_flags == 0 || p_sys->i_encoding_flags & (1 << jfields.AudioFormat.x) )
+    (encoding_flags == 0 || encoding_flags & (1 << jfields.AudioFormat.x) )
 
     switch( i_format )
     {
@@ -1173,19 +1124,16 @@ AudioTrack_HasEncoding( audio_output_t *p_aout, vlc_fourcc_t i_format )
         case VLC_CODEC_MLP:
             return MATCH_ENCODING_FLAG( ENCODING_DOLBY_TRUEHD );
         default:
-            return false;
+            return true;
     }
 }
 
 static int
-StartPassthrough( JNIEnv *env, audio_output_t *p_aout )
+StartPassthrough( JNIEnv *env, aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
     int i_at_format;
 
-    if( !AudioTrack_HasEncoding( p_aout, p_sys->fmt.i_format ) )
-        return VLC_EGENERIC;
-
     if( jfields.AudioFormat.has_ENCODING_IEC61937 )
     {
         i_at_format = jfields.AudioFormat.ENCODING_IEC61937;
@@ -1267,7 +1215,7 @@ StartPassthrough( JNIEnv *env, audio_output_t *p_aout )
 }
 
 static int
-StartPCM( JNIEnv *env, audio_output_t *p_aout, unsigned i_max_channels )
+StartPCM( JNIEnv *env, aout_stream_t *p_aout, unsigned i_max_channels )
 {
     aout_sys_t *p_sys = p_aout->sys;
     unsigned i_nb_channels;
@@ -1368,29 +1316,41 @@ StartPCM( JNIEnv *env, audio_output_t *p_aout, unsigned i_max_channels )
     return VLC_SUCCESS;
 }
 
-static int
-Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
+int
+AudioTrack_Start( aout_stream_t *p_aout, audio_sample_format_t *restrict p_fmt,
+                  enum android_audio_device_type adev )
 {
-    aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env;
     int i_ret;
     bool b_try_passthrough;
     unsigned i_max_channels;
 
-    if( p_sys->at_dev == AT_DEV_ENCODED )
+    if( adev == ANDROID_AUDIO_DEVICE_ENCODED )
     {
         b_try_passthrough = true;
-        i_max_channels = AT_DEV_MAX_CHANNELS;
+        i_max_channels = ANDROID_AUDIO_DEVICE_MAX_CHANNELS;
     }
     else
     {
-        b_try_passthrough = var_InheritBool( p_aout, "spdif" );
-        i_max_channels = p_sys->at_dev == AT_DEV_STEREO ? 2 : AT_DEV_MAX_CHANNELS;
+        b_try_passthrough = false;
+        i_max_channels = adev == ANDROID_AUDIO_DEVICE_STEREO ? 2 : ANDROID_AUDIO_DEVICE_MAX_CHANNELS;
     }
 
     if( !( env = GET_ENV() ) )
         return VLC_EGENERIC;
 
+    aout_sys_t *p_sys = p_aout->sys = calloc( 1, sizeof (aout_sys_t) );
+
+    if( unlikely( p_sys == NULL ) )
+        return VLC_ENOMEM;
+
+    vlc_mutex_init(&p_sys->lock);
+    vlc_cond_init(&p_sys->aout_cond);
+    vlc_cond_init(&p_sys->thread_cond);
+
+    p_sys->volume = 1.0f;
+    p_sys->mute = false;
+
     p_sys->fmt = *p_fmt;
 
     aout_FormatPrint( p_aout, "VLC is looking for:", &p_sys->fmt );
@@ -1564,11 +1524,16 @@ Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
 
     *p_fmt = p_sys->fmt;
 
-    p_aout->volume_set(p_aout, p_sys->volume);
-    if (p_sys->mute)
-        p_aout->mute_set(p_aout, true);
     aout_FormatPrint( p_aout, "VLC will output:", &p_sys->fmt );
 
+    p_aout->stop = Stop;
+    p_aout->play = Play;
+    p_aout->pause = Pause;
+    p_aout->flush = Flush;
+    p_aout->time_get = TimeGet;
+    p_aout->volume_set = VolumeSet;
+    p_aout->mute_set = MuteSet;
+
     return VLC_SUCCESS;
 
 error:
@@ -1577,7 +1542,7 @@ error:
 }
 
 static void
-Stop( audio_output_t *p_aout )
+Stop( aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env;
@@ -1607,21 +1572,14 @@ Stop( audio_output_t *p_aout )
                 JNI_AT_CALL_VOID( release );
         }
         (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
-        p_sys->p_audiotrack = NULL;
     }
 
     if( p_sys->p_dp )
-    {
         (*env)->DeleteGlobalRef( env, p_sys->p_dp );
-        p_sys->p_dp = NULL;
-    }
 
     /* Release the timestamp object */
     if( p_sys->timestamp.p_obj )
-    {
         (*env)->DeleteGlobalRef( env, p_sys->timestamp.p_obj );
-        p_sys->timestamp.p_obj = NULL;
-    }
 
     /* Release the Circular buffer data */
     switch( p_sys->i_write_type )
@@ -1629,34 +1587,22 @@ Stop( audio_output_t *p_aout )
     case WRITE_BYTEARRAY:
     case WRITE_BYTEARRAYV23:
         if( p_sys->circular.u.p_bytearray )
-        {
             (*env)->DeleteGlobalRef( env, p_sys->circular.u.p_bytearray );
-            p_sys->circular.u.p_bytearray = NULL;
-        }
         break;
     case WRITE_SHORTARRAYV23:
         if( p_sys->circular.u.p_shortarray )
-        {
             (*env)->DeleteGlobalRef( env, p_sys->circular.u.p_shortarray );
-            p_sys->circular.u.p_shortarray = NULL;
-        }
         break;
     case WRITE_FLOATARRAY:
         if( p_sys->circular.u.p_floatarray )
-        {
             (*env)->DeleteGlobalRef( env, p_sys->circular.u.p_floatarray );
-            p_sys->circular.u.p_floatarray = NULL;
-        }
         break;
     case WRITE_BYTEBUFFER:
         free( p_sys->circular.u.bytebuffer.p_data );
-        p_sys->circular.u.bytebuffer.p_data = NULL;
         break;
     }
 
-    p_sys->b_audiotrack_exception = false;
-    p_sys->b_error = false;
-    p_sys->b_passthrough = false;
+    free( p_sys );
 }
 
 /**
@@ -1665,7 +1611,7 @@ Stop( audio_output_t *p_aout )
  * that we won't wait in AudioTrack.write() method.
  */
 static int
-AudioTrack_WriteByteArray( JNIEnv *env, audio_output_t *p_aout,
+AudioTrack_WriteByteArray( JNIEnv *env, aout_stream_t *p_aout,
                            size_t i_data_size, size_t i_data_offset,
                            bool b_force )
 {
@@ -1709,7 +1655,7 @@ AudioTrack_WriteByteArray( JNIEnv *env, audio_output_t *p_aout,
  * flags.
  */
 static int
-AudioTrack_WriteByteArrayV23( JNIEnv *env, audio_output_t *p_aout,
+AudioTrack_WriteByteArrayV23( JNIEnv *env, aout_stream_t *p_aout,
                               size_t i_data_size, size_t i_data_offset )
 {
     aout_sys_t *p_sys = p_aout->sys;
@@ -1725,7 +1671,7 @@ AudioTrack_WriteByteArrayV23( JNIEnv *env, audio_output_t *p_aout,
  * flags.
  */
 static int
-AudioTrack_WriteByteBuffer( JNIEnv *env, audio_output_t *p_aout,
+AudioTrack_WriteByteBuffer( JNIEnv *env, aout_stream_t *p_aout,
                             size_t i_data_size, size_t i_data_offset )
 {
     aout_sys_t *p_sys = p_aout->sys;
@@ -1762,7 +1708,7 @@ AudioTrack_WriteByteBuffer( JNIEnv *env, audio_output_t *p_aout,
  * flags.
  */
 static int
-AudioTrack_WriteShortArrayV23( JNIEnv *env, audio_output_t *p_aout,
+AudioTrack_WriteShortArrayV23( JNIEnv *env, aout_stream_t *p_aout,
                                size_t i_data_size, size_t i_data_offset )
 {
     aout_sys_t *p_sys = p_aout->sys;
@@ -1783,7 +1729,7 @@ AudioTrack_WriteShortArrayV23( JNIEnv *env, audio_output_t *p_aout,
  * flags.
  */
 static int
-AudioTrack_WriteFloatArray( JNIEnv *env, audio_output_t *p_aout,
+AudioTrack_WriteFloatArray( JNIEnv *env, aout_stream_t *p_aout,
                             size_t i_data_size, size_t i_data_offset )
 {
     aout_sys_t *p_sys = p_aout->sys;
@@ -1799,7 +1745,7 @@ AudioTrack_WriteFloatArray( JNIEnv *env, audio_output_t *p_aout,
 }
 
 static int
-AudioTrack_Write( JNIEnv *env, audio_output_t *p_aout, size_t i_data_size,
+AudioTrack_Write( JNIEnv *env, aout_stream_t *p_aout, size_t i_data_size,
                   size_t i_data_offset, bool b_force )
 {
     aout_sys_t *p_sys = p_aout->sys;
@@ -1866,7 +1812,7 @@ AudioTrack_Write( JNIEnv *env, audio_output_t *p_aout, size_t i_data_size,
 static void *
 AudioTrack_Thread( void *p_data )
 {
-    audio_output_t *p_aout = p_data;
+    aout_stream_t *p_aout = p_data;
     aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env = GET_ENV();
     vlc_tick_t i_last_time_blocked = 0;
@@ -1959,7 +1905,7 @@ AudioTrack_Thread( void *p_data )
 }
 
 static int
-ConvertFromIEC61937( audio_output_t *p_aout, block_t *p_buffer )
+ConvertFromIEC61937( aout_stream_t *p_aout, block_t *p_buffer )
 {
     /* This function is only used for Android API 23 when AudioTrack is
      * configured with ENCODING_ AC3/E_AC3/DTS. In that case, only the codec
@@ -2009,7 +1955,7 @@ ConvertFromIEC61937( audio_output_t *p_aout, block_t *p_buffer )
 }
 
 static void
-Play( audio_output_t *p_aout, block_t *p_buffer, vlc_tick_t i_date )
+Play( aout_stream_t *p_aout, block_t *p_buffer, vlc_tick_t i_date )
 {
     JNIEnv *env = NULL;
     size_t i_buffer_offset = 0;
@@ -2095,7 +2041,7 @@ bailout:
 }
 
 static void
-Pause( audio_output_t *p_aout, bool b_pause, vlc_tick_t i_date )
+Pause( aout_stream_t *p_aout, bool b_pause, vlc_tick_t i_date )
 {
     aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env;
@@ -2124,7 +2070,7 @@ bailout:
 }
 
 static void
-Flush( audio_output_t *p_aout )
+Flush( aout_stream_t *p_aout )
 {
     aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env;
@@ -2174,7 +2120,7 @@ bailout:
 }
 
 static void
-AudioTrack_SetVolume( JNIEnv *env, audio_output_t *p_aout, float volume )
+AudioTrack_SetVolume( JNIEnv *env, aout_stream_t *p_aout, float volume )
 {
     aout_sys_t *p_sys = p_aout->sys;
 
@@ -2189,8 +2135,8 @@ AudioTrack_SetVolume( JNIEnv *env, audio_output_t *p_aout, float volume )
     }
 }
 
-static int
-VolumeSet( audio_output_t *p_aout, float volume )
+static void
+VolumeSet( aout_stream_t *p_aout, float volume )
 {
     aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env;
@@ -2232,14 +2178,11 @@ VolumeSet( audio_output_t *p_aout, float volume )
             }
         }
     }
-
-    aout_VolumeReport(p_aout, volume);
-    aout_GainRequest(p_aout, gain);
-    return 0;
+    aout_stream_GainRequest(p_aout, gain);
 }
 
-static int
-MuteSet( audio_output_t *p_aout, bool mute )
+static void
+MuteSet( aout_stream_t *p_aout, bool mute )
 {
     aout_sys_t *p_sys = p_aout->sys;
     JNIEnv *env;
@@ -2247,107 +2190,4 @@ MuteSet( audio_output_t *p_aout, bool mute )
 
     if( !p_sys->b_error && p_sys->p_audiotrack != NULL && ( env = GET_ENV() ) )
         AudioTrack_SetVolume( env, p_aout, mute ? 0.0f : p_sys->volume );
-
-    aout_MuteReport(p_aout, mute);
-    return 0;
-}
-
-static int DeviceSelect(audio_output_t *p_aout, const char *p_id)
-{
-    aout_sys_t *p_sys = p_aout->sys;
-    enum at_dev at_dev = AT_DEV_DEFAULT;
-
-    if( p_id )
-    {
-        for( unsigned int i = 0; at_devs[i].id; ++i )
-        {
-            if( strncmp( p_id, at_devs[i].id, strlen( at_devs[i].id ) ) == 0 )
-            {
-                at_dev = at_devs[i].at_dev;
-                break;
-            }
-        }
-    }
-
-    long long i_encoding_flags = 0;
-    if( at_dev == AT_DEV_ENCODED )
-    {
-        const size_t i_prefix_size = strlen( "encoded:" );
-        if( strncmp( p_id, "encoded:", i_prefix_size ) == 0 )
-            i_encoding_flags = atoll( p_id + i_prefix_size );
-    }
-
-    if( at_dev != p_sys->at_dev || i_encoding_flags != p_sys->i_encoding_flags )
-    {
-        p_sys->at_dev = at_dev;
-        p_sys->i_encoding_flags = i_encoding_flags;
-        aout_RestartRequest( p_aout, AOUT_RESTART_OUTPUT );
-        msg_Dbg( p_aout, "selected device: %s", p_id );
-
-        if( at_dev == AT_DEV_ENCODED )
-        {
-            static const vlc_fourcc_t enc_fourccs[] = {
-                VLC_CODEC_DTS, VLC_CODEC_DTSHD, VLC_CODEC_A52, VLC_CODEC_EAC3,
-                VLC_CODEC_TRUEHD,
-            };
-            for( size_t i = 0;
-                 i < sizeof( enc_fourccs ) / sizeof( enc_fourccs[0] ); ++i )
-            {
-                if( AudioTrack_HasEncoding( p_aout, enc_fourccs[i] ) )
-                    msg_Dbg( p_aout, "device has %4.4s passthrough support",
-                             (const char *)&enc_fourccs[i] );
-            }
-        }
-    }
-    aout_DeviceReport( p_aout, p_id );
-    return VLC_SUCCESS;
-}
-
-static int
-Open( vlc_object_t *obj )
-{
-    audio_output_t *p_aout = (audio_output_t *) obj;
-    aout_sys_t *p_sys;
-    JNIEnv *env = GET_ENV();
-
-    if( !env || !InitJNIFields( p_aout, env ) )
-        return VLC_EGENERIC;
-
-    p_sys = calloc( 1, sizeof (aout_sys_t) );
-
-    if( unlikely( p_sys == NULL ) )
-        return VLC_ENOMEM;
-
-    p_sys->at_dev = AT_DEV_DEFAULT;
-    vlc_mutex_init(&p_sys->lock);
-    vlc_cond_init(&p_sys->aout_cond);
-    vlc_cond_init(&p_sys->thread_cond);
-
-    p_aout->sys = p_sys;
-    p_aout->start = Start;
-    p_aout->stop = Stop;
-    p_aout->play = Play;
-    p_aout->pause = Pause;
-    p_aout->flush = Flush;
-    p_aout->time_get = TimeGet;
-    p_aout->device_select = DeviceSelect;
-
-    for( unsigned int i = 0; at_devs[i].id; ++i )
-        aout_HotplugReport(p_aout, at_devs[i].id, at_devs[i].name);
-
-    p_aout->volume_set = VolumeSet;
-    p_aout->mute_set = MuteSet;
-    p_sys->volume = 1.0f;
-    p_sys->mute = false;
-
-    return VLC_SUCCESS;
-}
-
-static void
-Close( vlc_object_t *obj )
-{
-    audio_output_t *p_aout = (audio_output_t *) obj;
-    aout_sys_t *p_sys = p_aout->sys;
-
-    free( p_sys );
 }
diff --git a/modules/audio_output/android/device.c b/modules/audio_output/android/device.c
new file mode 100644
index 00000000000..05bd81e003b
--- /dev/null
+++ b/modules/audio_output/android/device.c
@@ -0,0 +1,301 @@
+/*****************************************************************************
+ * android/device.c: Android AudioTrack/AAudio device handler
+ *****************************************************************************
+ * Copyright © 2012-2020 VLC authors and VideoLAN, VideoLabs
+ *
+ * Authors: Thomas Guillem <thomas at gllm.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_modules.h>
+#include <vlc_aout.h>
+#include "device.h"
+
+/* There is an undefined behavior when configuring AudioTrack with SPDIF or
+ * more than 2 channels when there is no HDMI out. It may succeed and the
+ * Android ressampler will be used to downmix to stereo. It may fails cleanly,
+ * and this module will be able to recover and fallback to stereo. Finally, in
+ * some rare cases, it may crash during init or while ressampling. Because of
+ * the last case we don't try up to 8 channels and we use
+ * ANDROID_AUDIO_DEVICE_STEREO device per default */
+#define ANDROID_AUDIO_DEVICE_DEFAULT ANDROID_AUDIO_DEVICE_STEREO
+
+static const struct {
+    const char *id;
+    const char *name;
+    enum android_audio_device_type adev;
+} adevs[] = {
+    { "stereo", "Up to 2 channels (compat mode).", ANDROID_AUDIO_DEVICE_STEREO },
+    { "pcm", "Up to 8 channels.", ANDROID_AUDIO_DEVICE_PCM },
+
+    /* With "encoded", the module will try to play every audio codecs via
+     * passthrough.
+     *
+     * With "encoded:ENCODING_FLAGS_MASK", the module will try to play only
+     * codecs specified by ENCODING_FLAGS_MASK. This extra value is a long long
+     * that contains binary-shifted AudioFormat.ENCODING_* values. */
+    { "encoded", "Up to 8 channels, passthrough if available.", ANDROID_AUDIO_DEVICE_ENCODED },
+    {  NULL, NULL, ANDROID_AUDIO_DEVICE_DEFAULT },
+};
+
+struct sys {
+    aout_stream_t *stream;
+
+    enum android_audio_device_type adev;
+    long long encoding_flags;
+
+    bool mute;
+    float volume;
+};
+
+static int
+Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
+{
+    struct sys *sys = aout->sys;
+
+    if (!AudioTrack_HasEncoding(sys->encoding_flags, fmt->i_format))
+        return VLC_EGENERIC;
+
+    aout_stream_t *s = vlc_object_create(aout, sizeof (*s));
+    if (unlikely(s == NULL))
+        return VLC_EGENERIC;
+    s->owner = aout;
+
+    char *modlist = var_InheritString(aout, "android-audio-backend");
+    module_t **mods;
+    ssize_t total = vlc_module_match("aout stream", modlist, false, &mods, NULL);
+    for (ssize_t i = 0; i < total; i++)
+    {
+        aout_stream_start start = vlc_module_map(vlc_object_logger(aout), mods[i]);
+        if (start == NULL)
+            continue;
+        int ret = start(s, fmt, sys->adev);
+        if (ret == VLC_SUCCESS)
+        {
+            sys->stream = s;
+
+            assert(s->stop != NULL && s->time_get != NULL && s->play != NULL &&
+                   s->pause != NULL && s->flush != NULL);
+
+            if (s->volume_set != NULL)
+                s->volume_set(s, sys->volume);
+            if (s->mute_set != NULL && sys->mute)
+                s->mute_set(s, true);
+            break;
+        }
+    }
+
+    free(modlist);
+    free(mods);
+
+    return sys->stream != NULL ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static void
+Stop(audio_output_t *aout)
+{
+    struct sys *sys = aout->sys;
+    assert(sys->stream != NULL);
+
+    sys->stream->stop(sys->stream);
+
+    vlc_object_delete(sys->stream);
+    sys->stream = NULL;
+}
+
+static int
+TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
+{
+    struct sys *sys = aout->sys;
+    assert(sys->stream != NULL);
+
+    return sys->stream->time_get(sys->stream, delay);
+}
+
+static void
+Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
+{
+    struct sys *sys = aout->sys;
+    assert(sys->stream != NULL);
+
+    return sys->stream->play(sys->stream, block, date);
+}
+
+static void
+Pause(audio_output_t *aout, bool paused, vlc_tick_t date)
+{
+    struct sys *sys = aout->sys;
+    assert(sys->stream != NULL);
+
+    return sys->stream->pause(sys->stream, paused, date);
+}
+
+static void
+Flush(audio_output_t *aout)
+{
+    struct sys *sys = aout->sys;
+    assert(sys->stream != NULL);
+
+    return sys->stream->flush(sys->stream);
+}
+
+static int
+VolumeSet(audio_output_t *aout, float vol)
+{
+    struct sys *sys = aout->sys;
+
+    sys->volume = vol;
+    if (sys->stream != NULL && sys->stream->volume_set != NULL)
+        sys->stream->volume_set(sys->stream, vol);
+
+    aout_VolumeReport(aout, vol);
+    return 0;
+}
+
+static int
+MuteSet(audio_output_t *aout, bool mute)
+{
+    struct sys *sys = aout->sys;
+
+    sys->mute = mute;
+    if (sys->stream != NULL && sys->stream->mute_set != NULL)
+        sys->stream->mute_set(sys->stream, mute);
+
+    aout_MuteReport(aout, mute);
+    return 0;
+}
+
+void
+aout_stream_GainRequest(aout_stream_t *s, float gain)
+{
+    aout_GainRequest(s->owner, gain);
+}
+
+static int DeviceSelect(audio_output_t *aout, const char *id)
+{
+    struct sys *sys = aout->sys;
+    enum android_audio_device_type adev = ANDROID_AUDIO_DEVICE_DEFAULT;
+
+    if (id)
+    {
+        for (unsigned int i = 0; adevs[i].id; ++i)
+        {
+            if (strncmp(id, adevs[i].id, strlen(adevs[i].id))== 0)
+            {
+                adev = adevs[i].adev;
+                break;
+            }
+        }
+    }
+
+    long long encoding_flags = 0;
+    if (adev == ANDROID_AUDIO_DEVICE_ENCODED)
+    {
+        const size_t prefix_size = strlen("encoded:");
+        if (strncmp(id, "encoded:", prefix_size)== 0)
+            encoding_flags = atoll(id + prefix_size);
+    }
+
+    if (adev != sys->adev || encoding_flags != sys->encoding_flags)
+    {
+        sys->adev = adev;
+        sys->encoding_flags = encoding_flags;
+        aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
+        msg_Dbg(aout, "selected device: %s", id);
+
+        if (adev == ANDROID_AUDIO_DEVICE_ENCODED)
+        {
+            static const vlc_fourcc_t enc_fourccs[] = {
+                VLC_CODEC_DTS, VLC_CODEC_DTSHD, VLC_CODEC_A52, VLC_CODEC_EAC3,
+                VLC_CODEC_TRUEHD,
+            };
+            for (size_t i = 0;
+                 i < sizeof(enc_fourccs)/ sizeof(enc_fourccs[0]); ++i)
+            {
+                if (AudioTrack_HasEncoding(sys->encoding_flags, enc_fourccs[i]))
+                    msg_Dbg(aout, "device has %4.4s passthrough support",
+                             (const char *)&enc_fourccs[i]);
+            }
+        }
+    }
+    aout_DeviceReport(aout, id);
+    return VLC_SUCCESS;
+}
+
+static int
+Open(vlc_object_t *obj)
+{
+    audio_output_t *aout = (audio_output_t *)obj;
+
+    int ret = AudioTrack_InitJNI(aout);
+    if (ret != VLC_SUCCESS)
+        return ret;
+
+    struct sys *sys = aout->sys = vlc_obj_malloc(obj, sizeof(*sys));
+    if (sys == NULL)
+        return VLC_ENOMEM;
+
+    sys->adev = ANDROID_AUDIO_DEVICE_DEFAULT;
+    sys->encoding_flags = 0;
+    sys->volume = 1.f;
+    sys->mute = false;
+
+    aout->start = Start;
+    aout->stop = Stop;
+    aout->play = Play;
+    aout->pause = Pause;
+    aout->flush = Flush;
+    aout->time_get = TimeGet;
+    aout->device_select = DeviceSelect;
+    aout->volume_set = VolumeSet;
+    aout->mute_set = MuteSet;
+
+    for (unsigned int i = 0; adevs[i].id; ++i)
+        aout_HotplugReport(aout, adevs[i].id, adevs[i].name);
+
+    if (var_InheritBool(aout, "spdif"))
+        DeviceSelect(aout, "encoded");
+
+    return VLC_SUCCESS;
+}
+
+#define AUDIOTRACK_SESSION_ID_TEXT " Id of audio session the AudioTrack must be attached to"
+
+vlc_module_begin ()
+    set_shortname("Android Audio")
+    set_description("Android audio output")
+    set_capability("audio output", 200)
+    set_category(CAT_AUDIO)
+    set_subcategory(SUBCAT_AUDIO_AOUT)
+    add_integer("audiotrack-session-id", 0,
+            AUDIOTRACK_SESSION_ID_TEXT, NULL, true)
+        change_private()
+    add_module("android-audio-backend", "aout stream", "any",
+               N_("Output back-end"), N_("Audio output back-end interface."))
+    set_callback(Open)
+
+    add_submodule()
+        set_capability("aout stream", 180)
+        set_callback(AudioTrack_Start)
+        add_shortcut("audiotrack")
+        add_shortcut("android_audiotrack")
+vlc_module_end ()
diff --git a/modules/audio_output/android/device.h b/modules/audio_output/android/device.h
new file mode 100644
index 00000000000..740e259886c
--- /dev/null
+++ b/modules/audio_output/android/device.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+ * android/device.h: Android AudioTrack/AAudio device handler
+ *****************************************************************************
+ * Copyright © 2012-2020 VLC authors and VideoLAN, VideoLabs
+ *
+ * Authors: Thomas Guillem <thomas at gllm.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+enum android_audio_device_type
+{
+    ANDROID_AUDIO_DEVICE_STEREO = 0,
+    ANDROID_AUDIO_DEVICE_PCM,
+    ANDROID_AUDIO_DEVICE_ENCODED,
+};
+#define ANDROID_AUDIO_DEVICE_MAX_CHANNELS 8
+
+int
+AudioTrack_InitJNI(audio_output_t *aout);
+
+bool
+AudioTrack_HasEncoding(long long encoding_flags, vlc_fourcc_t format);
+
+typedef struct aout_stream aout_stream_t;
+
+struct aout_stream
+{
+    struct vlc_object_t obj;
+    void *sys;
+
+    void (*stop)(aout_stream_t *);
+    int (*time_get)(aout_stream_t *, vlc_tick_t *);
+    void (*play)(aout_stream_t *, block_t *, vlc_tick_t);
+    void (*pause)(aout_stream_t *, bool, vlc_tick_t);
+    void (*flush)(aout_stream_t *);
+    void (*volume_set)(aout_stream_t *, float volume);
+    void (*mute_set)(aout_stream_t *, bool mute);
+
+    void *owner;
+};
+
+void
+aout_stream_GainRequest(aout_stream_t *s, float gain);
+
+typedef int (*aout_stream_start)(aout_stream_t *s, audio_sample_format_t *fmt,
+                                 enum android_audio_device_type dev);
+
+int
+AudioTrack_Start(aout_stream_t *, audio_sample_format_t *,
+                 enum android_audio_device_type);
-- 
2.28.0




More information about the vlc-devel mailing list