Commit b4d7ab2b authored by Thomas Guillem's avatar Thomas Guillem Committed by Jean-Baptiste Kempf

audiotrack: refactor Configure and Start

Rename JNIThread_Configure into JNIThread_NewAudioTrack. This function doesn't
touch any vlc format anymore, it does only Android AudioTrack configuration.
The VLC init part is done by the Start function. JNIThread_NewAudioTrack can be
called up to 3 times by Start with different channels and format until if find
a working configuration. Indeed some devices don't support 5.1, or FL32.
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent df48e0ee
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
static int Open( vlc_object_t * ); static int Open( vlc_object_t * );
static void Close( vlc_object_t * ); static void Close( vlc_object_t * );
static void JNIThread_Stop( JNIEnv *env, audio_output_t *p_aout );
struct thread_cmd; struct thread_cmd;
typedef TAILQ_HEAD(, thread_cmd) THREAD_CMD_QUEUE; typedef TAILQ_HEAD(, thread_cmd) THREAD_CMD_QUEUE;
...@@ -561,198 +562,221 @@ JNIThread_TimeGet( JNIEnv *env, audio_output_t *p_aout, mtime_t *p_delay ) ...@@ -561,198 +562,221 @@ JNIThread_TimeGet( JNIEnv *env, audio_output_t *p_aout, mtime_t *p_delay )
} }
static void static void
AudioTrack_GetChanOrder( int i_mask, uint32_t p_chans_out[] ) AudioTrack_GetChanOrder( uint16_t i_physical_channels, uint32_t p_chans_out[] )
{ {
#define HAS_CHAN( x ) ( ( i_mask & (jfields.AudioFormat.x) ) == (jfields.AudioFormat.x) ) #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 */ /* samples will be in the following order: FL FR FC LFE BL BR BC SL SR */
int i = 0; int i = 0;
if( HAS_CHAN( CHANNEL_OUT_FRONT_LEFT ) ) if( HAS_CHAN( AOUT_CHAN_LEFT ) )
p_chans_out[i++] = AOUT_CHAN_LEFT; p_chans_out[i++] = AOUT_CHAN_LEFT;
if( HAS_CHAN( CHANNEL_OUT_FRONT_RIGHT ) ) if( HAS_CHAN( AOUT_CHAN_RIGHT ) )
p_chans_out[i++] = AOUT_CHAN_RIGHT; p_chans_out[i++] = AOUT_CHAN_RIGHT;
if( HAS_CHAN( CHANNEL_OUT_FRONT_CENTER ) ) if( HAS_CHAN( AOUT_CHAN_CENTER ) )
p_chans_out[i++] = AOUT_CHAN_CENTER; p_chans_out[i++] = AOUT_CHAN_CENTER;
if( HAS_CHAN( CHANNEL_OUT_LOW_FREQUENCY ) ) if( HAS_CHAN( AOUT_CHAN_LFE ) )
p_chans_out[i++] = AOUT_CHAN_LFE; p_chans_out[i++] = AOUT_CHAN_LFE;
if( HAS_CHAN( CHANNEL_OUT_BACK_LEFT ) ) if( HAS_CHAN( AOUT_CHAN_REARLEFT ) )
p_chans_out[i++] = AOUT_CHAN_REARLEFT; p_chans_out[i++] = AOUT_CHAN_REARLEFT;
if( HAS_CHAN( CHANNEL_OUT_BACK_RIGHT ) ) if( HAS_CHAN( AOUT_CHAN_REARRIGHT ) )
p_chans_out[i++] = AOUT_CHAN_REARRIGHT; p_chans_out[i++] = AOUT_CHAN_REARRIGHT;
if( HAS_CHAN( CHANNEL_OUT_BACK_CENTER ) ) if( HAS_CHAN( AOUT_CHAN_REARCENTER ) )
p_chans_out[i++] = AOUT_CHAN_REARCENTER; p_chans_out[i++] = AOUT_CHAN_REARCENTER;
if( jfields.AudioFormat.has_CHANNEL_OUT_SIDE ) if( HAS_CHAN( AOUT_CHAN_MIDDLELEFT ) )
{ p_chans_out[i++] = AOUT_CHAN_MIDDLELEFT;
if( HAS_CHAN( CHANNEL_OUT_SIDE_LEFT ) ) if( HAS_CHAN( AOUT_CHAN_MIDDLERIGHT ) )
p_chans_out[i++] = AOUT_CHAN_MIDDLELEFT; p_chans_out[i++] = AOUT_CHAN_MIDDLERIGHT;
if( HAS_CHAN( CHANNEL_OUT_SIDE_RIGHT ) )
p_chans_out[i++] = AOUT_CHAN_MIDDLERIGHT;
}
assert( i <= AOUT_CHAN_MAX ); assert( i <= AOUT_CHAN_MAX );
#undef HAS_CHAN #undef HAS_CHAN
} }
/** /**
* Configure and create an Android AudioTrack. * Configure and create an Android AudioTrack.
* returns -1 on critical error, 0 on success, 1 on configuration error * returns NULL on configuration error
*/ */
static int static jobject
JNIThread_Configure( JNIEnv *env, audio_output_t *p_aout ) 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_format;
int i_size, i_min_buffer_size, i_channel_config, i_format,
i_format_size, i_nb_channels;
uint8_t i_chans_to_reorder = 0;
uint8_t p_chan_table[AOUT_CHAN_MAX];
uint32_t p_chans_out[AOUT_CHAN_MAX];
jobject p_audiotrack; jobject p_audiotrack;
audio_sample_format_t fmt = p_sys->fmt;
/* 4000 <= frequency <= 48000 */
fmt.i_rate = VLC_CLIP( fmt.i_rate, 4000, 48000 );
/* We can only accept U8, S16N, FL32 */ switch( i_vlc_format )
switch( fmt.i_format )
{
case VLC_CODEC_U8:
break;
case VLC_CODEC_S16N:
break;
case VLC_CODEC_FL32:
if( !jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
fmt.i_format = VLC_CODEC_S16N;
break;
default:
fmt.i_format = VLC_CODEC_S16N;
break;
}
switch( fmt.i_format )
{ {
case VLC_CODEC_U8: case VLC_CODEC_U8:
i_format = jfields.AudioFormat.ENCODING_PCM_8BIT; i_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
i_format_size = 1;
break; break;
case VLC_CODEC_S16N: case VLC_CODEC_S16N:
i_format = jfields.AudioFormat.ENCODING_PCM_16BIT; i_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
i_format_size = 2;
break; break;
case VLC_CODEC_FL32: case VLC_CODEC_FL32:
i_format = jfields.AudioFormat.ENCODING_PCM_FLOAT; i_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
i_format_size = 4;
break; break;
default: default:
vlc_assert_unreachable(); vlc_assert_unreachable();
} }
i_nb_channels = aout_FormatNbChannels( &fmt ); switch( i_physical_channels )
/* 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
*/
if( i_nb_channels > 5 )
{ {
if( i_nb_channels > 7 && jfields.AudioFormat.has_CHANNEL_OUT_SIDE ) case AOUT_CHANS_7_1:
{
fmt.i_physical_channels = AOUT_CHANS_7_1;
/* bitmask of CHANNEL_OUT_7POINT1 doesn't correspond to 5POINT1 and /* bitmask of CHANNEL_OUT_7POINT1 doesn't correspond to 5POINT1 and
* SIDES */ * SIDES */
i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1 | i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1 |
jfields.AudioFormat.CHANNEL_OUT_SIDE_LEFT | jfields.AudioFormat.CHANNEL_OUT_SIDE_LEFT |
jfields.AudioFormat.CHANNEL_OUT_SIDE_RIGHT; jfields.AudioFormat.CHANNEL_OUT_SIDE_RIGHT;
} else break;
{ case AOUT_CHANS_5_1:
fmt.i_physical_channels = AOUT_CHANS_5_1;
i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1; i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1;
} break;
} else case AOUT_CHAN_LEFT:
{
if( i_nb_channels == 1 )
{
i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO; i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;
} else break;
{ default:
fmt.i_physical_channels = AOUT_CHANS_STEREO; case AOUT_CHANS_STEREO:
i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO; i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
} break;
} }
i_nb_channels = aout_FormatNbChannels( &fmt );
memset( p_chans_out, 0, sizeof(p_chans_out) ); i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, i_rate,
AudioTrack_GetChanOrder( i_channel_config, p_chans_out );
i_chans_to_reorder = aout_CheckChannelReorder( NULL, p_chans_out,
fmt.i_physical_channels,
p_chan_table );
i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, fmt.i_rate,
i_channel_config, i_format ); i_channel_config, i_format );
if( i_min_buffer_size <= 0 ) if( i_min_buffer_size <= 0 )
{ {
msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ; msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ;
return 1; return NULL;
} }
i_size = i_min_buffer_size * 4; i_size = i_min_buffer_size * 4;
/* create AudioTrack object */ /* create AudioTrack object */
p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC, fmt.i_rate, p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC, i_rate,
i_channel_config, i_format, i_size, i_channel_config, i_format, i_size,
jfields.AudioTrack.MODE_STREAM ); jfields.AudioTrack.MODE_STREAM );
if( CHECK_AT_EXCEPTION( "AudioTrack<init>" ) || !p_audiotrack ) if( CHECK_AT_EXCEPTION( "AudioTrack<init>" ) || !p_audiotrack )
return 1; {
p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack ); msg_Warn( p_aout, "AudioTrack Init failed" ) ;
(*env)->DeleteLocalRef( env, p_audiotrack ); return NULL;
if( !p_sys->p_audiotrack ) }
return -1; if( JNI_CALL_INT( p_audiotrack, jfields.AudioTrack.getState )
!= jfields.AudioTrack.STATE_INITIALIZED )
p_sys->i_chans_to_reorder = i_chans_to_reorder; {
if( i_chans_to_reorder ) JNI_CALL_VOID( p_audiotrack, jfields.AudioTrack.release );
memcpy( p_sys->p_chan_table, p_chan_table, sizeof(p_sys->p_chan_table) ); (*env)->DeleteLocalRef( env, p_audiotrack );
p_sys->i_bytes_per_frame = i_nb_channels * i_format_size; msg_Err( p_aout, "AudioTrack getState failed" );
p_sys->i_max_audiotrack_samples = i_size / p_sys->i_bytes_per_frame; return NULL;
p_sys->fmt = fmt; }
*p_audiotrack_size = i_size;
return 0; return p_audiotrack;
} }
static int static int
JNIThread_Start( JNIEnv *env, audio_output_t *p_aout ) JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
{ {
aout_sys_t *p_sys = p_aout->sys; aout_sys_t *p_sys = p_aout->sys;
int i_ret; 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 ); aout_FormatPrint( p_aout, "VLC is looking for:", &p_sys->fmt );
p_sys->fmt.i_original_channels = p_sys->fmt.i_physical_channels; p_sys->fmt.i_original_channels = p_sys->fmt.i_physical_channels;
i_ret = JNIThread_Configure( env, p_aout ); /* 4000 <= frequency <= 48000 */
if( i_ret == 1 ) 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 )
{ {
/* configuration error, try to fallback to stereo */ case VLC_CODEC_U8:
if( ( p_sys->fmt.i_format != VLC_CODEC_U8 && break;
p_sys->fmt.i_format != VLC_CODEC_S16N ) || case VLC_CODEC_S16N:
aout_FormatNbChannels( &p_sys->fmt ) > 2 ) break;
{ case VLC_CODEC_FL32:
msg_Warn( p_aout, if( !jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
"AudioTrack configuration failed, try again in stereo" ); p_sys->fmt.i_format = VLC_CODEC_S16N;
break;
default:
p_sys->fmt.i_format = VLC_CODEC_S16N; 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; p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
}
i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );
i_ret = JNIThread_Configure( env, p_aout ); 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( i_ret != 0 )
if( !p_audiotrack )
return VLC_EGENERIC; return VLC_EGENERIC;
aout_FormatPrint( p_aout, "VLC will output:", &p_sys->fmt ); 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 ) memset( p_chans_out, 0, sizeof(p_chans_out) );
{ AudioTrack_GetChanOrder( p_sys->fmt.i_physical_channels, p_chans_out );
msg_Err( p_aout, "AudioTrack init failed" ); p_sys->i_chans_to_reorder =
goto error; 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 #ifdef AUDIOTRACK_USE_TIMESTAMP
if( jfields.AudioTimestamp.clazz ) if( jfields.AudioTimestamp.clazz )
...@@ -762,11 +786,17 @@ JNIThread_Start( JNIEnv *env, audio_output_t *p_aout ) ...@@ -762,11 +786,17 @@ JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
jfields.AudioTimestamp.clazz, jfields.AudioTimestamp.clazz,
jfields.AudioTimestamp.ctor ); jfields.AudioTimestamp.ctor );
if( !p_audioTimestamp ) if( !p_audioTimestamp )
goto error; {
JNIThread_Stop( env, p_aout );
return VLC_EGENERIC;
}
p_sys->p_audioTimestamp = (*env)->NewGlobalRef( env, p_audioTimestamp ); p_sys->p_audioTimestamp = (*env)->NewGlobalRef( env, p_audioTimestamp );
(*env)->DeleteLocalRef( env, p_audioTimestamp ); (*env)->DeleteLocalRef( env, p_audioTimestamp );
if( !p_sys->p_audioTimestamp ) if( !p_sys->p_audioTimestamp )
goto error; {
JNIThread_Stop( env, p_aout );
return VLC_EGENERIC;
}
} }
#endif #endif
...@@ -774,15 +804,9 @@ JNIThread_Start( JNIEnv *env, audio_output_t *p_aout ) ...@@ -774,15 +804,9 @@ JNIThread_Start( JNIEnv *env, audio_output_t *p_aout )
CHECK_AT_EXCEPTION( "play" ); CHECK_AT_EXCEPTION( "play" );
p_sys->i_play_time = mdate(); p_sys->i_play_time = mdate();
aout_FormatPrint( p_aout, "VLC will output:", &p_sys->fmt );
return VLC_SUCCESS; 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 static void
...@@ -790,15 +814,18 @@ JNIThread_Stop( JNIEnv *env, audio_output_t *p_aout ) ...@@ -790,15 +814,18 @@ JNIThread_Stop( JNIEnv *env, audio_output_t *p_aout )
{ {
aout_sys_t *p_sys = p_aout->sys; 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( !p_sys->b_audiotrack_exception )
if( !CHECK_AT_EXCEPTION( "stop" ) ) {
JNI_AT_CALL_VOID( release ); 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; p_sys->b_audiotrack_exception = false;
(*env)->DeleteGlobalRef( env, p_sys->p_audiotrack );
p_sys->p_audiotrack = NULL;
if( p_sys->p_audioTimestamp ) if( p_sys->p_audioTimestamp )
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment