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

Steve Lhomme robux4 at ycbcr.xyz
Thu Dec 3 10:24:55 CET 2020


IMO if you want to use the same "aout stream" object type, the type 
should be shared in a header (with mmdevice.h). While the code will 
never be used at the same time (until Microsoft brings Windows Subsystem 
for Android) it doesn't feel right.

So you should have a patch before this one to move the common parts with 
mmdevice.h in a header and then this patch (minus the duplicated code).

The rest LGTM.

On 2020-12-02 15:44, Thomas Guillem wrote:
> 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
> 
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
> 




More information about the vlc-devel mailing list