1 /*****************************************************************************
2 * audiotrack.c: Android Java AudioTrack audio output module
3 *****************************************************************************
4 * Copyright © 2012-2015 VLC authors and VideoLAN, VideoLabs
6 * Authors: Thomas Guillem <thomas@gllm.fr>
7 * Ming Hu <tewilove@gmail.com>
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.
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.
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 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_threads.h>
37 static int Open( vlc_object_t * );
38 static void Close( vlc_object_t * );
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 */
54 /* JNIThread control */
58 bool b_thread_run; /* is thread alive */
59 struct thread_cmd *p_cmd; /* actual cmd process by JNIThread */
62 /* Soft volume helper */
63 #include "audio_output/volume.h"
65 //#define AUDIOTRACK_USE_FLOAT
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 )
74 add_shortcut( "audiotrack" )
75 set_callbacks( Open, Close )
90 audio_sample_format_t *p_fmt;
106 audio_sample_format_t *p_fmt;
115 #define THREAD_NAME "android_audiotrack"
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);
132 jmethodID getPlaybackHeadPosition;
133 jmethodID getMinBufferSize;
136 jint ERROR_BAD_VALUE;
137 jint ERROR_INVALID_OPERATION;
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;
148 jint ERROR_DEAD_OBJECT;
149 bool has_ERROR_DEAD_OBJECT;
154 /* init all jni fields.
155 * Done only one time during the first initialisation */
157 InitJNIFields( audio_output_t *p_aout )
159 static vlc_mutex_t lock = VLC_STATIC_MUTEX;
160 static int i_init_state = -1;
161 bool ret, b_attached = false;
166 vlc_mutex_lock( &lock );
168 if( i_init_state != -1 )
171 if( jni_get_env(&env) < 0 )
173 jni_attach_thread( &env, THREAD_NAME );
182 #define CHECK_EXCEPTION( what, critical ) do { \
183 if( (*env)->ExceptionOccurred( env ) ) \
185 msg_Err( p_aout, "%s failed", what ); \
186 (*env)->ExceptionClear( env ); \
194 #define GET_CLASS( str, critical ) do { \
195 clazz = (*env)->FindClass( env, (str) ); \
196 CHECK_EXCEPTION( str, critical ); \
198 #define GET_ID( get, id, str, args, critical ) do { \
199 jfields.id = (*env)->get( env, clazz, (str), (args) ); \
200 CHECK_EXCEPTION( #get, critical ); \
202 #define GET_CONST_INT( id, str, critical ) do { \
204 field = (*env)->GetStaticFieldID( env, clazz, (str), "I" ); \
205 CHECK_EXCEPTION( #id, critical ); \
208 jfields.id = (*env)->GetStaticIntField( env, clazz, field ); \
209 CHECK_EXCEPTION( #id, critical ); \
213 GET_CLASS( "android/media/AudioTrack", true );
214 jfields.AudioTrack.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );
215 CHECK_EXCEPTION( "NewGlobalRef", true );
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",
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 );
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",
240 jfields.AudioFormat.has_ENCODING_PCM_FLOAT = field != NULL;
242 jfields.AudioFormat.has_ENCODING_PCM_FLOAT = false;
244 GET_CONST_INT( AudioFormat.CHANNEL_OUT_MONO, "CHANNEL_OUT_MONO", true );
245 GET_CONST_INT( AudioFormat.CHANNEL_OUT_STEREO, "CHANNEL_OUT_STEREO", true );
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 );
252 #undef CHECK_EXCEPTION
259 ret = i_init_state == 1;
261 msg_Err( p_aout, "AudioTrack jni init failed" );
264 vlc_mutex_unlock( &lock );
269 check_exception( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
272 if( (*env)->ExceptionOccurred( env ) )
274 (*env)->ExceptionClear( env );
276 msg_Err( p_aout, "AudioTrack.%s triggered an exception !", method );
281 #define CHECK_EXCEPTION( method ) check_exception( env, p_error, p_aout, method )
283 #define JNI_CALL( what, obj, method, ... ) (*env)->what( env, obj, method, ##__VA_ARGS__ )
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__ )
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__ )
295 JNIThread_TimeGet( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
298 VLC_UNUSED( p_error );
299 aout_sys_t *p_sys = p_aout->sys;
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().
312 dsp = (uint32_t )JNI_AT_CALL_INT( getPlaybackHeadPosition );
314 if( p_sys->i_samples_written == 0 ) {
315 p_sys->i_dsp_initial = dsp;
319 dsp -= p_sys->i_dsp_initial;
324 *p_delay = ((mtime_t)p_sys->i_samples_written - dsp) *
325 CLOCK_FREQ / p_sys->fmt.i_rate;
331 JNIThread_Start( JNIEnv *env, bool *p_error, audio_output_t *p_aout )
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;
338 /* 4000 <= frequency <= 48000 */
339 i_rate = p_sys->fmt.i_rate;
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;
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;
355 if( p_sys->fmt.i_format == VLC_CODEC_S16N )
357 i_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
359 } else if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
361 i_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
365 i_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
368 p_sys->fmt.i_original_channels = p_sys->fmt.i_physical_channels;
370 i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
371 switch( i_nb_channels )
374 i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;
375 p_sys->fmt.i_physical_channels = AOUT_CHAN_CENTER;
378 i_nb_channels = 2; // XXX: AudioTrack handle only stereo for now
380 i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
381 p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
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 )
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;
394 i_size = i_min_buffer_size * 2; // double buffering
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 )
401 p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack );
402 (*env)->DeleteLocalRef( env, p_audiotrack );
403 if( !p_sys->p_audiotrack )
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;
410 /* Gets the initial value of DAC samples counter */
411 JNIThread_TimeGet( env, p_error, p_aout, NULL );
413 JNI_AT_CALL_VOID( play );
419 JNIThread_Stop( JNIEnv *env, bool *p_error, audio_output_t *p_aout )
421 aout_sys_t *p_sys = p_aout->sys;
423 JNI_AT_CALL_VOID( stop );
424 CHECK_EXCEPTION( "stop" );
426 JNI_AT_CALL_VOID( release );
427 (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
428 p_sys->p_audiotrack = NULL;
432 JNIThread_Play( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
435 aout_sys_t *p_sys = p_aout->sys;
438 /* check if we need to realloc a ByteArray */
439 if( p_buffer->i_buffer > p_sys->i_bytearray_size )
441 jbyteArray p_bytearray;
443 if( p_sys->p_bytearray )
445 (*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
446 p_sys->p_bytearray = NULL;
449 p_bytearray = (*env)->NewByteArray( env, p_buffer->i_buffer );
452 p_sys->p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );
453 (*env)->DeleteLocalRef( env, p_bytearray );
455 p_sys->i_bytearray_size = p_buffer->i_buffer;
457 if( !p_sys->p_bytearray )
463 /* copy p_buffer in to ByteArray */
464 (*env)->SetByteArrayRegion( env, p_sys->p_bytearray, 0,
466 (jbyte *)p_buffer->p_buffer);
468 while ( p_buffer->i_buffer > (unsigned int) i_offset )
472 /* write ByteArray */
473 i_ret = JNI_AT_CALL_INT( write, p_sys->p_bytearray, i_offset,
474 p_buffer->i_buffer - i_offset);
476 if( jfields.AudioManager.has_ERROR_DEAD_OBJECT
477 && i_ret == jfields.AudioManager.ERROR_DEAD_OBJECT )
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 )
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";
494 msg_Err( p_aout, "Write failed: %s", str );
500 p_sys->i_samples_written += i_ret / p_sys->i_bytes_per_frame;
506 JNIThread_Pause( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
507 bool b_pause, mtime_t i_date )
509 VLC_UNUSED( i_date );
511 aout_sys_t *p_sys = p_aout->sys;
515 JNI_AT_CALL_VOID( pause );
516 CHECK_EXCEPTION( "pause" );
519 JNI_AT_CALL_VOID( play );
520 CHECK_EXCEPTION( "play" );
525 JNIThread_Flush( JNIEnv *env, bool *p_error, audio_output_t *p_aout,
528 aout_sys_t *p_sys = p_aout->sys;
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
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.
541 if( !p_sys->i_samples_written )
545 JNI_AT_CALL_VOID( stop );
546 if( CHECK_EXCEPTION( "stop" ) )
551 JNI_AT_CALL_VOID( pause );
552 if( CHECK_EXCEPTION( "pause" ) )
554 JNI_AT_CALL_VOID( flush );
556 p_sys->i_samples_written = 0;
557 JNI_AT_CALL_VOID( play );
558 CHECK_EXCEPTION( "play" );
562 JNIThread( void *data )
564 audio_output_t *p_aout = data;
565 aout_sys_t *p_sys = p_aout->sys;
566 bool b_error = false;
569 jni_attach_thread( &env, THREAD_NAME );
571 vlc_mutex_lock( &p_sys->mutex );
575 while( p_sys->b_thread_run )
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 )
583 /* process a command */
584 switch( p_sys->p_cmd->id )
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;
593 JNIThread_Stop( env, &b_error, p_aout );
596 JNIThread_Play( env, &b_error, p_aout,
597 p_sys->p_cmd->in.play.p_buffer );
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 );
605 JNIThread_Flush( env, &b_error, p_aout,
606 p_sys->p_cmd->in.flush.b_wait );
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 );
614 vlc_assert_unreachable();
617 p_sys->b_thread_run = false;
618 p_sys->p_cmd->id = CMD_DONE;
620 /* signal that command is processed */
621 vlc_cond_signal( &p_sys->cond );
626 if( p_sys->p_bytearray )
627 (*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
630 p_sys->b_thread_run = false;
631 vlc_cond_signal( &p_sys->cond );
632 vlc_mutex_unlock( &p_sys->mutex );
637 Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
640 struct thread_cmd cmd;
641 aout_sys_t *p_sys = p_aout->sys;
643 vlc_mutex_lock( &p_sys->mutex );
645 assert( !p_sys->b_thread_run && p_sys->p_cmd == NULL );
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 ) )
652 msg_Err( p_aout, "JNIThread creation failed" );
653 vlc_mutex_unlock( &p_sys->mutex );
657 /* ask the thread to process the Start command */
659 cmd.in.start.p_fmt = p_fmt;
661 vlc_cond_signal( &p_sys->cond );
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 );
667 vlc_mutex_unlock( &p_sys->mutex );
669 /* retrieve results */
670 i_ret = cmd.out.start.i_ret;
671 if( i_ret == VLC_SUCCESS )
673 *p_fmt = *cmd.out.start.p_fmt;
674 aout_SoftVolumeStart( p_aout );
681 Stop( audio_output_t *p_aout )
683 aout_sys_t *p_sys = p_aout->sys;
685 vlc_mutex_lock( &p_sys->mutex );
687 assert( p_sys->p_cmd == NULL );
689 if( p_sys->b_thread_run )
691 struct thread_cmd cmd;
693 /* ask the thread to process the Stop command */
696 vlc_cond_signal( &p_sys->cond );
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 );
702 /* kill the thread */
703 p_sys->b_thread_run = false;
704 vlc_cond_signal( &p_sys->cond );
706 vlc_mutex_unlock( &p_sys->mutex );
708 vlc_join( p_sys->thread, NULL );
712 Play( audio_output_t *p_aout, block_t *p_buffer )
714 aout_sys_t *p_sys = p_aout->sys;
715 vlc_mutex_lock( &p_sys->mutex );
717 assert( p_sys->p_cmd == NULL );
719 if( p_sys->b_thread_run )
721 struct thread_cmd cmd;
723 /* ask the thread to process the Play command */
725 cmd.in.play.p_buffer = p_buffer;
727 vlc_cond_signal( &p_sys->cond );
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 );
734 vlc_mutex_unlock( &p_sys->mutex );
736 block_Release( p_buffer );
740 Pause( audio_output_t *p_aout, bool b_pause, mtime_t i_date )
742 aout_sys_t *p_sys = p_aout->sys;
744 vlc_mutex_lock( &p_sys->mutex );
746 assert( p_sys->p_cmd == NULL );
748 if( p_sys->b_thread_run )
750 struct thread_cmd cmd;
752 /* ask the thread to process the Pause command */
754 cmd.in.pause.b_pause = b_pause;
755 cmd.in.pause.i_date = i_date;
757 vlc_cond_signal( &p_sys->cond );
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 );
764 vlc_mutex_unlock( &p_sys->mutex );
768 Flush ( audio_output_t *p_aout, bool b_wait )
770 aout_sys_t *p_sys = p_aout->sys;
772 vlc_mutex_lock( &p_sys->mutex );
774 assert( p_sys->p_cmd == NULL );
776 if( p_sys->b_thread_run )
778 struct thread_cmd cmd;
780 /* ask the thread to process the Flush command */
782 cmd.in.flush.b_wait = b_wait;
784 vlc_cond_signal( &p_sys->cond );
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 );
791 vlc_mutex_unlock( &p_sys->mutex );
795 TimeGet( audio_output_t *p_aout, mtime_t *restrict p_delay )
798 aout_sys_t *p_sys = p_aout->sys;
800 vlc_mutex_lock( &p_sys->mutex );
802 assert( p_sys->p_cmd == NULL );
804 if( p_sys->b_thread_run )
806 struct thread_cmd cmd;
808 /* ask the thread to process the TimeGet */
809 cmd.id = CMD_TIME_GET;
811 vlc_cond_signal( &p_sys->cond );
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 );
817 /* retrieve results */
818 i_ret = cmd.out.time_get.i_ret;
819 *p_delay = cmd.out.time_get.i_delay;
822 vlc_mutex_unlock( &p_sys->mutex );
829 Open( vlc_object_t *obj )
831 audio_output_t *p_aout = (audio_output_t *) obj;
834 if( !InitJNIFields( p_aout ) )
837 p_sys = calloc( 1, sizeof (aout_sys_t) );
839 if( unlikely( p_sys == NULL ) )
842 vlc_mutex_init( &p_sys->mutex );
843 vlc_cond_init( &p_sys->cond );
846 p_aout->start = Start;
849 p_aout->pause = Pause;
850 p_aout->flush = Flush;
851 p_aout->time_get = TimeGet;
853 aout_SoftVolumeInit( p_aout );
859 Close( vlc_object_t *obj )
861 audio_output_t *p_aout = (audio_output_t *) obj;
862 aout_sys_t *p_sys = p_aout->sys;
864 vlc_mutex_destroy( &p_sys->mutex );
865 vlc_cond_destroy( &p_sys->cond );