audiotrack.c 66.4 KB
Newer Older
Thomas Guillem's avatar
Thomas Guillem committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*****************************************************************************
 * audiotrack.c: Android Java AudioTrack audio output module
 *****************************************************************************
 * Copyright © 2012-2015 VLC authors and VideoLAN, VideoLabs
 *
 * Authors: Thomas Guillem <thomas@gllm.fr>
 *          Ming Hu <tewilove@gmail.com>
 *
 * 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 <assert.h>
#include <jni.h>
#include <dlfcn.h>
31
#include <stdbool.h>
Thomas Guillem's avatar
Thomas Guillem committed
32 33 34 35

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
36
#include "../video_output/android/utils.h"
37

38 39 40
#define SMOOTHPOS_SAMPLE_COUNT 10
#define SMOOTHPOS_INTERVAL_US INT64_C(30000) // 30ms

41 42
#define AUDIOTIMESTAMP_INTERVAL_US INT64_C(500000) // 500ms

Thomas Guillem's avatar
Thomas Guillem committed
43 44
static int  Open( vlc_object_t * );
static void Close( vlc_object_t * );
45 46
static void Stop( audio_output_t * );
static int Start( audio_output_t *, audio_sample_format_t * );
47
static void *AudioTrack_Thread( void * );
48

49 50 51 52 53 54 55 56 57
/* 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,
58
    AT_DEV_PCM,
59
    AT_DEV_ENCODED,
60 61 62 63 64 65 66 67 68 69
};
#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 },
70
    { "pcm", "Up to 8 channels.", AT_DEV_PCM },
71 72 73 74 75 76 77

    /* 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. */
78
    { "encoded", "Up to 8 channels, passthrough if available.", AT_DEV_ENCODED },
79 80 81
    {  NULL, NULL, AT_DEV_DEFAULT },
};

Thomas Guillem's avatar
Thomas Guillem committed
82 83 84 85 86
struct aout_sys_t {
    /* sw gain */
    float soft_gain;
    bool soft_mute;

87 88
    enum at_dev at_dev;

Thomas Guillem's avatar
Thomas Guillem committed
89
    jobject p_audiotrack; /* AudioTrack ref */
90

Thomas Guillem's avatar
Thomas Guillem committed
91
    audio_sample_format_t fmt; /* fmt setup by Start */
92

93 94 95 96 97 98 99
    struct {
        unsigned int i_rate;
        int i_channel_config;
        int i_format;
        int i_size;
    } audiotrack_args;

100
    /* Used by AudioTrack_getPlaybackHeadPosition */
101 102 103 104 105
    struct {
        uint32_t i_wrap_count;
        uint32_t i_last;
    } headpos;

106 107 108 109 110 111 112 113 114
    /* Used by AudioTrack_GetTimestampPositionUs */
    struct {
        jobject p_obj; /* AudioTimestamp ref */
        jlong i_frame_us;
        jlong i_frame_pos;
        mtime_t i_play_time; /* time when play was called */
        mtime_t i_last_time;
    } timestamp;

115 116 117 118 119 120 121
    /* Used by AudioTrack_GetSmoothPositionUs */
    struct {
        uint32_t i_idx;
        uint32_t i_count;
        mtime_t p_us[SMOOTHPOS_SAMPLE_COUNT];
        mtime_t i_us;
        mtime_t i_last_time;
122
        mtime_t i_latency_us;
123 124
    } smoothpos;

125
    uint32_t i_max_audiotrack_samples;
126
    long long i_encoding_flags;
127
    bool b_passthrough;
128 129
    uint8_t i_chans_to_reorder; /* do we need channel reordering */
    uint8_t p_chan_table[AOUT_CHAN_MAX];
130

Thomas Guillem's avatar
Thomas Guillem committed
131
    enum {
132
        WRITE_BYTEARRAY,
133
        WRITE_BYTEARRAYV23,
134
        WRITE_SHORTARRAYV23,
135 136
        WRITE_BYTEBUFFER,
        WRITE_FLOATARRAY
Thomas Guillem's avatar
Thomas Guillem committed
137
    } i_write_type;
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

    vlc_thread_t thread;    /* AudioTrack_Thread */
    vlc_mutex_t lock;
    vlc_cond_t aout_cond;   /* cond owned by aout */
    vlc_cond_t thread_cond; /* cond owned by AudioTrack_Thread */

    /* These variables need locking on read and write */
    bool b_thread_running;  /* Set to false by aout to stop the thread */
    bool b_thread_paused;   /* If true, the thread won't process any data, see
                             * Pause() */
    bool b_thread_waiting;  /* If true, the thread is waiting for enough spaces
                             * in AudioTrack internal buffers */

    uint64_t i_samples_written; /* Number of samples written since last flush */
    bool b_audiotrack_exception; /* True if audiotrack threw an exception */
    bool b_error; /* generic error */

    struct {
        uint64_t i_read;    /* Number of bytes read */
        uint64_t i_write;   /* Number of bytes written */
        size_t i_size;      /* Size of the circular buffer in bytes */
        union {
            jbyteArray p_bytearray;
            jfloatArray p_floatarray;
162
            jshortArray p_shortarray;
163 164 165 166 167 168
            struct {
                uint8_t *p_data;
                jobject p_obj;
            } bytebuffer;
        } u;
    } circular;
Thomas Guillem's avatar
Thomas Guillem committed
169 170 171 172 173
};

/* Soft volume helper */
#include "audio_output/volume.h"

174 175
// Don't use Float for now since 5.1/7.1 Float is down sampled to Stereo Float
//#define AUDIOTRACK_USE_FLOAT
176
//#define AUDIOTRACK_HW_LATENCY
Thomas Guillem's avatar
Thomas Guillem committed
177

178 179
/* Get AudioTrack native sample rate: if activated, most of  the resampling
 * will be done by VLC */
180
#define AUDIOTRACK_NATIVE_SAMPLERATE
181

182 183
#define AUDIOTRACK_SESSION_ID_TEXT " Id of audio session the AudioTrack must be attached to"

Thomas Guillem's avatar
Thomas Guillem committed
184 185
vlc_module_begin ()
    set_shortname( "AudioTrack" )
186
    set_description( "Android AudioTrack audio output" )
Thomas Guillem's avatar
Thomas Guillem committed
187 188 189
    set_capability( "audio output", 180 )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
190 191 192
    add_integer( "audiotrack-session-id", 0,
            AUDIOTRACK_SESSION_ID_TEXT, NULL, true )
        change_private()
Thomas Guillem's avatar
Thomas Guillem committed
193 194 195 196 197 198
    add_sw_gain()
    add_shortcut( "audiotrack" )
    set_callbacks( Open, Close )
vlc_module_end ()

#define THREAD_NAME "android_audiotrack"
199
#define GET_ENV() android_getEnv( VLC_OBJECT(p_aout), THREAD_NAME )
Thomas Guillem's avatar
Thomas Guillem committed
200 201 202 203 204 205 206

