]> git.sesse.net Git - vlc/blob - modules/audio_output/audiotrack.c
Mark unreachable code on GCC even if NDEBUG
[vlc] / modules / audio_output / audiotrack.c
1 /*****************************************************************************
2  * audiotrack.c: Android Java AudioTrack audio output module
3  *****************************************************************************
4  * Copyright © 2012-2015 VLC authors and VideoLAN, VideoLabs
5  *
6  * Authors: Thomas Guillem <thomas@gllm.fr>
7  *          Ming Hu <tewilove@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29 #include <jni.h>
30 #include <dlfcn.h>
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_aout.h>
35 #include <vlc_threads.h>
36
37 static int  Open( vlc_object_t * );
38 static void Close( vlc_object_t * );
39
40 struct aout_sys_t {
41     /* sw gain */
42     float soft_gain;
43     bool soft_mute;
44
45     /* Owned by JNIThread */
46     jobject p_audiotrack; /* AudioTrack ref */
47     jbyteArray p_bytearray; /* ByteArray ref */
48     size_t i_bytearray_size; /* size of the ByteArray */
49     audio_sample_format_t fmt; /* fmt setup by Start */
50     uint32_t i_samples_written; /* samples written since start/flush */
51     uint32_t i_dsp_initial; /* initial delay of the dsp */
52     int i_bytes_per_frame; /* byte per frame */
53
54     /* JNIThread control */
55     vlc_mutex_t mutex;
56     vlc_cond_t cond;
57     vlc_thread_t thread;
58     bool b_thread_run; /* is thread alive */
59     struct thread_cmd *p_cmd; /* actual cmd process by JNIThread */
60 };
61
62 /* Soft volume helper */
63 #include "audio_output/volume.h"
64
65 //#define AUDIOTRACK_USE_FLOAT
66
67 vlc_module_begin ()
68     set_shortname( "AudioTrack" )
69     set_description( N_( "Android AudioTrack audio output" ) )
70     set_capability( "audio output", 180 )
71     set_category( CAT_AUDIO )
72     set_subcategory( SUBCAT_AUDIO_AOUT )
73     add_sw_gain()
74     add_shortcut( "audiotrack" )
75     set_callbacks( Open, Close )
76 vlc_module_end ()
77
78 struct thread_cmd {
79     enum {
80         CMD_START,
81         CMD_STOP,
82         CMD_PLAY,
83         CMD_PAUSE,
84         CMD_FLUSH,
85         CMD_TIME_GET,
86         CMD_DONE,
87     } id;
88     union {
89         struct {
90             audio_sample_format_t *p_fmt;
91         } start;
92         struct {
93             block_t *p_buffer;
94         } play;
95         struct {
96             bool b_pause;
97             mtime_t i_date;
98         } pause;
99         struct {
100             bool b_wait;
101         } flush;
102     } in;
103     union {
104         struct {
105             int i_ret;
106             audio_sample_format_t *p_fmt;
107         } start;
108         struct {
109             int i_ret;
110             mtime_t i_delay;
111         } time_get;
112     } out;
113 };
114
115 #define THREAD_NAME "android_audiotrack"
116
117 extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
118 extern void jni_detach_thread();
119 extern int jni_get_env(JNIEnv **env);
120
121 static struct
122 {
123     struct {
124         jclass clazz;
125         jmethodID ctor;
126         jmethodID release;
127         jmethodID play;
128         jmethodID stop;
129         jmethodID flush;
130         jmethodID pause;
131         jmethodID write;
132         jmethodID getPlaybackHeadPosition;
133         jmethodID getMinBufferSize;
134         jint MODE_STREAM;
135         jint ERROR;
136         jint ERROR_BAD_VALUE;
137         jint ERROR_INVALID_OPERATION;
138     } AudioTrack;
139     struct {
140         jint ENCODING_PCM_8BIT;
141         jint ENCODING_PCM_16BIT;
142         jint ENCODING_PCM_FLOAT;
143         bool has_ENCODING_PCM_FLOAT;
144         jint CHANNEL_OUT_MONO;
145         jint CHANNEL_OUT_STEREO;
146     } AudioFormat;
147     struct {
148         jint ERROR_DEAD_OBJECT;
149         bool has_ERROR_DEAD_OBJECT;
150         jint STREAM_MUSIC;
151     } AudioManager;
152 } jfields;
153
154 /* init all jni fields.
155  * Done only one time during the first initialisation */
156 static bool
157 InitJNIFields( audio_output_t *p_aout )
158 {
159     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
160     static int i_init_state = -1;
161     bool ret, b_attached = false;
162     jclass clazz;
163     jfieldID field;
164     JNIEnv* env = NULL;
165
166     vlc_mutex_lock( &lock );
167
168     if( i_init_state != -1 )
169         goto end;
170
171     if( jni_get_env(&env) < 0 )
172     {
173         jni_attach_thread( &env, THREAD_NAME );
174         if( !env )
175         {
176             i_init_state = 0;
177             goto end;
178         }
179         b_attached = true;
180     }
181
182 #define CHECK_EXCEPTION( what, critical ) do { \
183     if( (*env)->ExceptionOccurred( env ) ) \
184     { \
185         msg_Err( p_aout, "%s failed", what ); \
186         (*env)->ExceptionClear( env ); \
187         if( (critical) ) \
188         { \
189             i_init_state = 0; \
190             goto end; \
191         } \
192     } \
193 } while( 0 )
194 #define GET_CLASS( str, critical ) do { \
195     clazz = (*env)->FindClass( env, (str) ); \
196     CHECK_EXCEPTION( str, critical ); \
197 } while( 0 )
198 #define GET_ID( get, id, str, args, critical ) do { \
199     jfields.id = (*env)->get( env, clazz, (str), (args) ); \
200     CHECK_EXCEPTION( #get, critical ); \
201 } while( 0 )
202 #define GET_CONST_INT( id, str, critical ) do { \
203     field = NULL; \
204     field = (*env)->GetStaticFieldID( env, clazz, (str), "I" ); \
205     CHECK_EXCEPTION( #id, critical ); \
206     if( field ) \
207     { \
208         jfields.id = (*env)->GetStaticIntField( env, clazz, field ); \
209         CHECK_EXCEPTION( #id, critical ); \
210     } \
211 } while( 0 )
212
213     GET_CLASS( "android/media/AudioTrack", true );
214     jfields.AudioTrack.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );
215     CHECK_EXCEPTION( "NewGlobalRef", true );
216
217     GET_ID( GetMethodID, AudioTrack.ctor, "<init>", "(IIIIII)V", true );
218     GET_ID( GetMethodID, AudioTrack.release, "release", "()V", true );
219     GET_ID( GetMethodID, AudioTrack.play, "play", "()V", true );
220     GET_ID( GetMethodID, AudioTrack.stop, "stop", "()V", true );
221     GET_ID( GetMethodID, AudioTrack.flush, "flush", "()V", true );
222     GET_ID( GetMethodID, AudioTrack.pause, "pause", "()V", true );
223     GET_ID( GetMethodID, AudioTrack.write, "write", "([BII)I", true );
224     GET_ID( GetMethodID, AudioTrack.getPlaybackHeadPosition,
225             "getPlaybackHeadPosition", "()I", true );
226     GET_ID( GetStaticMethodID, AudioTrack.getMinBufferSize, "getMinBufferSize",
227             "(III)I", true );
228     GET_CONST_INT( AudioTrack.MODE_STREAM, "MODE_STREAM", true );
229     GET_CONST_INT( AudioTrack.ERROR, "ERROR", true );
230     GET_CONST_INT( AudioTrack.ERROR_BAD_VALUE , "ERROR_BAD_VALUE", true );
231     GET_CONST_INT( AudioTrack.ERROR_INVALID_OPERATION ,
232                    "ERROR_INVALID_OPERATION", true );
233
234     GET_CLASS( "android/media/AudioFormat", true );
235     GET_CONST_INT( AudioFormat.ENCODING_PCM_8BIT, "ENCODING_PCM_8BIT", true );
236     GET_CONST_INT( AudioFormat.ENCODING_PCM_16BIT, "ENCODING_PCM_16BIT", true );
237 #ifdef AUDIOTRACK_USE_FLOAT
238     GET_CONST_INT( AudioFormat.ENCODING_PCM_FLOAT, "ENCODING_PCM_FLOAT",
239                    false );
240     jfields.AudioFormat.has_ENCODING_PCM_FLOAT = field != NULL;
241 #else
242     jfields.AudioFormat.has_ENCODING_PCM_FLOAT = false;
243 #endif
244     GET_CONST_INT( AudioFormat.CHANNEL_OUT_MONO, "CHANNEL_OUT_MONO", true );
245     GET_CONST_INT( AudioFormat.CHANNEL_OUT_STEREO, "CHANNEL_OUT_STEREO", true );
246
247     GET_CLASS( "android/media/AudioManager", true );
248     GET_CONST_INT( AudioManager.ERROR_DEAD_OBJECT, "ERROR_DEAD_OBJECT", false );
249     jfields.AudioManager.has_ERROR_DEAD_OBJECT = field != NULL;
250     GET_CONST_INT( AudioManager.STREAM_MUSIC, "STREAM_MUSIC", true );
251
252 #undef CHECK_EXCEPTION
253 #undef GET_CLASS
254 #undef GET_ID
255 #undef GET_CONST_INT
256
257     i_init_state = 1;
258 end:
259     ret = i_init_state == 1;
260     if( !ret )
261         msg_Err( p_aout, "AudioTrack jni init failed" );
262     if( b_attached )
263         jni_detach_thread();
264     vlc_mutex_unlock( &lock );
265     return ret;
266 }
267
268 static inline bool
269 check_exception( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
270                  const char *method )
271 {
272     if( (*env)->ExceptionOccurred( env ) )
273     {
274         (*env)->ExceptionClear( env );
275         *p_error = true;
276         msg_Err( p_aout, "AudioTrack.%s triggered an exception !", method );
277         return true;
278     } else
279         return false;
280 }
281 #define CHECK_EXCEPTION( method ) check_exception( env, p_error, p_aout, method )
282
283 #define JNI_CALL( what, obj, method, ... ) (*env)->what( env, obj, method, ##__VA_ARGS__ )
284
285 #define JNI_CALL_INT( obj, method, ... ) JNI_CALL( CallIntMethod, obj, method, ##__VA_ARGS__ )
286 #define JNI_CALL_VOID( obj, method, ... ) JNI_CALL( CallVoidMethod, obj, method, ##__VA_ARGS__ )
287 #define JNI_CALL_STATIC_INT( clazz, method, ... ) JNI_CALL( CallStaticIntMethod, clazz, method, ##__VA_ARGS__ )
288
289 #define JNI_AT_NEW( ... ) JNI_CALL( NewObject, jfields.AudioTrack.clazz, jfields.AudioTrack.ctor, ##__VA_ARGS__ )
290 #define JNI_AT_CALL_INT( method, ... ) JNI_CALL_INT( p_sys->p_audiotrack, jfields.AudioTrack.method, ##__VA_ARGS__ )
291 #define JNI_AT_CALL_VOID( method, ... ) JNI_CALL_VOID( p_sys->p_audiotrack, jfields.AudioTrack.method, ##__VA_ARGS__ )
292 #define JNI_AT_CALL_STATIC_INT( method, ... ) JNI_CALL( CallStaticIntMethod, jfields.AudioTrack.clazz, jfields.AudioTrack.method, ##__VA_ARGS__ )
293
294 static int
295 JNIThread_TimeGet( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
296                    mtime_t *p_delay )
297 {
298     VLC_UNUSED( p_error );
299     aout_sys_t *p_sys = p_aout->sys;
300     uint32_t dsp;
301
302     /* Android doc:
303      * getPlaybackHeadPosition: Returns the playback head position expressed in
304      * frames. Though the "int" type is signed 32-bits, the value should be
305      * reinterpreted as if it is unsigned 32-bits. That is, the next position
306      * after 0x7FFFFFFF is (int) 0x80000000. This is a continuously advancing
307      * counter. It will wrap (overflow) periodically, for example approximately
308      * once every 27:03:11 hours:minutes:seconds at 44.1 kHz. It is reset to
309      * zero by flush(), reload(), and stop().
310      */
311
312     dsp = (uint32_t )JNI_AT_CALL_INT( getPlaybackHeadPosition );
313
314     if( p_sys->i_samples_written == 0 ) {
315         p_sys->i_dsp_initial = dsp;
316         return -1;
317     }
318
319     dsp -= p_sys->i_dsp_initial;
320     if( dsp == 0 )
321         return -1;
322
323     if( p_delay )
324         *p_delay = ((mtime_t)p_sys->i_samples_written - dsp) *
325                    CLOCK_FREQ / p_sys->fmt.i_rate;
326
327     return 0;
328 }
329
330 static int
331 JNIThread_Start( JNIEnv *env, bool *p_error, audio_output_t *p_aout )
332 {
333     struct aout_sys_t *p_sys = p_aout->sys;
334     int i_size, i_min_buffer_size, i_channel_config, i_rate, i_format,
335         i_format_size, i_nb_channels;
336     jobject p_audiotrack;
337
338     /* 4000 <= frequency <= 48000 */
339     i_rate = p_sys->fmt.i_rate;
340     if( i_rate < 4000 )
341         i_rate = 4000;
342     if( i_rate > 48000 )
343         i_rate = 48000;
344
345     /* We can only accept U8, S16N, and FL32 (depending on Android version) */
346     if( p_sys->fmt.i_format != VLC_CODEC_U8
347         && p_sys->fmt.i_format != VLC_CODEC_S16N
348         && p_sys->fmt.i_format != VLC_CODEC_FL32 )
349         p_sys->fmt.i_format = VLC_CODEC_S16N;
350
351     if( p_sys->fmt.i_format == VLC_CODEC_FL32
352         && !jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
353         p_sys->fmt.i_format = VLC_CODEC_S16N;
354
355     if( p_sys->fmt.i_format == VLC_CODEC_S16N )
356     {
357         i_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
358         i_format_size = 2;
359     } else if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
360     {
361         i_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
362         i_format_size = 4;
363     } else
364     {
365         i_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
366         i_format_size = 1;
367     }
368     p_sys->fmt.i_original_channels = p_sys->fmt.i_physical_channels;
369
370     i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
371     switch( i_nb_channels )
372     {
373     case 1:
374         i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;
375         p_sys->fmt.i_physical_channels = AOUT_CHAN_CENTER;
376         break;
377     default:
378         i_nb_channels = 2; // XXX: AudioTrack handle only stereo for now
379     case 2:
380         i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
381         p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
382         break;
383     }
384
385     i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, i_rate,
386                                                 i_channel_config, i_format );
387     if( i_min_buffer_size <= 0 )
388     {
389         msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ;
390         /* use a defaut min buffer size (shouldn't happen) */
391         i_min_buffer_size = i_nb_channels * i_format_size * 2024;
392     }
393
394     i_size = i_min_buffer_size * 2; // double buffering
395
396     p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC, i_rate,
397                                i_channel_config, i_format, i_size,
398                                jfields.AudioTrack.MODE_STREAM );
399     if( CHECK_EXCEPTION( "<init>" ) || !p_audiotrack )
400         return VLC_EGENERIC;
401     p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack );
402     (*env)->DeleteLocalRef( env, p_audiotrack );
403     if( !p_sys->p_audiotrack )
404         return VLC_EGENERIC;
405
406     p_sys->fmt.i_rate = i_rate;
407     p_sys->i_samples_written = 0;
408     p_sys->i_bytes_per_frame = i_nb_channels * i_format_size;
409
410     /* Gets the initial value of DAC samples counter */
411     JNIThread_TimeGet( env, p_error, p_aout, NULL );
412
413     JNI_AT_CALL_VOID( play );
414
415     return VLC_SUCCESS;
416 }
417
418 static void
419 JNIThread_Stop( JNIEnv *env, bool *p_error, audio_output_t *p_aout )
420 {
421     aout_sys_t *p_sys = p_aout->sys;
422
423     JNI_AT_CALL_VOID( stop );
424     CHECK_EXCEPTION( "stop" );
425
426     JNI_AT_CALL_VOID( release );
427     (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
428     p_sys->p_audiotrack = NULL;
429 }
430
431 static void
432 JNIThread_Play( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
433                 block_t *p_buffer )
434 {
435     aout_sys_t *p_sys = p_aout->sys;
436     int i_offset = 0;
437
438     /* check if we need to realloc a ByteArray */
439     if( p_buffer->i_buffer > p_sys->i_bytearray_size )
440     {
441         jbyteArray p_bytearray;
442
443         if( p_sys->p_bytearray )
444         {
445             (*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
446             p_sys->p_bytearray = NULL;
447         }
448
449         p_bytearray = (*env)->NewByteArray( env, p_buffer->i_buffer );
450         if( p_bytearray )
451         {
452             p_sys->p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );
453             (*env)->DeleteLocalRef( env, p_bytearray );
454         }
455         p_sys->i_bytearray_size = p_buffer->i_buffer;
456     }
457     if( !p_sys->p_bytearray )
458     {
459         *p_error = true;
460         return;
461     }
462
463     /* copy p_buffer in to ByteArray */
464     (*env)->SetByteArrayRegion( env, p_sys->p_bytearray, 0,
465                                 p_buffer->i_buffer,
466                                 (jbyte *)p_buffer->p_buffer);
467
468     while ( p_buffer->i_buffer > (unsigned int) i_offset )
469     {
470         int i_ret;
471
472         /* write ByteArray */
473         i_ret = JNI_AT_CALL_INT( write, p_sys->p_bytearray, i_offset,
474                                  p_buffer->i_buffer - i_offset);
475         if( i_ret < 0 ) {
476             if( jfields.AudioManager.has_ERROR_DEAD_OBJECT
477                 && i_ret == jfields.AudioManager.ERROR_DEAD_OBJECT )
478             {
479                 msg_Warn( p_aout, "ERROR_DEAD_OBJECT: "
480                                   "try recreating AudioTrack" );
481                 JNIThread_Stop( env, p_error, p_aout );
482                 i_ret = JNIThread_Start( env, p_error, p_aout );
483                 if( i_ret == VLC_SUCCESS )
484                     continue;
485             } else
486             {
487                 const char *str;
488                 if( i_ret == jfields.AudioTrack.ERROR_INVALID_OPERATION )
489                     str = "ERROR_INVALID_OPERATION";
490                 else if( i_ret == jfields.AudioTrack.ERROR_BAD_VALUE )
491                     str = "ERROR_BAD_VALUE";
492                 else
493                     str = "ERROR";
494                 msg_Err( p_aout, "Write failed: %s", str );
495             }
496             *p_error = true;
497             break;
498         }
499
500         p_sys->i_samples_written += i_ret / p_sys->i_bytes_per_frame;
501         i_offset += i_ret;
502     }
503 }
504
505 static void
506 JNIThread_Pause( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
507                  bool b_pause, mtime_t i_date )
508 {
509     VLC_UNUSED( i_date );
510
511     aout_sys_t *p_sys = p_aout->sys;
512
513     if( b_pause )
514     {
515         JNI_AT_CALL_VOID( pause );
516         CHECK_EXCEPTION( "pause" );
517     } else
518     {
519         JNI_AT_CALL_VOID( play );
520         CHECK_EXCEPTION( "play" );
521     }
522 }
523
524 static void
525 JNIThread_Flush( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
526                  bool b_wait )
527 {
528     aout_sys_t *p_sys = p_aout->sys;
529
530     /* Android doc:
531      * stop(): Stops playing the audio data. When used on an instance created
532      * in MODE_STREAM mode, audio will stop playing after the last buffer that
533      * was written has been played. For an immediate stop, use pause(),
534      * followed by flush() to discard audio data that hasn't been played back
535      * yet.
536      *
537      * flush(): Flushes the audio data currently queued for playback. Any data
538      * that has not been played back will be discarded.  No-op if not stopped
539      * or paused, or if the track's creation mode is not MODE_STREAM.
540      */
541     if( !p_sys->i_samples_written )
542         return;
543     if( b_wait )
544     {
545         JNI_AT_CALL_VOID( stop );
546         if( CHECK_EXCEPTION( "stop" ) )
547             return;
548     } else
549     {
550
551         JNI_AT_CALL_VOID( pause );
552         if( CHECK_EXCEPTION( "pause" ) )
553             return;
554         JNI_AT_CALL_VOID( flush );
555     }
556     p_sys->i_samples_written = 0;
557     JNI_AT_CALL_VOID( play );
558     CHECK_EXCEPTION( "play" );
559 }
560
561 static void *
562 JNIThread( void *data )
563 {
564     audio_output_t *p_aout = data;
565     aout_sys_t *p_sys = p_aout->sys;
566     bool b_error = false;
567     JNIEnv* env;
568
569     jni_attach_thread( &env, THREAD_NAME );
570
571     vlc_mutex_lock( &p_sys->mutex );
572     if( !env )
573         goto end;
574
575     while( p_sys->b_thread_run )
576     {
577         /* wait to process a command */
578         while( p_sys->b_thread_run && p_sys->p_cmd == NULL )
579             vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
580         if( !p_sys->b_thread_run || p_sys->p_cmd == NULL )
581             break;
582
583         /* process a command */
584         switch( p_sys->p_cmd->id )
585         {
586             case CMD_START:
587                 p_sys->fmt = *p_sys->p_cmd->in.start.p_fmt;
588                 p_sys->p_cmd->out.start.i_ret =
589                     JNIThread_Start( env, &b_error, p_aout );
590                 p_sys->p_cmd->out.start.p_fmt = &p_sys->fmt;
591                 break;
592             case CMD_STOP:
593                 JNIThread_Stop( env, &b_error, p_aout );
594                 break;
595             case CMD_PLAY:
596                 JNIThread_Play( env, &b_error, p_aout,
597                                  p_sys->p_cmd->in.play.p_buffer );
598                 break;
599             case CMD_PAUSE:
600                 JNIThread_Pause( env, &b_error, p_aout,
601                                  p_sys->p_cmd->in.pause.b_pause,
602                                  p_sys->p_cmd->in.pause.i_date );
603                 break;
604             case CMD_FLUSH:
605                 JNIThread_Flush( env, &b_error, p_aout,
606                                  p_sys->p_cmd->in.flush.b_wait );
607                 break;
608             case CMD_TIME_GET:
609                 p_sys->p_cmd->out.time_get.i_ret =
610                     JNIThread_TimeGet( env, &b_error, p_aout,
611                                        &p_sys->p_cmd->out.time_get.i_delay );
612                 break;
613             default:
614                 vlc_assert_unreachable();
615         }
616         if( b_error )
617             p_sys->b_thread_run = false;
618         p_sys->p_cmd->id = CMD_DONE;
619         p_sys->p_cmd = NULL;
620         /* signal that command is processed */
621         vlc_cond_signal( &p_sys->cond );
622     }
623 end:
624     if( env )
625     {
626         if( p_sys->p_bytearray )
627             (*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
628         jni_detach_thread();
629     }
630     p_sys->b_thread_run = false;
631     vlc_cond_signal( &p_sys->cond );
632     vlc_mutex_unlock( &p_sys->mutex );
633     return NULL;
634 }
635
636 static int
637 Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
638 {
639     int i_ret;
640     struct thread_cmd cmd;
641     aout_sys_t *p_sys = p_aout->sys;
642
643     vlc_mutex_lock( &p_sys->mutex );
644
645     assert( !p_sys->b_thread_run && p_sys->p_cmd == NULL );
646
647     /* create JNIThread */
648     p_sys->b_thread_run = true;
649     if( vlc_clone( &p_sys->thread,
650                    JNIThread, p_aout, VLC_THREAD_PRIORITY_AUDIO ) )
651     {
652         msg_Err( p_aout, "JNIThread creation failed" );
653         vlc_mutex_unlock( &p_sys->mutex );
654         return VLC_EGENERIC;
655     }
656
657     /* ask the thread to process the Start command */
658     cmd.id = CMD_START;
659     cmd.in.start.p_fmt = p_fmt;
660     p_sys->p_cmd = &cmd;
661     vlc_cond_signal( &p_sys->cond );
662
663     /* wait for the thread */
664     while( cmd.id != CMD_DONE && p_sys->b_thread_run )
665         vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
666
667     vlc_mutex_unlock( &p_sys->mutex );
668
669     /* retrieve results */
670     i_ret = cmd.out.start.i_ret;
671     if( i_ret == VLC_SUCCESS )
672     {
673         *p_fmt = *cmd.out.start.p_fmt;
674         aout_SoftVolumeStart( p_aout );
675     }
676
677     return i_ret;
678 }
679
680 static void
681 Stop( audio_output_t *p_aout )
682 {
683     aout_sys_t *p_sys = p_aout->sys;
684
685     vlc_mutex_lock( &p_sys->mutex );
686
687     assert( p_sys->p_cmd == NULL );
688
689     if( p_sys->b_thread_run )
690     {
691         struct thread_cmd cmd;
692
693         /* ask the thread to process the Stop command */
694         cmd.id = CMD_STOP;
695         p_sys->p_cmd = &cmd;
696         vlc_cond_signal( &p_sys->cond );
697
698         /* wait for the thread */
699         while( cmd.id != CMD_DONE && p_sys->b_thread_run )
700             vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
701
702         /* kill the thread */
703         p_sys->b_thread_run = false;
704         vlc_cond_signal( &p_sys->cond );
705     }
706     vlc_mutex_unlock( &p_sys->mutex );
707
708     vlc_join( p_sys->thread, NULL );
709 }
710
711 static void
712 Play( audio_output_t *p_aout, block_t *p_buffer )
713 {
714     aout_sys_t *p_sys = p_aout->sys;
715     vlc_mutex_lock( &p_sys->mutex );
716
717     assert( p_sys->p_cmd == NULL );
718
719     if( p_sys->b_thread_run )
720     {
721         struct thread_cmd cmd;
722
723         /* ask the thread to process the Play command */
724         cmd.id = CMD_PLAY;
725         cmd.in.play.p_buffer = p_buffer;
726         p_sys->p_cmd = &cmd;
727         vlc_cond_signal( &p_sys->cond );
728
729         /* wait for the thread */
730         while( cmd.id != CMD_DONE && p_sys->b_thread_run )
731             vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
732     }
733
734     vlc_mutex_unlock( &p_sys->mutex );
735
736     block_Release( p_buffer );
737 }
738
739 static void
740 Pause( audio_output_t *p_aout, bool b_pause, mtime_t i_date )
741 {
742     aout_sys_t *p_sys = p_aout->sys;
743
744     vlc_mutex_lock( &p_sys->mutex );
745
746     assert( p_sys->p_cmd == NULL );
747
748     if( p_sys->b_thread_run )
749     {
750         struct thread_cmd cmd;
751
752         /* ask the thread to process the Pause command */
753         cmd.id = CMD_PAUSE;
754         cmd.in.pause.b_pause = b_pause;
755         cmd.in.pause.i_date = i_date;
756         p_sys->p_cmd = &cmd;
757         vlc_cond_signal( &p_sys->cond );
758
759         /* wait for the thread */
760         while( cmd.id != CMD_DONE && p_sys->b_thread_run  )
761             vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
762     }
763
764     vlc_mutex_unlock( &p_sys->mutex );
765 }
766
767 static void
768 Flush ( audio_output_t *p_aout, bool b_wait )
769 {
770     aout_sys_t *p_sys = p_aout->sys;
771
772     vlc_mutex_lock( &p_sys->mutex );
773
774     assert( p_sys->p_cmd == NULL );
775
776     if( p_sys->b_thread_run )
777     {
778         struct thread_cmd cmd;
779
780         /* ask the thread to process the Flush command */
781         cmd.id = CMD_FLUSH;
782         cmd.in.flush.b_wait = b_wait;
783         p_sys->p_cmd = &cmd;
784         vlc_cond_signal( &p_sys->cond );
785
786         /* wait for the thread */
787         while( cmd.id != CMD_DONE && p_sys->b_thread_run  )
788             vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
789     }
790
791     vlc_mutex_unlock( &p_sys->mutex );
792 }
793
794 static int
795 TimeGet( audio_output_t *p_aout, mtime_t *restrict p_delay )
796 {
797     int i_ret = -1;
798     aout_sys_t *p_sys = p_aout->sys;
799
800     vlc_mutex_lock( &p_sys->mutex );
801
802     assert( p_sys->p_cmd == NULL );
803
804     if( p_sys->b_thread_run )
805     {
806         struct thread_cmd cmd;
807
808         /* ask the thread to process the TimeGet */
809         cmd.id = CMD_TIME_GET;
810         p_sys->p_cmd = &cmd;
811         vlc_cond_signal( &p_sys->cond );
812
813         /* wait for the thread */
814         while( cmd.id != CMD_DONE && p_sys->b_thread_run  )
815             vlc_cond_wait( &p_sys->cond, &p_sys->mutex );
816
817         /* retrieve results */
818         i_ret = cmd.out.time_get.i_ret;
819         *p_delay = cmd.out.time_get.i_delay;
820     }
821
822     vlc_mutex_unlock( &p_sys->mutex );
823
824     return i_ret;
825 }
826
827
828 static int
829 Open( vlc_object_t *obj )
830 {
831     audio_output_t *p_aout = (audio_output_t *) obj;
832     aout_sys_t *p_sys;
833
834     if( !InitJNIFields( p_aout ) )
835         return VLC_EGENERIC;
836
837     p_sys = calloc( 1, sizeof (aout_sys_t) );
838
839     if( unlikely( p_sys == NULL ) )
840         return VLC_ENOMEM;
841
842     vlc_mutex_init( &p_sys->mutex );
843     vlc_cond_init( &p_sys->cond );
844
845     p_aout->sys = p_sys;
846     p_aout->start = Start;
847     p_aout->stop = Stop;
848     p_aout->play = Play;
849     p_aout->pause = Pause;
850     p_aout->flush = Flush;
851     p_aout->time_get = TimeGet;
852
853     aout_SoftVolumeInit( p_aout );
854
855     return VLC_SUCCESS;
856 }
857
858 static void
859 Close( vlc_object_t *obj )
860 {
861     audio_output_t *p_aout = (audio_output_t *) obj;
862     aout_sys_t *p_sys = p_aout->sys;
863
864     vlc_mutex_destroy( &p_sys->mutex );
865     vlc_cond_destroy( &p_sys->cond );
866
867     free( p_sys );
868 }