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 */
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;
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
}
#endif
+ 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();
* 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( p_buffer == NULL )
{
p_buffer = p_cmd->in.play.p_buffer;
- 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 );
-
+ b_error = JNIThread_PreparePlay( env, p_aout, p_buffer )
+ != VLC_SUCCESS;
}
- b_error = JNIThread_Play( env, p_aout, &p_buffer,
+ 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();