static struct
{
    struct {
        jclass clazz;
        jmethodID ctor;
        jmethodID release;
207
        jmethodID getState;
Thomas Guillem's avatar
Thomas Guillem committed
208 209 210 211 212
        jmethodID play;
        jmethodID stop;
        jmethodID flush;
        jmethodID pause;
        jmethodID write;
213
        jmethodID writeV23;
214
        jmethodID writeShortV23;
215
        jmethodID writeBufferV21;
216
        jmethodID writeFloat;
Thomas Guillem's avatar
Thomas Guillem committed
217
        jmethodID getPlaybackHeadPosition;
218
        jmethodID getTimestamp;
Thomas Guillem's avatar
Thomas Guillem committed
219
        jmethodID getMinBufferSize;
220
        jmethodID getNativeOutputSampleRate;
221
        jint STATE_INITIALIZED;
Thomas Guillem's avatar
Thomas Guillem committed
222 223 224 225
        jint MODE_STREAM;
        jint ERROR;
        jint ERROR_BAD_VALUE;
        jint ERROR_INVALID_OPERATION;
226
        jint WRITE_NON_BLOCKING;
Thomas Guillem's avatar
Thomas Guillem committed
227 228 229 230 231 232
    } AudioTrack;
    struct {
        jint ENCODING_PCM_8BIT;
        jint ENCODING_PCM_16BIT;
        jint ENCODING_PCM_FLOAT;
        bool has_ENCODING_PCM_FLOAT;
233 234
        jint ENCODING_AC3;
        bool has_ENCODING_AC3;
235 236
        jint ENCODING_E_AC3;
        bool has_ENCODING_E_AC3;
237 238
        jint ENCODING_DOLBY_TRUEHD;
        bool has_ENCODING_DOLBY_TRUEHD;
239
        jint ENCODING_DTS;
240
        bool has_ENCODING_DTS;
241 242
        jint ENCODING_DTS_HD;
        bool has_ENCODING_DTS_HD;
243 244
        jint ENCODING_IEC61937;
        bool has_ENCODING_IEC61937;
Thomas Guillem's avatar
Thomas Guillem committed
245 246
        jint CHANNEL_OUT_MONO;
        jint CHANNEL_OUT_STEREO;
247 248
        jint CHANNEL_OUT_FRONT_LEFT;
        jint CHANNEL_OUT_FRONT_RIGHT;
249 250 251 252 253 254 255 256 257
        jint CHANNEL_OUT_BACK_LEFT;
        jint CHANNEL_OUT_BACK_RIGHT;
        jint CHANNEL_OUT_FRONT_CENTER;
        jint CHANNEL_OUT_LOW_FREQUENCY;
        jint CHANNEL_OUT_BACK_CENTER;
        jint CHANNEL_OUT_5POINT1;
        jint CHANNEL_OUT_SIDE_LEFT;
        jint CHANNEL_OUT_SIDE_RIGHT;
        bool has_CHANNEL_OUT_SIDE;
Thomas Guillem's avatar
Thomas Guillem committed
258 259 260 261 262 263
    } AudioFormat;
    struct {
        jint ERROR_DEAD_OBJECT;
        bool has_ERROR_DEAD_OBJECT;
        jint STREAM_MUSIC;
    } AudioManager;
264 265 266 267
    struct {
        jclass clazz;
        jmethodID getOutputLatency;
    } AudioSystem;
268 269 270 271 272 273
    struct {
        jclass clazz;
        jmethodID ctor;
        jfieldID framePosition;
        jfieldID nanoTime;
    } AudioTimestamp;
Thomas Guillem's avatar
Thomas Guillem committed
274 275 276 277 278
} jfields;

/* init all jni fields.
 * Done only one time during the first initialisation */
static bool
279
InitJNIFields( audio_output_t *p_aout, JNIEnv* env )
Thomas Guillem's avatar
Thomas Guillem committed
280 281 282
{
    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
    static int i_init_state = -1;
283
    bool ret;
Thomas Guillem's avatar
Thomas Guillem committed
284 285 286 287 288 289 290 291 292
    jclass clazz;
    jfieldID field;

    vlc_mutex_lock( &lock );

    if( i_init_state != -1 )
        goto end;

#define CHECK_EXCEPTION( what, critical ) do { \
293
    if( (*env)->ExceptionCheck( env ) ) \
Thomas Guillem's avatar
Thomas Guillem committed
294 295 296 297 298 299 300 301 302 303 304 305
    { \
        msg_Err( p_aout, "%s failed", what ); \
        (*env)->ExceptionClear( env ); \
        if( (critical) ) \
        { \
            i_init_state = 0; \
            goto end; \
        } \
    } \
} while( 0 )
#define GET_CLASS( str, critical ) do { \
    clazz = (*env)->FindClass( env, (str) ); \
306
    CHECK_EXCEPTION( "FindClass(" str ")", critical ); \
Thomas Guillem's avatar
Thomas Guillem committed
307 308 309
} while( 0 )
#define GET_ID( get, id, str, args, critical ) do { \
    jfields.id = (*env)->get( env, clazz, (str), (args) ); \
310
    CHECK_EXCEPTION( #get "(" #id ")", critical ); \
Thomas Guillem's avatar
Thomas Guillem committed
311 312 313 314
} while( 0 )
#define GET_CONST_INT( id, str, critical ) do { \
    field = NULL; \
    field = (*env)->GetStaticFieldID( env, clazz, (str), "I" ); \
315
    CHECK_EXCEPTION( "GetStaticFieldID(" #id ")", critical ); \
Thomas Guillem's avatar
Thomas Guillem committed
316 317 318 319 320 321 322
    if( field ) \
    { \
        jfields.id = (*env)->GetStaticIntField( env, clazz, field ); \
        CHECK_EXCEPTION( #id, critical ); \
    } \
} while( 0 )

323
    /* AudioTrack class init */
Thomas Guillem's avatar
Thomas Guillem committed
324 325 326 327
    GET_CLASS( "android/media/AudioTrack", true );
    jfields.AudioTrack.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );
    CHECK_EXCEPTION( "NewGlobalRef", true );

328
    GET_ID( GetMethodID, AudioTrack.ctor, "<init>", "(IIIIIII)V", true );
Thomas Guillem's avatar
Thomas Guillem committed
329
    GET_ID( GetMethodID, AudioTrack.release, "release", "()V", true );
330
    GET_ID( GetMethodID, AudioTrack.getState, "getState", "()I", true );
Thomas Guillem's avatar
Thomas Guillem committed
331 332 333 334
    GET_ID( GetMethodID, AudioTrack.play, "play", "()V", true );
    GET_ID( GetMethodID, AudioTrack.stop, "stop", "()V", true );
    GET_ID( GetMethodID, AudioTrack.flush, "flush", "()V", true );
    GET_ID( GetMethodID, AudioTrack.pause, "pause", "()V", true );
335

336
    GET_ID( GetMethodID, AudioTrack.writeV23, "write", "([BIII)I", false );
337
    GET_ID( GetMethodID, AudioTrack.writeShortV23, "write", "([SIII)I", false );
338 339 340 341
    if( !jfields.AudioTrack.writeV23 )
        GET_ID( GetMethodID, AudioTrack.writeBufferV21, "write", "(Ljava/nio/ByteBuffer;II)I", false );

    if( jfields.AudioTrack.writeV23 || jfields.AudioTrack.writeBufferV21 )
