static int Open( vlc_object_t * );
static void Close( vlc_object_t * );
+static void JNIThread_Stop( JNIEnv *env, audio_output_t *p_aout );
struct thread_cmd;
typedef TAILQ_HEAD(, thread_cmd) THREAD_CMD_QUEUE;
jobject p_audioTimestamp; /* AudioTimestamp ref */
jbyteArray p_bytearray; /* ByteArray ref (for Write) */
size_t i_bytearray_size; /* size of the ByteArray */
+ jfloatArray p_floatarray; /* FloatArray ref (for WriteFloat) */
+ size_t i_floatarray_size; /* size of the FloatArray */
jobject p_bytebuffer; /* ByteBuffer ref (for WriteV21) */
audio_sample_format_t fmt; /* fmt setup by Start */
uint32_t i_pos_initial; /* initial position set by getPlaybackHeadPosition */
mtime_t i_play_time; /* time when play was called */
bool b_audiotrack_exception; /* true if audiotrack throwed an exception */
int i_audiotrack_stuck_count;
+ uint8_t i_chans_to_reorder; /* do we need channel reordering */
+ uint8_t p_chan_table[AOUT_CHAN_MAX];
+ enum {
+ WRITE,
+ WRITE_V21,
+ WRITE_FLOAT
+ } i_write_type;
/* JNIThread control */
vlc_mutex_t mutex;
/* Soft volume helper */
#include "audio_output/volume.h"
-//#define AUDIOTRACK_USE_FLOAT
+#define AUDIOTRACK_USE_FLOAT
// TODO: activate getTimestamp for new android versions
//#define AUDIOTRACK_USE_TIMESTAMP
jmethodID pause;
jmethodID write;
jmethodID writeV21;
+ jmethodID writeFloat;
jmethodID getPlaybackHeadPosition;
jmethodID getTimestamp;
jmethodID getMinBufferSize;
bool has_ENCODING_PCM_FLOAT;
jint CHANNEL_OUT_MONO;
jint CHANNEL_OUT_STEREO;
+ jint CHANNEL_OUT_FRONT_LEFT;
+ jint CHANNEL_OUT_FRONT_RIGHT;
+ 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;
} AudioFormat;
struct {
jint ERROR_DEAD_OBJECT;
GET_ID( GetMethodID, AudioTrack.writeV21, "write", "(Ljava/nio/ByteBuffer;II)I", false );
if( jfields.AudioTrack.writeV21 )
{
- jfields.AudioTrack.write = NULL;
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 );
"framePosition", "J", true );
GET_ID( GetFieldID, AudioTimestamp.nanoTime,
"nanoTime", "J", true );
- } else
- {
- jfields.AudioTimestamp.clazz = NULL;
- jfields.AudioTimestamp.ctor = NULL;
- jfields.AudioTimestamp.framePosition = NULL;
- jfields.AudioTimestamp.nanoTime = NULL;
}
/* AudioFormat class init */
#ifdef AUDIOTRACK_USE_FLOAT
GET_CONST_INT( AudioFormat.ENCODING_PCM_FLOAT, "ENCODING_PCM_FLOAT",
false );
- jfields.AudioFormat.has_ENCODING_PCM_FLOAT = field != NULL;
+ jfields.AudioFormat.has_ENCODING_PCM_FLOAT = field != NULL &&
+ jfields.AudioTrack.writeFloat;
#else
jfields.AudioFormat.has_ENCODING_PCM_FLOAT = false;
#endif
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 );
return -1;
}
-static int
-JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
+static void
+AudioTrack_GetChanOrder( uint16_t i_physical_channels, uint32_t p_chans_out[] )
+{
+#define HAS_CHAN( x ) ( ( i_physical_channels & (x) ) == (x) )
+ /* samples will be in the following order: FL FR FC LFE BL BR BC SL SR */
+ int i = 0;
+
+ if( HAS_CHAN( AOUT_CHAN_LEFT ) )
+ p_chans_out[i++] = AOUT_CHAN_LEFT;
+ if( HAS_CHAN( AOUT_CHAN_RIGHT ) )
+ p_chans_out[i++] = AOUT_CHAN_RIGHT;
+
+ if( HAS_CHAN( AOUT_CHAN_CENTER ) )
+ p_chans_out[i++] = AOUT_CHAN_CENTER;
+
+ if( HAS_CHAN( AOUT_CHAN_LFE ) )
+ p_chans_out[i++] = AOUT_CHAN_LFE;
+
+ if( HAS_CHAN( AOUT_CHAN_REARLEFT ) )
+ p_chans_out[i++] = AOUT_CHAN_REARLEFT;
+ if( HAS_CHAN( AOUT_CHAN_REARRIGHT ) )
+ p_chans_out[i++] = AOUT_CHAN_REARRIGHT;
+
+ if( HAS_CHAN( AOUT_CHAN_REARCENTER ) )
+ p_chans_out[i++] = AOUT_CHAN_REARCENTER;
+
+ 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;
+
+ assert( i <= AOUT_CHAN_MAX );
+#undef HAS_CHAN
+}
+
+/**
+ * Configure and create an Android AudioTrack.
+ * returns NULL on configuration error
+ */
+static jobject
+JNIThread_NewAudioTrack( JNIEnv *env, audio_output_t *p_aout,
+ unsigned int i_rate,
+ vlc_fourcc_t i_vlc_format,
+ uint16_t i_physical_channels,
+ int *p_audiotrack_size )
{
- struct aout_sys_t *p_sys = p_aout->sys;
- int i_size, i_min_buffer_size, i_channel_config, i_rate, i_format,
- i_format_size, i_nb_channels;
+ int i_size, i_min_buffer_size, i_channel_config, i_format;
jobject p_audiotrack;
- /* 4000 <= frequency <= 48000 */
- i_rate = p_sys->fmt.i_rate;
- if( i_rate < 4000 )
- i_rate = 4000;
- if( i_rate > 48000 )
- i_rate = 48000;
-
- /* We can only accept U8, S16N, and FL32 (depending on Android version) */
- if( p_sys->fmt.i_format != VLC_CODEC_U8
- && p_sys->fmt.i_format != VLC_CODEC_S16N
- && p_sys->fmt.i_format != VLC_CODEC_FL32 )
- p_sys->fmt.i_format = VLC_CODEC_S16N;
-
- if( p_sys->fmt.i_format == VLC_CODEC_FL32
- && !jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
- p_sys->fmt.i_format = VLC_CODEC_S16N;
-
- if( p_sys->fmt.i_format == VLC_CODEC_S16N )
- {
- i_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
- i_format_size = 2;
- } else if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
+ switch( i_vlc_format )
{
- i_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
- i_format_size = 4;
- } else
- {
- i_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
- i_format_size = 1;
+ case VLC_CODEC_U8:
+ i_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
+ break;
+ case VLC_CODEC_S16N:
+ i_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
+ break;
+ case VLC_CODEC_FL32:
+ i_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
+ break;
+ default:
+ vlc_assert_unreachable();
}
- p_sys->fmt.i_original_channels = p_sys->fmt.i_physical_channels;
- i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
- switch( i_nb_channels )
+ switch( i_physical_channels )
{
- case 1:
- i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;
- p_sys->fmt.i_physical_channels = AOUT_CHAN_CENTER;
- break;
- default:
- i_nb_channels = 2; // XXX: AudioTrack handle only stereo for now
- case 2:
- i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
- p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
- break;
+ case AOUT_CHANS_7_1:
+ /* bitmask of CHANNEL_OUT_7POINT1 doesn't correspond to 5POINT1 and
+ * SIDES */
+ i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1 |
+ jfields.AudioFormat.CHANNEL_OUT_SIDE_LEFT |
+ jfields.AudioFormat.CHANNEL_OUT_SIDE_RIGHT;
+ break;
+ case AOUT_CHANS_5_1:
+ i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1;
+ break;
+ case AOUT_CHAN_LEFT:
+ i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;
+ break;
+ default:
+ case AOUT_CHANS_STEREO:
+ i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
+ break;
}
i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, i_rate,
if( i_min_buffer_size <= 0 )
{
msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ;
- /* use a defaut min buffer size (shouldn't happen) */
- i_min_buffer_size = i_nb_channels * i_format_size * 2048;
+ return NULL;
}
-
i_size = i_min_buffer_size * 4;
- p_sys->i_bytes_per_frame = i_nb_channels * i_format_size;
- p_sys->i_max_audiotrack_samples = i_size / p_sys->i_bytes_per_frame;
/* create AudioTrack object */
p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC, i_rate,
i_channel_config, i_format, i_size,
jfields.AudioTrack.MODE_STREAM );
if( CHECK_AT_EXCEPTION( "AudioTrack<init>" ) || !p_audiotrack )
+ {
+ msg_Warn( p_aout, "AudioTrack Init failed" ) ;
+ return NULL;
+ }
+ 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 NULL;
+ }
+ *p_audiotrack_size = i_size;
+
+ return p_audiotrack;
+}
+
+static int
+JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
+{
+ aout_sys_t *p_sys = p_aout->sys;
+ jobject p_audiotrack = NULL;
+ int i_nb_channels, i_audiotrack_size;
+ uint32_t p_chans_out[AOUT_CHAN_MAX];
+
+ aout_FormatPrint( p_aout, "VLC is looking for:", &p_sys->fmt );
+
+ p_sys->fmt.i_original_channels = p_sys->fmt.i_physical_channels;
+
+ /* 4000 <= frequency <= 48000 */
+ p_sys->fmt.i_rate = VLC_CLIP( p_sys->fmt.i_rate, 4000, 48000 );
+
+ /* We can only accept U8, S16N, FL32, and AC3 */
+ switch( p_sys->fmt.i_format )
+ {
+ case VLC_CODEC_U8:
+ break;
+ case VLC_CODEC_S16N:
+ break;
+ case VLC_CODEC_FL32:
+ if( !jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
+ p_sys->fmt.i_format = VLC_CODEC_S16N;
+ break;
+ default:
+ p_sys->fmt.i_format = VLC_CODEC_S16N;
+ break;
+ }
+
+ /* 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
+ */
+ i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
+ 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;
+ }
+ i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
+
+ do
+ {
+ /* Try to create an AudioTrack with the most advanced channel and
+ * format configuration. If NewAudioTrack fails, try again with a less
+ * advanced format (PCM S16N). If it fails again, try again with Stereo
+ * channels. */
+ p_audiotrack = JNIThread_NewAudioTrack( env, p_aout, p_sys->fmt.i_rate,
+ p_sys->fmt.i_format,
+ p_sys->fmt.i_physical_channels,
+ &i_audiotrack_size );
+ if( !p_audiotrack )
+ {
+ if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
+ {
+ msg_Warn( p_aout, "FL32 configuration failed, "
+ "fallback to S16N PCM" );
+ p_sys->fmt.i_format = VLC_CODEC_S16N;
+ }
+ else if( i_nb_channels > 5 )
+ {
+ msg_Warn( p_aout, "5.1 or 7.1 configuration failed, "
+ "fallback to Stereo" );
+ p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
+ i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
+ }
+ else
+ break;
+ }
+ } while( !p_audiotrack );
+
+ if( !p_audiotrack )
return VLC_EGENERIC;
+
p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack );
(*env)->DeleteLocalRef( env, p_audiotrack );
if( !p_sys->p_audiotrack )
return VLC_EGENERIC;
- if( JNI_AT_CALL_INT( getState ) != jfields.AudioTrack.STATE_INITIALIZED )
- {
- msg_Err( p_aout, "AudioTrack init failed" );
- goto error;
- }
+
+ 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 );
+
+ p_sys->i_bytes_per_frame = i_nb_channels *
+ aout_BitsPerSample( p_sys->fmt.i_format ) /
+ 8;
+ p_sys->i_max_audiotrack_samples = i_audiotrack_size /
+ p_sys->i_bytes_per_frame;
#ifdef AUDIOTRACK_USE_TIMESTAMP
if( jfields.AudioTimestamp.clazz )
jfields.AudioTimestamp.clazz,
jfields.AudioTimestamp.ctor );
if( !p_audioTimestamp )
- goto error;
+ {
+ JNIThread_Stop( env, p_aout );
+ return VLC_EGENERIC;
+ }
p_sys->p_audioTimestamp = (*env)->NewGlobalRef( env, p_audioTimestamp );
(*env)->DeleteLocalRef( env, p_audioTimestamp );
if( !p_sys->p_audioTimestamp )
- goto error;
+ {
+ JNIThread_Stop( env, p_aout );
+ return VLC_EGENERIC;
+ }
}
#endif
- p_sys->fmt.i_rate = i_rate;
+ if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
+ {
+ msg_Dbg( p_aout, "using WRITE_FLOAT");
+ p_sys->i_write_type = WRITE_FLOAT;
+ }
+ else if( jfields.AudioTrack.writeV21 )
+ msg_Dbg( p_aout, "using WRITE_V21");
+ p_sys->i_write_type = WRITE_V21;
+ }
+ else
+ {
+ msg_Dbg( p_aout, "using WRITE");
+ p_sys->i_write_type = WRITE;
+ }
JNI_AT_CALL_VOID( play );
CHECK_AT_EXCEPTION( "play" );
p_sys->i_play_time = mdate();
+ aout_FormatPrint( p_aout, "VLC will output:", &p_sys->fmt );
+
return VLC_SUCCESS;
-error:
- if( p_sys->p_audiotrack )
- {
- JNI_AT_CALL_VOID( release );
- (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
- p_sys->p_audiotrack = NULL;
- }
- return VLC_EGENERIC;
}
static void
{
aout_sys_t *p_sys = p_aout->sys;
- if( !p_sys->b_audiotrack_exception )
+ if( p_sys->p_audiotrack )
{
- JNI_AT_CALL_VOID( stop );
- if( !CHECK_AT_EXCEPTION( "stop" ) )
- JNI_AT_CALL_VOID( release );
+ if( !p_sys->b_audiotrack_exception )
+ {
+ JNI_AT_CALL_VOID( stop );
+ if( !CHECK_AT_EXCEPTION( "stop" ) )
+ JNI_AT_CALL_VOID( release );
+ }
+ (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
+ p_sys->p_audiotrack = NULL;
}
p_sys->b_audiotrack_exception = false;
- (*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
- p_sys->p_audiotrack = NULL;
if( p_sys->p_audioTimestamp )
{
* that we won't wait in AudioTrack.write() method
*/
static int
-JNIThread_Write( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer )
+JNIThread_Write( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer,
+ size_t i_buffer_offset )
{
aout_sys_t *p_sys = p_aout->sys;
- uint8_t *p_data = p_buffer->p_buffer;
size_t i_data;
uint32_t i_samples;
uint32_t i_audiotrack_pos;
uint32_t i_samples_pending;
+ i_data = p_buffer->i_buffer - i_buffer_offset;
i_audiotrack_pos = JNIThread_GetAudioTrackPos( env, p_aout );
if( i_audiotrack_pos > p_sys->i_samples_written )
{
} else
p_sys->i_audiotrack_stuck_count = 0;
i_samples = __MIN( p_sys->i_max_audiotrack_samples - i_samples_pending,
- p_buffer->i_nb_samples );
+ BYTES_TO_FRAMES( i_data ) );
i_data = i_samples * p_sys->i_bytes_per_frame;
- /* check if we need to realloc a ByteArray */
- if( i_data > p_sys->i_bytearray_size )
- {
- jbyteArray p_bytearray;
-
- if( p_sys->p_bytearray )
- {
- (*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
- p_sys->p_bytearray = NULL;
- }
-
- p_bytearray = (*env)->NewByteArray( env, i_data );
- if( p_bytearray )
- {
- p_sys->p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );
- (*env)->DeleteLocalRef( env, p_bytearray );
- }
- p_sys->i_bytearray_size = i_data;
- }
- if( !p_sys->p_bytearray )
- return jfields.AudioTrack.ERROR_BAD_VALUE;
-
- /* copy p_buffer in to ByteArray */
- (*env)->SetByteArrayRegion( env, p_sys->p_bytearray, 0, i_data,
- (jbyte *)p_data);
-
- return JNI_AT_CALL_INT( write, p_sys->p_bytearray, 0, i_data );
+ return JNI_AT_CALL_INT( write, p_sys->p_bytearray,
+ i_buffer_offset, i_data );
}
/**
* It calls a new write method with WRITE_NON_BLOCKING flags.
*/
static int
-JNIThread_WriteV21( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer )
+JNIThread_WriteV21( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer,
+ size_t i_buffer_offset )
{
aout_sys_t *p_sys = p_aout->sys;
int i_ret;
+ size_t i_data = p_buffer->i_buffer - i_buffer_offset;
+ uint8_t *p_data = p_buffer->p_buffer + i_buffer_offset;
if( !p_sys->p_bytebuffer )
{
jobject p_bytebuffer;
- p_bytebuffer = (*env)->NewDirectByteBuffer( env, p_buffer->p_buffer,
- p_buffer->i_buffer );
+ p_bytebuffer = (*env)->NewDirectByteBuffer( env, p_data, i_data );
if( !p_bytebuffer )
return jfields.AudioTrack.ERROR_BAD_VALUE;
}
}
- i_ret = JNI_AT_CALL_INT( writeV21, p_sys->p_bytebuffer, p_buffer->i_buffer,
+ i_ret = JNI_AT_CALL_INT( writeV21, p_sys->p_bytebuffer, i_data,
jfields.AudioTrack.WRITE_NON_BLOCKING );
if( i_ret > 0 )
{
return i_ret;
}
+/**
+ * Non blocking write float function for Lollipop and after.
+ * It calls a new write method with WRITE_NON_BLOCKING flags.
+ */
static int
-JNIThread_Play( JNIEnv *env, audio_output_t *p_aout,
- block_t **pp_buffer, mtime_t *p_wait )
+JNIThread_WriteFloat( JNIEnv *env, audio_output_t *p_aout, block_t *p_buffer,
+ size_t i_buffer_offset )
{
aout_sys_t *p_sys = p_aout->sys;
- block_t *p_buffer = *pp_buffer;
int i_ret;
+ size_t i_data;
- if( jfields.AudioTrack.writeV21 )
- i_ret = JNIThread_WriteV21( env, p_aout, p_buffer );
+ i_buffer_offset /= 4;
+ i_data = p_buffer->i_buffer / 4 - i_buffer_offset;
+
+ i_ret = JNI_AT_CALL_INT( writeFloat, p_sys->p_floatarray,
+ i_buffer_offset, i_data,
+ jfields.AudioTrack.WRITE_NON_BLOCKING );
+ if( i_ret < 0 )
+ return i_ret;
else
- i_ret = JNIThread_Write( env, p_aout, p_buffer );
+ return i_ret * 4;
+}
+
+static int
+JNIThread_PreparePlay( JNIEnv *env, audio_output_t *p_aout,
+ block_t *p_buffer )
+{
+ aout_sys_t *p_sys = p_aout->sys;
+
+ if( p_sys->i_chans_to_reorder )
+ aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
+ p_sys->i_chans_to_reorder, p_sys->p_chan_table,
+ p_sys->fmt.i_format );
+
+ switch( p_sys->i_write_type )
+ {
+ case WRITE:
+ /* check if we need to realloc a ByteArray */
+ if( p_buffer->i_buffer > p_sys->i_bytearray_size )
+ {
+ jbyteArray p_bytearray;
+
+ if( p_sys->p_bytearray )
+ {
+ (*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
+ p_sys->p_bytearray = NULL;
+ }
+
+ p_bytearray = (*env)->NewByteArray( env, p_buffer->i_buffer );
+ if( p_bytearray )
+ {
+ p_sys->p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );
+ (*env)->DeleteLocalRef( env, p_bytearray );
+ }
+ p_sys->i_bytearray_size = p_buffer->i_buffer;
+ }
+ if( !p_sys->p_bytearray )
+ return VLC_EGENERIC;
+
+ /* copy p_buffer in to ByteArray */
+ (*env)->SetByteArrayRegion( env, p_sys->p_bytearray, 0,
+ p_buffer->i_buffer,
+ (jbyte *)p_buffer->p_buffer);
+ break;
+ case WRITE_FLOAT:
+ {
+ size_t i_data = p_buffer->i_buffer / 4;
+
+ /* check if we need to realloc a floatArray */
+ if( i_data > p_sys->i_floatarray_size )
+ {
+ jfloatArray p_floatarray;
+
+ if( p_sys->p_floatarray )
+ {
+ (*env)->DeleteGlobalRef( env, p_sys->p_floatarray );
+ p_sys->p_floatarray = NULL;
+ }
+
+ p_floatarray = (*env)->NewFloatArray( env, i_data );
+ if( p_floatarray )
+ {
+ p_sys->p_floatarray = (*env)->NewGlobalRef( env, p_floatarray );
+ (*env)->DeleteLocalRef( env, p_floatarray );
+ }
+ p_sys->i_floatarray_size = i_data;
+ }
+ if( !p_sys->p_floatarray )
+ return VLC_EGENERIC;
+
+ /* copy p_buffer in to FloatArray */
+ (*env)->SetFloatArrayRegion( env, p_sys->p_floatarray, 0, i_data,
+ (jfloat *)p_buffer->p_buffer);
+
+ break;
+ }
+ case WRITE_V21:
+ break;
+ }
+
+ return VLC_SUCCESS;
+}
+
+static int
+JNIThread_Play( JNIEnv *env, audio_output_t *p_aout,
+ block_t *p_buffer, size_t *p_buffer_offset, mtime_t *p_wait )
+{
+ aout_sys_t *p_sys = p_aout->sys;
+ int i_ret;
+
+ switch( p_sys->i_write_type )
+ {
+ case WRITE_V21:
+ i_ret = JNIThread_WriteV21( env, p_aout, p_buffer, *p_buffer_offset );
+ break;
+ case WRITE:
+ i_ret = JNIThread_Write( env, p_aout, p_buffer, *p_buffer_offset );
+ break;
+ case WRITE_FLOAT:
+ i_ret = JNIThread_WriteFloat( env, p_aout, p_buffer, *p_buffer_offset );
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
if( i_ret < 0 ) {
if( jfields.AudioManager.has_ERROR_DEAD_OBJECT
p_sys->i_samples_queued -= i_samples;
p_sys->i_samples_written += i_samples;
- p_buffer->p_buffer += i_ret;
- p_buffer->i_buffer -= i_ret;
- p_buffer->i_nb_samples -= i_samples;
- if( p_buffer->i_buffer == 0 )
- *pp_buffer = NULL;
+ *p_buffer_offset += i_ret;
/* HACK: There is a known issue in audiotrack, "due to an internal
* timeout within the AudioTrackThread". It happens after android
* write. This hack is done only for API 19 (AudioTimestamp was added
* in API 19). */
- if( jfields.AudioTimestamp.clazz && !jfields.AudioTrack.writeV21 )
+ if( p_sys->i_write_type == WRITE && jfields.AudioTimestamp.clazz )
*p_wait = FRAMES_TO_US( i_samples ) / 2;
}
return i_ret >= 0 ? VLC_SUCCESS : VLC_EGENERIC;
bool b_error = false;
bool b_paused = false;
block_t *p_buffer = NULL;
+ size_t i_buffer_offset = 0;
mtime_t i_play_deadline = 0;
JNIEnv* env;
if( b_error )
break;
if( p_buffer == NULL )
+ {
p_buffer = p_cmd->in.play.p_buffer;
- b_error = JNIThread_Play( env, p_aout, &p_buffer,
+ b_error = JNIThread_PreparePlay( env, p_aout, p_buffer )
+ != VLC_SUCCESS;
+ }
+ if( b_error )
+ break;
+ b_error = JNIThread_Play( env, p_aout, p_buffer,
+ &i_buffer_offset,
&i_play_wait ) != VLC_SUCCESS;
- if( p_buffer != NULL )
+ if( i_buffer_offset < p_buffer->i_buffer )
+ {
+ /* buffer is not fully processed, try again with the
+ * same cmd and buffer */
b_remove_cmd = false;
+ }
+ else
+ {
+ p_buffer = NULL;
+ i_buffer_offset = 0;
+ }
if( i_play_wait > 0 )
i_play_deadline = mdate() + i_play_wait;
break;
case CMD_TIME_GET:
assert( p_sys->p_audiotrack );
if( b_error )
+ {
+ p_cmd->out.time_get.i_ret = -1;
break;
+ }
p_cmd->out.time_get.i_ret =
JNIThread_TimeGet( env, p_aout,
&p_cmd->out.time_get.i_delay );
}
if( p_sys->b_audiotrack_exception )
b_error = true;
+ if( b_error )
+ p_sys->i_samples_queued = 0;
if( b_remove_cmd )
{
{
if( p_sys->p_bytearray )
(*env)->DeleteGlobalRef( env, p_sys->p_bytearray );
+ if( p_sys->p_floatarray )
+ (*env)->DeleteGlobalRef( env, p_sys->p_floatarray );
if( p_sys->p_bytebuffer )
(*env)->DeleteGlobalRef( env, p_sys->p_bytebuffer );
jni_detach_thread();