+#define CHECK_EXCEPTION( what, critical ) do { \
+ if( (*env)->ExceptionOccurred( env ) ) \
+ { \
+ 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) ); \
+ CHECK_EXCEPTION( str, critical ); \
+} while( 0 )
+#define GET_ID( get, id, str, args, critical ) do { \
+ jfields.id = (*env)->get( env, clazz, (str), (args) ); \
+ CHECK_EXCEPTION( #get, critical ); \
+} while( 0 )
+#define GET_CONST_INT( id, str, critical ) do { \
+ field = NULL; \
+ field = (*env)->GetStaticFieldID( env, clazz, (str), "I" ); \
+ CHECK_EXCEPTION( #id, critical ); \
+ if( field ) \
+ { \
+ jfields.id = (*env)->GetStaticIntField( env, clazz, field ); \
+ CHECK_EXCEPTION( #id, critical ); \
+ } \
+} while( 0 )
+
+ /* AudioTrack class init */
+ GET_CLASS( "android/media/AudioTrack", true );
+ jfields.AudioTrack.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );
+ CHECK_EXCEPTION( "NewGlobalRef", true );
+
+ GET_ID( GetMethodID, AudioTrack.ctor, "<init>", "(IIIIII)V", true );
+ GET_ID( GetMethodID, AudioTrack.release, "release", "()V", true );
+ GET_ID( GetMethodID, AudioTrack.getState, "getState", "()I", true );
+ 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 );
+
+ GET_ID( GetMethodID, AudioTrack.writeV21, "write", "(Ljava/nio/ByteBuffer;II)I", false );
+ if( jfields.AudioTrack.writeV21 )
+ {
+ GET_CONST_INT( AudioTrack.WRITE_NON_BLOCKING, "WRITE_NON_BLOCKING", true );
+#ifdef AUDIOTRACK_USE_FLOAT
+ GET_ID( GetMethodID, AudioTrack.writeFloat, "write", "([FIII)I", true );
+#endif
+ } else
+ GET_ID( GetMethodID, AudioTrack.write, "write", "([BII)I", true );
+
+ GET_ID( GetMethodID, AudioTrack.getTimestamp,
+ "getTimestamp", "(Landroid/media/AudioTimestamp;)Z", false );
+ GET_ID( GetMethodID, AudioTrack.getPlaybackHeadPosition,
+ "getPlaybackHeadPosition", "()I", true );
+
+ GET_ID( GetStaticMethodID, AudioTrack.getMinBufferSize, "getMinBufferSize",
+ "(III)I", true );
+ GET_CONST_INT( AudioTrack.STATE_INITIALIZED, "STATE_INITIALIZED", true );
+ 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 );
+ GET_CONST_INT( AudioTrack.ERROR_INVALID_OPERATION,
+ "ERROR_INVALID_OPERATION", true );
+
+ /* 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 );
+ }
+
+ /* AudioFormat class init */
+ 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 );
+ jfields.AudioFormat.has_ENCODING_PCM_FLOAT = field != NULL &&
+ jfields.AudioTrack.writeFloat;
+#else
+ jfields.AudioFormat.has_ENCODING_PCM_FLOAT = false;
+#endif
+ GET_CONST_INT( AudioFormat.ENCODING_AC3, "ENCODING_AC3", false );
+ if( field != NULL )
+ {
+ GET_CONST_INT( AudioFormat.ENCODING_E_AC3, "ENCODING_E_AC3", false );
+ jfields.AudioFormat.has_ENCODING_AC3 = field != NULL;
+ } else
+ jfields.AudioFormat.has_ENCODING_AC3 = false;
+
+ GET_CONST_INT( AudioFormat.CHANNEL_OUT_MONO, "CHANNEL_OUT_MONO", true );
+ GET_CONST_INT( AudioFormat.CHANNEL_OUT_STEREO, "CHANNEL_OUT_STEREO", true );
+ 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 );
+ 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;
+
+ /* AudioManager class init */
+ 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" );
+ if( b_attached )
+ jni_detach_thread();
+ vlc_mutex_unlock( &lock );
+ return ret;
+}
+
+static inline bool
+check_exception( JNIEnv *env, audio_output_t *p_aout,
+ const char *method )
+{
+ if( (*env)->ExceptionOccurred( env ) )
+ {
+ aout_sys_t *p_sys = p_aout->sys;
+
+ p_sys->b_audiotrack_exception = true;
+ (*env)->ExceptionClear( env );
+ msg_Err( p_aout, "AudioTrack.%s triggered an exception !", method );
+ return true;
+ } else
+ return false;
+}
+#define CHECK_AT_EXCEPTION( method ) check_exception( env, p_aout, method )
+
+#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__ )
+#define JNI_CALL_BOOL( obj, method, ... ) JNI_CALL( CallBooleanMethod, obj, method, ##__VA_ARGS__ )
+#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__ )
+#define JNI_AT_CALL_BOOL( method, ... ) JNI_CALL_BOOL( p_sys->p_audiotrack, jfields.AudioTrack.method, ##__VA_ARGS__ )
+#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__ )
+
+#define JNI_AUDIOTIMESTAMP_GET_LONG( field ) JNI_CALL( GetLongField, p_sys->p_audioTimestamp, jfields.AudioTimestamp.field )
+
+static inline mtime_t
+frames_to_us( aout_sys_t *p_sys, uint32_t i_nb_frames )
+{
+ return i_nb_frames * CLOCK_FREQ / p_sys->fmt.i_rate;
+}
+#define FRAMES_TO_US(x) frames_to_us( p_sys, (x) )
+
+static inline uint32_t
+bytes_to_frames( aout_sys_t *p_sys, size_t i_bytes )
+{
+ if( p_sys->b_spdif )
+ return i_bytes * A52_FRAME_NB / p_sys->i_bytes_per_frame;
+ else
+ return i_bytes / p_sys->i_bytes_per_frame;
+}
+#define BYTES_TO_FRAMES(x) bytes_to_frames( p_sys, (x) )
+
+static inline size_t
+frames_to_bytes( aout_sys_t *p_sys, uint32_t i_frames )
+{
+ if( p_sys->b_spdif )
+ return i_frames * p_sys->i_bytes_per_frame / A52_FRAME_NB;
+ else
+ return i_frames * p_sys->i_bytes_per_frame;
+}
+#define FRAMES_TO_BYTES(x) frames_to_bytes( p_sys, (x) )
+
+static struct thread_cmd *
+ThreadCmd_New( int id )
+{
+ struct thread_cmd *p_cmd = calloc( 1, sizeof(struct thread_cmd) );
+
+ if( p_cmd )
+ p_cmd->id = id;
+
+ return p_cmd;
+}
+
+static void
+ThreadCmd_InsertHead( aout_sys_t *p_sys, struct thread_cmd *p_cmd )
+{
+ TAILQ_INSERT_HEAD( &p_sys->thread_cmd_queue, p_cmd, next);
+ vlc_cond_signal( &p_sys->cond );
+}
+
+static void
+ThreadCmd_InsertTail( aout_sys_t *p_sys, struct thread_cmd *p_cmd )
+{
+ TAILQ_INSERT_TAIL( &p_sys->thread_cmd_queue, p_cmd, next);
+ vlc_cond_signal( &p_sys->cond );
+}
+
+static bool
+ThreadCmd_Wait( aout_sys_t *p_sys, struct thread_cmd *p_cmd )
+{
+ while( p_cmd->id != CMD_DONE )
+ vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
+
+ return p_cmd->id == CMD_DONE;
+}
+
+static void
+ThreadCmd_FlushQueue( aout_sys_t *p_sys )
+{
+ struct thread_cmd *p_cmd, *p_cmd_next;
+
+ for ( p_cmd = TAILQ_FIRST( &p_sys->thread_cmd_queue );
+ p_cmd != NULL; p_cmd = p_cmd_next )
+ {
+ p_cmd_next = TAILQ_NEXT( p_cmd, next );
+ TAILQ_REMOVE( &p_sys->thread_cmd_queue, p_cmd, next );
+ if( p_cmd->pf_destroy )
+ p_cmd->pf_destroy( p_cmd );