342 343
    {
        GET_CONST_INT( AudioTrack.WRITE_NON_BLOCKING, "WRITE_NON_BLOCKING", true );
344 345 346
#ifdef AUDIOTRACK_USE_FLOAT
        GET_ID( GetMethodID, AudioTrack.writeFloat, "write", "([FIII)I", true );
#endif
347 348
    } else
        GET_ID( GetMethodID, AudioTrack.write, "write", "([BII)I", true );
349 350 351

    GET_ID( GetMethodID, AudioTrack.getTimestamp,
            "getTimestamp", "(Landroid/media/AudioTimestamp;)Z", false );
Thomas Guillem's avatar
Thomas Guillem committed
352 353
    GET_ID( GetMethodID, AudioTrack.getPlaybackHeadPosition,
            "getPlaybackHeadPosition", "()I", true );
354

Thomas Guillem's avatar
Thomas Guillem committed
355 356
    GET_ID( GetStaticMethodID, AudioTrack.getMinBufferSize, "getMinBufferSize",
            "(III)I", true );
357
#ifdef AUDIOTRACK_NATIVE_SAMPLERATE
358 359
    GET_ID( GetStaticMethodID, AudioTrack.getNativeOutputSampleRate,
            "getNativeOutputSampleRate",  "(I)I", true );
360
#endif
361
    GET_CONST_INT( AudioTrack.STATE_INITIALIZED, "STATE_INITIALIZED", true );
Thomas Guillem's avatar
Thomas Guillem committed
362 363 364
    GET_CONST_INT( AudioTrack.MODE_STREAM, "MODE_STREAM", true );
    GET_CONST_INT( AudioTrack.ERROR, "ERROR", true );
    GET_CONST_INT( AudioTrack.ERROR_BAD_VALUE , "ERROR_BAD_VALUE", true );
365
    GET_CONST_INT( AudioTrack.ERROR_INVALID_OPERATION,
Thomas Guillem's avatar
Thomas Guillem committed
366 367
                   "ERROR_INVALID_OPERATION", true );

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
    /* AudioTimestamp class init (if any) */
    if( jfields.AudioTrack.getTimestamp )
    {
        GET_CLASS( "android/media/AudioTimestamp", true );
        jfields.AudioTimestamp.clazz = (jclass) (*env)->NewGlobalRef( env,
                                                                      clazz );
        CHECK_EXCEPTION( "NewGlobalRef", true );

        GET_ID( GetMethodID, AudioTimestamp.ctor, "<init>", "()V", true );
        GET_ID( GetFieldID, AudioTimestamp.framePosition,
                "framePosition", "J", true );
        GET_ID( GetFieldID, AudioTimestamp.nanoTime,
                "nanoTime", "J", true );
    }

383
#ifdef AUDIOTRACK_HW_LATENCY
384 385 386 387 388 389 390 391
    /* AudioSystem class init */
    GET_CLASS( "android/media/AudioSystem", false );
    if( clazz )
    {
        jfields.AudioSystem.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );
        GET_ID( GetStaticMethodID, AudioSystem.getOutputLatency,
                "getOutputLatency", "(I)I", false );
    }
392
#endif
393

394
    /* AudioFormat class init */
Thomas Guillem's avatar
Thomas Guillem committed
395 396 397 398 399 400
    GET_CLASS( "android/media/AudioFormat", true );
    GET_CONST_INT( AudioFormat.ENCODING_PCM_8BIT, "ENCODING_PCM_8BIT", true );
    GET_CONST_INT( AudioFormat.ENCODING_PCM_16BIT, "ENCODING_PCM_16BIT", true );
#ifdef AUDIOTRACK_USE_FLOAT
    GET_CONST_INT( AudioFormat.ENCODING_PCM_FLOAT, "ENCODING_PCM_FLOAT",
                   false );
401 402
    jfields.AudioFormat.has_ENCODING_PCM_FLOAT = field != NULL &&
                                                 jfields.AudioTrack.writeFloat;
Thomas Guillem's avatar
Thomas Guillem committed
403 404 405
#else
    jfields.AudioFormat.has_ENCODING_PCM_FLOAT = false;
#endif
406 407

    if( jfields.AudioTrack.writeShortV23 )
408
    {
409 410
        GET_CONST_INT( AudioFormat.ENCODING_IEC61937, "ENCODING_IEC61937", false );
        jfields.AudioFormat.has_ENCODING_IEC61937 = field != NULL;
411 412
    }
    else
413
        jfields.AudioFormat.has_ENCODING_IEC61937 = false;
414 415 416 417 418 419 420 421 422 423

    GET_CONST_INT( AudioFormat.ENCODING_AC3, "ENCODING_AC3", false );
    jfields.AudioFormat.has_ENCODING_AC3 = field != NULL;
    GET_CONST_INT( AudioFormat.ENCODING_E_AC3, "ENCODING_E_AC3", false );
    jfields.AudioFormat.has_ENCODING_E_AC3 = field != NULL;

    GET_CONST_INT( AudioFormat.ENCODING_DTS, "ENCODING_DTS", false );
    jfields.AudioFormat.has_ENCODING_DTS = field != NULL;
    GET_CONST_INT( AudioFormat.ENCODING_DTS_HD, "ENCODING_DTS_HD", false );
    jfields.AudioFormat.has_ENCODING_DTS_HD = field != NULL;
424

425 426 427 428
    GET_CONST_INT( AudioFormat.ENCODING_DOLBY_TRUEHD, "ENCODING_DOLBY_TRUEHD",
                   false );
    jfields.AudioFormat.has_ENCODING_DOLBY_TRUEHD = field != NULL;

Thomas Guillem's avatar
Thomas Guillem committed
429 430
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_MONO, "CHANNEL_OUT_MONO", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_STEREO, "CHANNEL_OUT_STEREO", true );
431 432
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_FRONT_LEFT, "CHANNEL_OUT_FRONT_LEFT", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_FRONT_RIGHT, "CHANNEL_OUT_FRONT_RIGHT", true );
433 434 435 436 437 438 439 440 441 442 443 444 445
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_5POINT1, "CHANNEL_OUT_5POINT1", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_BACK_LEFT, "CHANNEL_OUT_BACK_LEFT", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_BACK_RIGHT, "CHANNEL_OUT_BACK_RIGHT", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_FRONT_CENTER, "CHANNEL_OUT_FRONT_CENTER", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_LOW_FREQUENCY, "CHANNEL_OUT_LOW_FREQUENCY", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_BACK_CENTER, "CHANNEL_OUT_BACK_CENTER", true );
    GET_CONST_INT( AudioFormat.CHANNEL_OUT_SIDE_LEFT, "CHANNEL_OUT_SIDE_LEFT", false );
    if( field != NULL )
    {
        GET_CONST_INT( AudioFormat.CHANNEL_OUT_SIDE_RIGHT, "CHANNEL_OUT_SIDE_RIGHT", true );
        jfields.AudioFormat.has_CHANNEL_OUT_SIDE = true;
    } else
        jfields.AudioFormat.has_CHANNEL_OUT_SIDE = false;
Thomas Guillem's avatar
Thomas Guillem committed
446

447
    /* AudioManager class init */
