【十四】【vlc-android】aout音频输出模块源码实现分析【Part 2】

该章节承接上一章节内容继续分析
上一章节:【十四】【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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值