该章节承接上一章节内容继续分析
上一章节:【十四】【vlc-android】aout音频输出模块源码实现分析【Part 1】
3、Stop实现分析:【停止AudioTrack线程等相关操作】
// [vlc/modules/audio_output/audiotrack.c]
static void
Stop( audio_output_t *p_aout )
{
aout_sys_t *p_sys = p_aout->sys;
JNIEnv *env;
if( !( env = GET_ENV() ) )
return;
/* Stop the AudioTrack thread */
vlc_mutex_lock( &p_sys->lock );
if( p_sys->b_thread_running )
{
// 停止线程运行处理
p_sys->b_thread_running = false;
// 唤醒当前可能沉睡的线程
vlc_cond_signal( &p_sys->thread_cond );
vlc_mutex_unlock( &p_sys->lock );
// 以阻塞的方式等待thread指定的线程结束
vlc_join( p_sys->thread, NULL );
}
else
vlc_mutex_unlock( &p_sys->lock );
// 释放AudioTrack对象引用,后面类似处理释放内存或初始化
/* Release the AudioTrack object */
if( p_sys->p_audiotrack )
{
if( !p_sys->b_audiotrack_exception )
{
// 若没有异常,调用java层AudioTrack的stop和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;
}
/* Release the timestamp object */
if( p_sys->timestamp.p_obj )
{
(*env)->DeleteGlobalRef( env, p_sys->timestamp.p_obj );
p_sys->timestamp.p_obj = NULL;
}
/* Release the Circular buffer data */
switch( p_sys->i_write_type )
{
case WRITE_BYTEARRAY:
case WRITE_BYTEARRAYV23:
if( p_sys->circular.u.p_bytearray )
{
(*env)->DeleteGlobalRef( env, p_sys->circular.u.p_bytearray );
p_sys->circular.u.p_bytearray = NULL;
}
break;
case WRITE_SHORTARRAYV23:
if( p_sys->circular.u.p_shortarray )
{
(*env)->DeleteGlobalRef( env, p_sys->circular.u.p_shortarray );
p_sys->circular.u.p_shortarray = NULL;
}
break;
case WRITE_FLOATARRAY:
if( p_sys->circular.u.p_floatarray )
{
(*env)->DeleteGlobalRef( env, p_sys->circular.u.p_floatarray );
p_sys->circular.u.p_floatarray = NULL;
}
break;
case WRITE_BYTEBUFFER:
free( p_sys->circular.u.bytebuffer.p_data );
p_sys->circular.u.bytebuffer.p_data = NULL;
break;
}
p_sys->b_audiotrack_exception = false;
p_sys->b_error = false;
p_sys->b_passthrough = false;
}
4、Play实现分析:【播放已解码音频buffer数据】
// [vlc/modules/audio_output/audiotrack.c]
static void
Play( audio_output_t *p_aout, block_t *p_buffer )
{
JNIEnv *env = NULL;
size_t i_buffer_offset = 0;
aout_sys_t *p_sys = p_aout->sys;
// IEC61937 的数据格式可以包含象MPEG2那种多声道, AC3 或DTS。
// 当IEC61937的数据流可以在保持原有采样率的情况下被转换为S/PDIF信号,
// 声道标识信息只占1bit (仅1),代表数据在S/PDIF帧是数字音频还是其他数据 (DTS, AC3, MPEG audio etc.)。
// 这个bit会说明标准数字音频设备不用尝试以他们的采样率回放这些数据。
// S/PDIF(Sony/Philips Digital Interface Format)是一种数字传输接口,可使用光纤或同轴电缆输出,
// 把音频输出至解码器上,能保持高保真度的输出结果。
if( p_sys->b_passthrough && p_sys->fmt.i_format == VLC_CODEC_SPDIFB
&& ConvertFromIEC61937( p_aout, p_buffer ) != 0 )
{
block_Release(p_buffer);
return;
}
vlc_mutex_lock( &p_sys->lock );
if( p_sys->b_error || !( env = GET_ENV() ) )
goto bailout;
if( p_sys->i_chans_to_reorder )
// channel表重排序
// 在一个线性音频交错样本块内重新排序音频样本。
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 );
// [i_buffer_offset]该值根据下面的实现可知作用:即记录当前已从block buffer数据读取的数据量
// 即定位block中已读取到当前的位置
while( i_buffer_offset < p_buffer->i_buffer && !p_sys->b_error )
{
size_t i_circular_free;
size_t i_data_offset;
size_t i_data_size;
// 判断在buffer循环缓冲区队列中是否有足够内存数据空间可写入新的待播放数据
// 即若写入数据已满占整个循环缓冲区内存则必须wait解码器解码端线程,
// 否则数据会造成溢出来不及播放就被覆盖了。并等待aout播放端进行播放后唤醒此处
/* Wait for enough room in circular buffer */
while( !p_sys->b_error && ( i_circular_free = p_sys->circular.i_size -
( p_sys->circular.i_write - p_sys->circular.i_read ) ) == 0 )
// 若没有释放【播放后】的内存空间则wait当前播放方法的调用端线程。
// 【第七章音频decoder层分析中3.1小节分析调用了audio播放流程】
// 作用就是:当前被写入【解码时】的buffer内存大小已使用完整个buffer循环缓冲区大小,
// 导致没有足够已释放空间内存写入当前待播放的block数据则wait当前play操作
// 而唤醒此处的操作是在下面的【AudioTrack_Thread】第12小节分析中
vlc_cond_wait( &p_sys->aout_cond, &p_sys->lock );
if( p_sys->b_error )
goto bailout;
// 取余获取当前已写入的数据偏移量
i_data_offset = p_sys->circular.i_write % p_sys->circular.i_size;
// 计算当前能够写入已释放空间内存的block数据大小
i_data_size = __MIN( p_buffer->i_buffer - i_buffer_offset,
p_sys->circular.i_size - i_data_offset );
i_data_size = __MIN( i_data_size, i_circular_free );
// 以下为根据写入数据类型来设置
switch( p_sys->i_write_type )
{
case WRITE_BYTEARRAY:
case WRITE_BYTEARRAYV23:
// i_buffer_offset根据偏移量定位block写入数据的开始位置,写入的数据长度为i_data_size
(*env)->SetByteArrayRegion( env, p_sys->circular.u.p_bytearray,
i_data_offset, i_data_size,
(jbyte *)p_buffer->p_buffer
+ i_buffer_offset);
break;
case WRITE_SHORTARRAYV23:
// ~取反算术运算符表示二进制位取反,即~1 = 1111 1110
// &与运算:因此如【size &= ~1】作用为,若size的最后一位为1则将其变为0,其他位保持不变
// 即最终size可能减小1
i_data_offset &= ~1;
i_data_size &= ~1;
// 其实就是将size作为2的整数倍用于此处的写入计算,因为变小了2倍,因此必须使其能保持整数大小写入数据
(*env)->SetShortArrayRegion( env, p_sys->circular.u.p_shortarray,
i_data_offset / 2, i_data_size / 2,
(jshort *)p_buffer->p_buffer
+ i_buffer_offset / 2)