Thomas Guillem's avatar
Thomas Guillem committed
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    GET_CLASS( "android/media/AudioManager", true );
    GET_CONST_INT( AudioManager.ERROR_DEAD_OBJECT, "ERROR_DEAD_OBJECT", false );
    jfields.AudioManager.has_ERROR_DEAD_OBJECT = field != NULL;
    GET_CONST_INT( AudioManager.STREAM_MUSIC, "STREAM_MUSIC", true );

#undef CHECK_EXCEPTION
#undef GET_CLASS
#undef GET_ID
#undef GET_CONST_INT

    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;
}

static inline bool
468
check_exception( JNIEnv *env, audio_output_t *p_aout,
Thomas Guillem's avatar
Thomas Guillem committed
469 470
                 const char *method )
{
471
    if( (*env)->ExceptionCheck( env ) )
Thomas Guillem's avatar
Thomas Guillem committed
472
    {
473 474 475
        aout_sys_t *p_sys = p_aout->sys;

        p_sys->b_audiotrack_exception = true;
476
        p_sys->b_error = true;
477
        (*env)->ExceptionDescribe( env );
Thomas Guillem's avatar
Thomas Guillem committed
478 479 480 481 482 483
        (*env)->ExceptionClear( env );
        msg_Err( p_aout, "AudioTrack.%s triggered an exception !", method );
        return true;
    } else
        return false;
}
484
#define CHECK_AT_EXCEPTION( method ) check_exception( env, p_aout, method )
Thomas Guillem's avatar
Thomas Guillem committed
485 486 487 488

#define JNI_CALL( what, obj, method, ... ) (*env)->what( env, obj, method, ##__VA_ARGS__ )

#define JNI_CALL_INT( obj, method, ... ) JNI_CALL( CallIntMethod, obj, method, ##__VA_ARGS__ )
489
#define JNI_CALL_BOOL( obj, method, ... ) JNI_CALL( CallBooleanMethod, obj, method, ##__VA_ARGS__ )
Thomas Guillem's avatar
Thomas Guillem committed
490 491 492 493 494
#define JNI_CALL_VOID( obj, method, ... ) JNI_CALL( CallVoidMethod, obj, method, ##__VA_ARGS__ )
#define JNI_CALL_STATIC_INT( clazz, method, ... ) JNI_CALL( CallStaticIntMethod, clazz, method, ##__VA_ARGS__ )

#define JNI_AT_NEW( ... ) JNI_CALL( NewObject, jfields.AudioTrack.clazz, jfields.AudioTrack.ctor, ##__VA_ARGS__ )
#define JNI_AT_CALL_INT( method, ... ) JNI_CALL_INT( p_sys->p_audiotrack, jfields.AudioTrack.method, ##__VA_ARGS__ )
495
#define JNI_AT_CALL_BOOL( method, ... ) JNI_CALL_BOOL( p_sys->p_audiotrack, jfields.AudioTrack.method, ##__VA_ARGS__ )
Thomas Guillem's avatar
Thomas Guillem committed
496 497 498
#define JNI_AT_CALL_VOID( method, ... ) JNI_CALL_VOID( p_sys->p_audiotrack, jfields.AudioTrack.method, ##__VA_ARGS__ )
#define JNI_AT_CALL_STATIC_INT( method, ... ) JNI_CALL( CallStaticIntMethod, jfields.AudioTrack.clazz, jfields.AudioTrack.method, ##__VA_ARGS__ )

499
#define JNI_AUDIOTIMESTAMP_GET_LONG( field ) JNI_CALL( GetLongField, p_sys->timestamp.p_obj, jfields.AudioTimestamp.field )
500 501

static inline mtime_t
502
frames_to_us( aout_sys_t *p_sys, uint64_t i_nb_frames )
Thomas Guillem's avatar
Thomas Guillem committed
503
{
504 505 506
    return  i_nb_frames * CLOCK_FREQ / p_sys->fmt.i_rate;
}
#define FRAMES_TO_US(x) frames_to_us( p_sys, (x) )
Thomas Guillem's avatar
Thomas Guillem committed
507

508
static inline uint64_t
509 510
bytes_to_frames( aout_sys_t *p_sys, size_t i_bytes )
{
511
    return i_bytes * p_sys->fmt.i_frame_length / p_sys->fmt.i_bytes_per_frame;
512 513
}
#define BYTES_TO_FRAMES(x) bytes_to_frames( p_sys, (x) )
514
#define BYTES_TO_US(x) frames_to_us( p_sys, bytes_to_frames( p_sys, (x) ) )
515 516

static inline size_t
517
frames_to_bytes( aout_sys_t *p_sys, uint64_t i_frames )
518
{
519
    return i_frames * p_sys->fmt.i_bytes_per_frame / p_sys->fmt.i_frame_length;
520 521 522
}
#define FRAMES_TO_BYTES(x) frames_to_bytes( p_sys, (x) )

523 524 525 526
/**
 * Get the AudioTrack position
 *
 * The doc says that the position is reset to zero after flush but it's not
527
 * true for all devices or Android versions.
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
 */
static uint64_t
AudioTrack_getPlaybackHeadPosition( JNIEnv *env, audio_output_t *p_aout )
{
    /* Android doc:
     * getPlaybackHeadPosition: Returns the playback head position expressed in
     * frames. Though the "int" type is signed 32-bits, the value should be
     * reinterpreted as if it is unsigned 32-bits. That is, the next position
     * after 0x7FFFFFFF is (int) 0x80000000. This is a continuously advancing
     * counter. It will wrap (overflow) periodically, for example approximately
     * once every 27:03:11 hours:minutes:seconds at 44.1 kHz. It is reset to
     * zero by flush(), reload(), and stop().
     */

    aout_sys_t *p_sys = p_aout->sys;
    uint32_t i_pos;

    /* int32_t to uint32_t */
    i_pos = 0xFFFFFFFFL & JNI_AT_CALL_INT( getPlaybackHeadPosition );

    /* uint32_t to uint64_t */
    if( p_sys->headpos.i_last > i_pos )
        p_sys->headpos.i_wrap_count++;
    p_sys->headpos.i_last = i_pos;
    return p_sys->headpos.i_last + ((uint64_t)p_sys->headpos.i_wrap_count << 32);
}

555 556 557 558 559
/**
 * Reset AudioTrack position
 *
 * Called after flush, or start
 */
560
static void
561
AudioTrack_ResetPlaybackHeadPosition( JNIEnv *env, audio_output_t *p_aout )
562
{
563
    (void) env;
564 565
    aout_sys_t *p_sys = p_aout->sys;

566 567
    p_sys->headpos.i_last = 0;
    p_sys->headpos.i_wrap_count = 0;
568 569
}

570
/**
571
 * Reset AudioTrack SmoothPosition and TimestampPosition
572 573 574 575 576 577 578 579 580 581 582
 */
static void
AudioTrack_ResetPositions( JNIEnv *env, audio_output_t *p_aout )
{
    aout_sys_t *p_sys = p_aout->sys;
    VLC_UNUSED( env );

    p_sys->timestamp.i_play_time = mdate();
    p_sys->timestamp.i_last_time = 0;
    p_sys->timestamp.i_frame_us = 0;
    p_sys->timestamp.i_frame_pos = 0;
583 584 585 586 587

    p_sys->smoothpos.i_count = 0;
    p_sys->smoothpos.i_idx = 0;
    p_sys->smoothpos.i_last_time = 0;
    p_sys->smoothpos.i_us = 0;
588
    p_sys->smoothpos.i_latency_us = 0;
589 590
}

591 592 593 594 595 596 597 598 599 600 601 602 603
/**
 * Reset all AudioTrack positions and internal state
 */
static void
AudioTrack_Reset( JNIEnv *env, audio_output_t *p_aout )
{
    aout_sys_t *p_sys = p_aout->sys;

    AudioTrack_ResetPositions( env, p_aout );
    AudioTrack_ResetPlaybackHeadPosition( env, p_aout );
    p_sys->i_samples_written = 0;
}

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
/**
 * Get a smooth AudioTrack position
 *
 * This function smooth out the AudioTrack position since it has a very bad
 * precision (+/- 20ms on old devices).
 */
static mtime_t
AudioTrack_GetSmoothPositionUs( JNIEnv *env, audio_output_t *p_aout )
{
    aout_sys_t *p_sys = p_aout->sys;
    uint64_t i_audiotrack_us;
    mtime_t i_now = mdate();

    /* Fetch an AudioTrack position every SMOOTHPOS_INTERVAL_US (30ms) */
    if( i_now - p_sys->smoothpos.i_last_time >= SMOOTHPOS_INTERVAL_US )
    {
620
        i_audiotrack_us = FRAMES_TO_US( AudioTrack_getPlaybackHeadPosition( env, p_aout ) );
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635

        p_sys->smoothpos.i_last_time = i_now;

        /* Base the position off the current time */
        p_sys->smoothpos.p_us[p_sys->smoothpos.i_idx] = i_audiotrack_us - i_now;
        p_sys->smoothpos.i_idx = (p_sys->smoothpos.i_idx + 1)
                                 % SMOOTHPOS_SAMPLE_COUNT;
        if( p_sys->smoothpos.i_count < SMOOTHPOS_SAMPLE_COUNT )
            p_sys->smoothpos.i_count++;

        /* Calculate the average position based off the current time */
        p_sys->smoothpos.i_us = 0;
        for( uint32_t i = 0; i < p_sys->smoothpos.i_count; ++i )
            p_sys->smoothpos.i_us += p_sys->smoothpos.p_us[i];
        p_sys->smoothpos.i_us /= p_sys->smoothpos.i_count;
636 637 638 639 640 641 642 643 644 645 646

        if( jfields.AudioSystem.getOutputLatency )
        {
            int i_latency_ms = JNI_CALL( CallStaticIntMethod,
                                         jfields.AudioSystem.clazz,
                                         jfields.AudioSystem.getOutputLatency,
                                         jfields.AudioManager.STREAM_MUSIC );

            p_sys->smoothpos.i_latency_us = i_latency_ms > 0 ?
                                            i_latency_ms * 1000L : 0;
        }
647 648
    }
    if( p_sys->smoothpos.i_us != 0 )
649
        return p_sys->smoothpos.i_us + i_now - p_sys->smoothpos.i_latency_us;
650 651
    else
        return 0;
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
}

static mtime_t
AudioTrack_GetTimestampPositionUs( JNIEnv *env, audio_output_t *p_aout )
{
    aout_sys_t *p_sys = p_aout->sys;
    mtime_t i_now;

    if( !p_sys->timestamp.p_obj )
        return 0;

    i_now = mdate();

    /* Android doc:
     * getTimestamp: Poll for a timestamp on demand.
     *
     * If you need to track timestamps during initial warmup or after a
     * routing or mode change, you should request a new timestamp once per
     * second until the reported timestamps show that the audio clock is
     * stable. Thereafter, query for a new timestamp approximately once
     * every 10 seconds to once per minute. Calling this method more often
     * is inefficient. It is also counter-productive to call this method
     * more often than recommended, because the short-term differences
     * between successive timestamp reports are not meaningful. If you need
     * a high-resolution mapping between frame position and presentation
     * time, consider implementing that at application level, based on
     * low-resolution timestamps.
     */

    /* Fetch an AudioTrack timestamp every AUDIOTIMESTAMP_INTERVAL_US (500ms) */
    if( i_now - p_sys->timestamp.i_last_time >= AUDIOTIMESTAMP_INTERVAL_US )
    {
        p_sys->timestamp.i_last_time = i_now;

        if( JNI_AT_CALL_BOOL( getTimestamp, p_sys->timestamp.p_obj ) )
        {
            p_sys->timestamp.i_frame_us = JNI_AUDIOTIMESTAMP_GET_LONG( nanoTime ) / 1000;
            p_sys->timestamp.i_frame_pos = JNI_AUDIOTIMESTAMP_GET_LONG( framePosition );
        }
        else
        {
            p_sys->timestamp.i_frame_us = 0;
            p_sys->timestamp.i_frame_pos = 0;
        }
    }

    /* frame time should be after last play time
     * frame time shouldn't be in the future
     * frame time should be less than 10 seconds old */
    if( p_sys->timestamp.i_frame_us != 0 && p_sys->timestamp.i_frame_pos != 0
     && p_sys->timestamp.i_frame_us > p_sys->timestamp.i_play_time
     && i_now > p_sys->timestamp.i_frame_us
     && ( i_now - p_sys->timestamp.i_frame_us ) <= INT64_C(10000000) )
    {
        jlong i_time_diff = i_now - p_sys->timestamp.i_frame_us;
        jlong i_frames_diff = i_time_diff * p_sys->fmt.i_rate / CLOCK_FREQ;
        return FRAMES_TO_US( p_sys->timestamp.i_frame_pos + i_frames_diff );
    } else
        return 0;
}

713
static int
714
TimeGet( audio_output_t *p_aout, mtime_t *restrict p_delay )
715 716
{
    aout_sys_t *p_sys = p_aout->sys;
717
    mtime_t i_audiotrack_us;
718 719
    JNIEnv *env;

720
    if( p_sys->b_passthrough )
721
        return -1;
722

723 724 725 726 727
    vlc_mutex_lock( &p_sys->lock );

    if( p_sys->b_error || !p_sys->i_samples_written || !( env = GET_ENV() ) )
        goto bailout;

728 729
    i_audiotrack_us = AudioTrack_GetTimestampPositionUs( env, p_aout );

730 731
    if( i_audiotrack_us <= 0 )
        i_audiotrack_us = AudioTrack_GetSmoothPositionUs(env, p_aout );
Thomas Guillem's avatar
Thomas Guillem committed
732

Thomas Guillem's avatar
Thomas Guillem committed
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
/* Debug log for both delays */
#if 0
{
    mtime_t i_written_us = FRAMES_TO_US( p_sys->i_samples_written );
    mtime_t i_ts_us = AudioTrack_GetTimestampPositionUs( env, p_aout );
    mtime_t i_smooth_us = 0;

    if( i_ts_us > 0 )
        i_smooth_us = AudioTrack_GetSmoothPositionUs(env, p_aout );
    else if ( p_sys->smoothpos.i_us != 0 )
        i_smooth_us = p_sys->smoothpos.i_us + mdate()
            - p_sys->smoothpos.i_latency_us;

    msg_Err( p_aout, "TimeGet: TimeStamp: %lld, Smooth: %lld (latency: %lld)",
                    i_ts_us ? i_written_us - i_ts_us : 0,
                    i_smooth_us ? i_written_us - i_smooth_us : 0,
                    p_sys->smoothpos.i_latency_us );
}
#endif

753
    if( i_audiotrack_us > 0 )
754
    {
755
        /* AudioTrack delay */
756
        mtime_t i_delay = FRAMES_TO_US( p_sys->i_samples_written )
757
                        - i_audiotrack_us;
758 759
        if( i_delay >= 0 )
        {
760 761 762
            /* Circular buffer delay */
            i_delay += BYTES_TO_US( p_sys->circular.i_write
                                    - p_sys->circular.i_read );
763
            *p_delay = i_delay;
764
            vlc_mutex_unlock( &p_sys->lock );
765 766 767
            return 0;
        }
        else
768 769 770 771
        {
            msg_Warn( p_aout, "timing screwed, reset positions" );
            AudioTrack_ResetPositions( env, p_aout );
        }
772
    }
773 774 775

bailout:
    vlc_mutex_unlock( &p_sys->lock );
776
    return -1;
Thomas Guillem's avatar
Thomas Guillem committed
777 778
}

779
static void
780
AudioTrack_GetChanOrder( uint16_t i_physical_channels, uint32_t p_chans_out[] )
781
{
782
#define HAS_CHAN( x ) ( ( i_physical_channels & (x) ) == (x) )
783 784
    /* samples will be in the following order: FL FR FC LFE BL BR BC SL SR */
    int i = 0;
785

786
    if( HAS_CHAN( AOUT_CHAN_LEFT ) )
787
        p_chans_out[i++] = AOUT_CHAN_LEFT;
788
    if( HAS_CHAN( AOUT_CHAN_RIGHT ) )
789
        p_chans_out[i++] = AOUT_CHAN_RIGHT;
790

791
    if( HAS_CHAN( AOUT_CHAN_CENTER ) )
792
        p_chans_out[i++] = AOUT_CHAN_CENTER;
793

794
    if( HAS_CHAN( AOUT_CHAN_LFE ) )
795 796
        p_chans_out[i++] = AOUT_CHAN_LFE;

797
    if( HAS_CHAN( AOUT_CHAN_REARLEFT ) )
798
        p_chans_out[i++] = AOUT_CHAN_REARLEFT;
799
    if( HAS_CHAN( AOUT_CHAN_REARRIGHT ) )
800 801
        p_chans_out[i++] = AOUT_CHAN_REARRIGHT;

802
    if( HAS_CHAN( AOUT_CHAN_REARCENTER ) )
803 804
        p_chans_out[i++] = AOUT_CHAN_REARCENTER;

805 806 807 808 809
    if( HAS_CHAN( AOUT_CHAN_MIDDLELEFT ) )
        p_chans_out[i++] = AOUT_CHAN_MIDDLELEFT;
    if( HAS_CHAN( AOUT_CHAN_MIDDLERIGHT ) )
        p_chans_out[i++] = AOUT_CHAN_MIDDLERIGHT;

810
    assert( i <= AOUT_CHAN_MAX );
811 812 813
#undef HAS_CHAN
}

814 815 816 817 818 819 820 821 822
/**
 * Create an Android AudioTrack.
 * returns -1 on error, 0 on success.
 */
static int
AudioTrack_New( JNIEnv *env, audio_output_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;
823
    jint session_id = var_InheritInteger( p_aout, "audiotrack-session-id" );
824 825
    jobject p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC,
                                       i_rate, i_channel_config, i_format,
826 827
                                       i_size, jfields.AudioTrack.MODE_STREAM,
                                       session_id );
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
    if( CHECK_AT_EXCEPTION( "AudioTrack<init>" ) || !p_audiotrack )
    {
        msg_Warn( p_aout, "AudioTrack Init failed" ) ;
        return -1;
    }
    if( JNI_CALL_INT( p_audiotrack, jfields.AudioTrack.getState )
        != jfields.AudioTrack.STATE_INITIALIZED )
    {
        JNI_CALL_VOID( p_audiotrack, jfields.AudioTrack.release );
        (*env)->DeleteLocalRef( env, p_audiotrack );
        msg_Err( p_aout, "AudioTrack getState failed" );
        return -1;
    }

    p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack );
    (*env)->DeleteLocalRef( env, p_audiotrack );
    if( !p_sys->p_audiotrack )
        return -1;

    return 0;
}

/**
 * Destroy and recreate an Android AudioTrack using the same arguments.
 * returns -1 on error, 0 on success.
 */
static int
855
AudioTrack_Recreate( JNIEnv *env, audio_output_t *p_aout )
856 857 858 859 860
{
    aout_sys_t *p_sys = p_aout->sys;

    JNI_AT_CALL_VOID( release );
    (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
861
    p_sys->p_audiotrack = NULL;
862 863 864 865 866 867
    return AudioTrack_New( env, p_aout, p_sys->audiotrack_args.i_rate,
                           p_sys->audiotrack_args.i_channel_config,
                           p_sys->audiotrack_args.i_format,
                           p_sys->audiotrack_args.i_size );
}

868 869
/**
 * Configure and create an Android AudioTrack.
870
 * returns -1 on configuration error, 0 on success.
871
 */
872 873 874
static int
AudioTrack_Create( JNIEnv *env, audio_output_t *p_aout,
                   unsigned int i_rate,
875
                   int i_format,
876
                   uint16_t i_physical_channels )
Thomas Guillem's avatar
Thomas Guillem committed
877
{
878
    aout_sys_t *p_sys = p_aout->sys;
879
    int i_size, i_min_buffer_size, i_channel_config;
880

881
    switch( i_physical_channels )
882
    {
883
        case AOUT_CHANS_7_1:
Thomas Guillem's avatar
Thomas Guillem committed
884
            /* bitmask of CHANNEL_OUT_7POINT1 doesn't correspond to 5POINT1 and
885 886 887 888
             * SIDES */
            i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1 |
                               jfields.AudioFormat.CHANNEL_OUT_SIDE_LEFT |
                               jfields.AudioFormat.CHANNEL_OUT_SIDE_RIGHT;
889 890
            break;
        case AOUT_CHANS_5_1:
891
            i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1;
892 893
            break;
        case AOUT_CHAN_LEFT:
894
            i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;
895 896
            break;
        case AOUT_CHANS_STEREO:
897
            i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
898
            break;
899 900
        default:
            vlc_assert_unreachable();
Thomas Guillem's avatar
Thomas Guillem committed
901
    }
902

903
    i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, i_rate,
Thomas Guillem's avatar
Thomas Guillem committed
904 905 906 907
                                                i_channel_config, i_format );
    if( i_min_buffer_size <= 0 )
    {
        msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ;
908
        return -1;
Thomas Guillem's avatar
Thomas Guillem committed
909
    }
910
    i_size = i_min_buffer_size * 2;
Thomas Guillem's avatar
Thomas Guillem committed
911

912
    /* create AudioTrack object */
913 914 915
    if( AudioTrack_New( env, p_aout, i_rate, i_channel_config,
                        i_format , i_size ) != 0 )
        return -1;
916

917 918 919 920 921 922
    p_sys->audiotrack_args.i_rate = i_rate;
    p_sys->audiotrack_args.i_channel_config = i_channel_config;
    p_sys->audiotrack_args.i_format = i_format;
    p_sys->audiotrack_args.i_size = i_size;

    return 0;
923 924
}

925 926 927 928 929 930 931 932 933 934 935 936 937
static bool
AudioTrack_HasEncoding( audio_output_t *p_aout, vlc_fourcc_t i_format,
                        bool *p_dtshd )
{
    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) )

    *p_dtshd = false;
    switch( i_format )
    {
        case VLC_CODEC_DTS:
938 939
            if( MATCH_ENCODING_FLAG( ENCODING_DTS_HD )
             && var_GetBool( p_aout, "dtshd" ) )
940 941 942 943 944 945 946 947 948
            {
                *p_dtshd = true;
                return true;
            }
            return MATCH_ENCODING_FLAG( ENCODING_DTS );
        case VLC_CODEC_A52:
            return MATCH_ENCODING_FLAG( ENCODING_AC3 );
        case VLC_CODEC_EAC3:
            return MATCH_ENCODING_FLAG( ENCODING_E_AC3 );
949 950 951
        case VLC_CODEC_TRUEHD:
        case VLC_CODEC_MLP:
            return MATCH_ENCODING_FLAG( ENCODING_DOLBY_TRUEHD );
952 953 954 955 956
        default:
            return false;
    }
}

957
static int
958
StartPassthrough( JNIEnv *env, audio_output_t *p_aout )
959 960
{
    aout_sys_t *p_sys = p_aout->sys;
961
    int i_at_format;
962

963
    if( jfields.AudioFormat.has_ENCODING_IEC61937 )
964
    {
965 966 967
        bool b_dtshd;
        if( !AudioTrack_HasEncoding( p_aout, p_sys->fmt.i_format, &b_dtshd ) )
            return VLC_EGENERIC;
968 969 970 971 972 973 974
        i_at_format = jfields.AudioFormat.ENCODING_IEC61937;
        switch( p_sys->fmt.i_format )
        {
            case VLC_CODEC_TRUEHD:
            case VLC_CODEC_MLP:
                p_sys->fmt.i_rate = 192000;
                p_sys->fmt.i_bytes_per_frame = 16;
975 976 977 978 979

                /* AudioFormat.ENCODING_IEC61937 documentation says that the
                 * channel layout must be stereo. Well, not for TrueHD
                 * apparently */
                p_sys->fmt.i_physical_channels = AOUT_CHANS_7_1;
980
                break;
981
            case VLC_CODEC_DTS:
982 983 984
                p_sys->fmt.i_bytes_per_frame = 4;
                p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
                if( b_dtshd )
985 986 987 988 989
                {
                    p_sys->fmt.i_rate = 192000;
                    p_sys->fmt.i_bytes_per_frame = 16;
                }
                break;
990 991 992
            case VLC_CODEC_EAC3:
                p_sys->fmt.i_rate = 192000;
            case VLC_CODEC_A52:
993
                p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
994 995 996 997 998 999 1000 1001
                p_sys->fmt.i_bytes_per_frame = 4;
                break;
            default:
                return VLC_EGENERIC;
        }
        p_sys->fmt.i_frame_length = 1;
        p_sys->fmt.i_channels = aout_FormatNbChannels( &p_sys->fmt );
        p_sys->fmt.i_format = VLC_CODEC_SPDIFL;
1002 1003 1004
    }
    else
    {
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
        switch( p_sys->fmt.i_format )
        {
            case VLC_CODEC_A52:
                if( !jfields.AudioFormat.has_ENCODING_AC3 )
                    return VLC_EGENERIC;
                i_at_format = jfields.AudioFormat.ENCODING_AC3;
                break;
            case VLC_CODEC_DTS:
                if( !jfields.AudioFormat.has_ENCODING_DTS )
                    return VLC_EGENERIC;
                i_at_format = jfields.AudioFormat.ENCODING_DTS;
                break;
            default:
                return VLC_EGENERIC;
        }
        p_sys->fmt.i_bytes_per_frame = 4;
        p_sys->fmt.i_frame_length = 1;
1022
        p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
1023 1024
        p_sys->fmt.i_channels = 2;
        p_sys->fmt.i_format = VLC_CODEC_SPDIFB;
1025
    }
1026

1027 1028 1029 1030 1031 1032 1033 1034 1035
    int i_ret = AudioTrack_Create( env, p_aout, p_sys->fmt.i_rate, i_at_format,
                                   p_sys->fmt.i_physical_channels );
    if( i_ret != VLC_SUCCESS )
        msg_Warn( p_aout, "SPDIF configuration failed" );
    else
    {
        p_sys->b_passthrough = true;
        p_sys->i_chans_to_reorder = 0;
    }
1036

1037 1038
    return i_ret;
}
1039

1040 1041 1042 1043 1044 1045
static int
StartPCM( JNIEnv *env, audio_output_t *p_aout, unsigned i_max_channels )
{
    aout_sys_t *p_sys = p_aout->sys;
    unsigned i_nb_channels;
    int i_at_format, i_ret;
1046

1047 1048 1049 1050 1051 1052
    if (jfields.AudioTrack.getNativeOutputSampleRate)
        p_sys->fmt.i_rate =
            JNI_AT_CALL_STATIC_INT( getNativeOutputSampleRate,
                                    jfields.AudioManager.STREAM_MUSIC );
    else
        p_sys->fmt.i_rate = VLC_CLIP( p_sys->fmt.i_rate, 4000, 48000 );
1053

1054
    do
1055
    {
1056 1057 1058
        /* We can only accept U8, S16N, FL32, and AC3 */
        switch( p_sys->fmt.i_format )
        {
1059
        case VLC_CODEC_U8:
1060
            i_at_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
1061 1062
            break;
        case VLC_CODEC_S16N:
1063
            i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
1064 1065
            break;
        case VLC_CODEC_FL32:
1066 1067 1068 1069
            if( jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
                i_at_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
            else
            {
1070
                p_sys->fmt.i_format = VLC_CODEC_S16N;
1071 1072
                i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
            }
1073 1074
            break;
        default:
1075
            p_sys->fmt.i_format = VLC_CODEC_S16N;
1076
            i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
1077
            break;
1078
        }
1079

1080 1081 1082
        /* Android AudioTrack supports only mono, stereo, 5.1 and 7.1.
         * Android will downmix to stereo if audio output doesn't handle 5.1 or 7.1
         */
1083

1084
        i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
1085 1086 1087
        if( i_nb_channels == 0 )
            return VLC_EGENERIC;
        if( AOUT_FMT_LINEAR( &p_sys->fmt ) )
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
            i_nb_channels = __MIN( i_max_channels, i_nb_channels );
        if( i_nb_channels > 5 )
        {
            if( i_nb_channels > 7 && jfields.AudioFormat.has_CHANNEL_OUT_SIDE )
                p_sys->fmt.i_physical_channels = AOUT_CHANS_7_1;
            else
                p_sys->fmt.i_physical_channels = AOUT_CHANS_5_1;
        } else
        {
            if( i_nb_channels == 1 )
                p_sys->fmt.i_physical_channels = AOUT_CHAN_LEFT;
            else
                p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
        }
1102

1103
        /* Try to create an AudioTrack with the most advanced channel and
1104 1105 1106
         * format configuration. If AudioTrack_Create fails, try again with a
         * less advanced format (PCM S16N). If it fails again, try again with
         * Stereo channels. */
1107
        i_ret = AudioTrack_Create( env, p_aout, p_sys->fmt.i_rate, i_at_format,
1108
                                   p_sys->fmt.i_physical_channels );
1109
        if( i_ret != 0 )
1110
        {
1111
            if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
1112 1113 1114 1115 1116
            {
                msg_Warn( p_aout, "FL32 configuration failed, "
                                  "fallback to S16N PCM" );
                p_sys->fmt.i_format = VLC_CODEC_S16N;
            }
1117
            else if( p_sys->fmt.i_physical_channels & AOUT_CHANS_5_1 )
1118 1119 1120 1121 1122 1123 1124
            {
                msg_Warn( p_aout, "5.1 or 7.1 configuration failed, "
                                  "fallback to Stereo" );
                p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
            }
            else
                break;
1125
        }
1126
    } while( i_ret != 0 );
1127

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
    if( i_ret != VLC_SUCCESS )
        return i_ret;

    uint32_t p_chans_out[AOUT_CHAN_MAX];
    memset( p_chans_out, 0, sizeof(p_chans_out) );
    AudioTrack_GetChanOrder( p_sys->fmt.i_physical_channels, p_chans_out );
    p_sys->i_chans_to_reorder =
        aout_CheckChannelReorder( NULL, p_chans_out,
                                  p_sys->fmt.i_physical_channels,
                                  p_sys->p_chan_table );
    aout_FormatPrepare( &p_sys->fmt );
    return VLC_SUCCESS;
}
1141

1142 1143 1144 1145 1146 1147 1148 1149 1150
static int
Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
{
    aout_sys_t *p_sys = p_aout->sys;
    JNIEnv *env;
    int i_ret;
    bool b_try_passthrough;
    unsigned i_max_channels;

1151
    if( p_sys->at_dev == AT_DEV_ENCODED )
1152
    {
1153 1154 1155 1156 1157 1158
        b_try_passthrough = true;
        i_max_channels = AT_DEV_MAX_CHANNELS;
    }
    else
    {
        b_try_passthrough = var_InheritBool( p_aout, "spdif" );
1159
        i_max_channels = p_sys->at_dev == AT_DEV_STEREO ? 2 : AT_DEV_MAX_CHANNELS;
1160
    }
1161 1162 1163 1164 1165 1166 1167 1168

    if( !( env = GET_ENV() ) )
        return VLC_EGENERIC;

    p_sys->fmt = *p_fmt;

    aout_FormatPrint( p_aout, "VLC is looking for:", &p_sys->fmt );

1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
    bool low_latency = false;
    if (p_sys->fmt.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
    {
        p_sys->fmt.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;

        /* TODO: detect sink channel layout */
        p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
        aout_FormatPrepare(&p_sys->fmt);
        low_latency = true;
    }

1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
    if( AOUT_FMT_LINEAR( &p_sys->fmt ) )
        i_ret = StartPCM( env, p_aout, i_max_channels );
    else if( b_try_passthrough )
        i_ret = StartPassthrough( env, p_aout );
    else
        return VLC_EGENERIC;

    if( i_ret != 0 )
        return VLC_EGENERIC;

1190
    p_sys->i_max_audiotrack_samples = BYTES_TO_FRAMES( p_sys->audiotrack_args.i_size );
Thomas Guillem's avatar
Thomas Guillem committed
1191

1192
#ifdef AUDIOTRACK_HW_LATENCY
1193 1194 1195
    if( jfields.AudioTimestamp.clazz )
    {
        /* create AudioTimestamp object */
1196 1197 1198
        jobject p_obj = JNI_CALL( NewObject, jfields.AudioTimestamp.clazz,
                                 jfields.AudioTimestamp.ctor );
        if( p_obj )
1199
        {
1200 1201
            p_sys->timestamp.p_obj = (*env)->NewGlobalRef( env, p_obj );
            (*env)->DeleteLocalRef( env, p_obj );
1202
        }
1203
        if( !p_sys->timestamp.p_obj )
1204
            goto error;
1205
    }
1206
#endif
Thomas Guillem's avatar
Thomas Guillem committed
1207

1208 1209
    AudioTrack_Reset( env, p_aout );

1210
    if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
Thomas Guillem's avatar
Thomas Guillem committed
1211
    {
1212 1213
        msg_Dbg( p_aout, "using WRITE_FLOATARRAY");
        p_sys->i_write_type = WRITE_FLOATARRAY;
1214
    }
1215
    else if( p_sys->fmt.i_format == VLC_CODEC_SPDIFL )
1216
    {
1217
        assert( jfields.AudioFormat.has_ENCODING_IEC61937 );
1218 1219 1220
        msg_Dbg( p_aout, "using WRITE_SHORTARRAYV23");
        p_sys->i_write_type = WRITE_SHORTARRAYV23;
    }
1221 1222 1223 1224 1225
    else if( jfields.AudioTrack.writeV23 )
    {
        msg_Dbg( p_aout, "using WRITE_BYTEARRAYV23");
        p_sys->i_write_type = WRITE_BYTEARRAYV23;
    }
1226
    else if( jfields.AudioTrack.writeBufferV21 )
1227
    {
1228 1229
        msg_Dbg( p_aout, "using WRITE_BYTEBUFFER");
        p_sys->i_write_type = WRITE_BYTEBUFFER;
Thomas Guillem's avatar
Thomas Guillem committed
1230 1231 1232
    }
    else
    {
1233 1234 1235 1236 1237
        msg_Dbg( p_aout, "using WRITE_BYTEARRAY");
        p_sys->i_write_type = WRITE_BYTEARRAY;
    }

    p_sys->circular.i_read = p_sys->circular.i_write = 0;
1238
    p_sys->circular.i_size = (int)p_sys->fmt.i_rate
1239
                           * p_sys->fmt.i_bytes_per_frame
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
                           / p_sys->fmt.i_frame_length;
    if (low_latency)
    {
        /* 40 ms of buffering */
        p_sys->circular.i_size = p_sys->circular.i_size / 25;
    }
    else
    {
        /* 2 seconds of buffering */
        p_sys->circular.i_size = p_sys->circular.i_size * AOUT_MAX_PREPARE_TIME
                               / CLOCK_FREQ;
    }
1252 1253 1254 1255 1256

    /* Allocate circular buffer */
    switch( p_sys->i_write_type )
    {
        case WRITE_BYTEARRAY:
1257
        case WRITE_BYTEARRAYV23:
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
        {
            jbyteArray p_bytearray;

            p_bytearray = (*env)->NewByteArray( env, p_sys->circular.i_size );
            if( p_bytearray )
            {
                p_sys->circular.u.p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );
                (*env)->DeleteLocalRef( env, p_bytearray );
            }

            if( !p_sys->circular.u.p_bytearray )
            {
                msg_Err(p_aout, "byte array allocation failed");
                goto error;
            }
            break;
        }
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290