天天看点

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

接着第九章节分析aout输出组件模块的加载和实现分析。

有第九章节第2小节中可知,加载的模块名为【“audio output”】

通过全局搜索有如下android端支持组件模块:

// 第1种方式
audiotrack.c (vlc\modules\audio_output) line 183 :     set_capability( "audio output", 180 )

// 第2种方式
// 将pcm源数据保存到文件中
file.c (vlc\modules\audio_output) line 127 :     set_capability( "audio output", 0 )

// 第3种方式
opensles_android.c (vlc\modules\audio_output) line 139 :     set_capability("audio output", 170)
           

由上面可知,默认采用第一种audiotrack方式输出音频。

audiotrack方式的音频输出模块组件声明如下:

// [vlc/modules/audio_output/audiotrack.c]

vlc_module_begin ()
    set_shortname( "AudioTrack" )
    set_description( "Android AudioTrack audio output" )
    set_capability( "audio output", 180 )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
    add_integer( "audiotrack-session-id", 0,
            AUDIOTRACK_SESSION_ID_TEXT, NULL, true )
        change_private()
    add_shortcut( "audiotrack" )
    set_callbacks( Open, Close )
vlc_module_end ()
           

1、组件初始化入口方法:

// [vlc/modules/audio_output/audiotrack.c]
static int
Open( vlc_object_t *obj )
{
    audio_output_t *p_aout = (audio_output_t *) obj;
    aout_sys_t *p_sys;
    // 获取JNI环境变量
    JNIEnv *env = GET_ENV();

    // 初始化audio相关的java类相关方法、变量常量等信息引用为JNI字段以便后续获取和调用
    if( !env || !InitJNIFields( p_aout, env ) )
        return VLC_EGENERIC;

    p_sys = calloc( 1, sizeof (aout_sys_t) );

    if( unlikely( p_sys == NULL ) )
        return VLC_ENOMEM;

    p_sys->at_dev = AT_DEV_DEFAULT;
    vlc_mutex_init(&p_sys->lock);
    vlc_cond_init(&p_sys->aout_cond);
    vlc_cond_init(&p_sys->thread_cond);

    p_aout->sys = p_sys;
    // 见第2小节分析
    p_aout->start = Start;
    // 见第3小节分析
    p_aout->stop = Stop;
    // 见第4小节分析
    p_aout->play = Play;
    // 见第5小节分析
    p_aout->pause = Pause;
    // 见第6小节分析
    p_aout->flush = Flush;
    // 见第7小节分析
    p_aout->time_get = TimeGet;
    // 见第8小节分析
    p_aout->device_select = DeviceSelect;

    for( unsigned int i = 0; at_devs[i].id; ++i )
        // 该方法内部调用了【aout->event.hotplug_report(aout, id, name);】,
        // 而该方法的赋值在第九章节第2小节分析中赋值的,
        // 其功能为:上报设备的热插拔事件给java层
        aout_HotplugReport(p_aout, at_devs[i].id, at_devs[i].name);

    // 调节音量功能
    // 见第9小节分析
    p_aout->volume_set = VolumeSet;
    // 设置静音功能
    // 见第10小节分析
    p_aout->mute_set = MuteSet;
    // 默认正常系统音量
    p_sys->volume = 1.0f;
    // 默认不为静音
    p_sys->mute = false;

    return VLC_SUCCESS;
}

// [vlc/modules/audio_output/audiotrack.c]
// 初始化加载java类【"android/media/AudioTrack"】、【"android/media/AudioTimestamp"】、
//【"android/media/AudioSystem"】、【"android/media/AudioFormat"】、
//【"android/media/AudioManager"】所有相关的方法或变量常量等JNI字段引用
static bool
InitJNIFields( audio_output_t *p_aout, JNIEnv* env )
{
    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
    static int i_init_state = -1;
    bool ret;
    jclass clazz;
    jfieldID field;

    vlc_mutex_lock( &lock );

    if( i_init_state != -1 )
        goto end;

// 方法宏定义

#define CHECK_EXCEPTION( what, critical ) do { \
    if( (*env)->ExceptionCheck( env ) ) \
    { \
        msg_Err( p_aout, "%s failed", what ); \
        (*env)->ExceptionClear( env ); \
        if( (critical) ) \
        { \
            i_init_state = 0; \
            goto end; \
        } \
    } \
} while( 0 )
#define GET_CLASS( str, critical ) do { \
    clazz = (*env)->FindClass( env, (str) ); \
    CHECK_EXCEPTION( "FindClass(" str ")", critical ); \
} while( 0 )
#define GET_ID( get, id, str, args, critical ) do { \
    jfields.id = (*env)->get( env, clazz, (str), (args) ); \
    CHECK_EXCEPTION( #get "(" #id ")", critical ); \
} while( 0 )
#define GET_CONST_INT( id, str, critical ) do { \
    field = NULL; \
    field = (*env)->GetStaticFieldID( env, clazz, (str), "I" ); \
    CHECK_EXCEPTION( "GetStaticFieldID(" #id ")", critical ); \
    if( field ) \
    { \
        jfields.id = (*env)->GetStaticIntField( env, clazz, field ); \
        CHECK_EXCEPTION( #id, critical ); \
    } \
} while( 0 )

    /* AudioTrack class init */
    GET_CLASS( "android/media/AudioTrack", true );
    jfields.AudioTrack.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );
    CHECK_EXCEPTION( "NewGlobalRef", true );

    GET_ID( GetMethodID, AudioTrack.ctor, "<init>", "(IIIIIII)V", true );
    GET_ID( GetMethodID, AudioTrack.release, "release", "()V", true );
    GET_ID( GetMethodID, AudioTrack.getState, "getState", "()I", true );
    GET_ID( GetMethodID, AudioTrack.play, "play", "()V", true );
    GET_ID( GetMethodID, AudioTrack.stop, "stop", "()V", true );
    GET_ID( GetMethodID, AudioTrack.flush, "flush", "()V", true );
    GET_ID( GetMethodID, AudioTrack.pause, "pause", "()V", true );

    GET_ID( GetMethodID, AudioTrack.writeV23, "write", "([BIII)I", false );
    GET_ID( GetMethodID, AudioTrack.writeShortV23, "write", "([SIII)I", false );
    
    // ...省略其他代码
    return ret;
}
           

2、Start实现分析:【该方法在第9行3.1小节分析过】

// [vlc/modules/audio_output/audiotrack.c]
static int
Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
{
    aout_sys_t *p_sys = p_aout->sys;
    JNIEnv *env;
    int i_ret;
    bool b_try_passthrough;
    unsigned i_max_channels;

    if( p_sys->at_dev == AT_DEV_ENCODED )
    {// 播放编码数据的设备?
        b_try_passthrough = true;
        i_max_channels = AT_DEV_MAX_CHANNELS;
    }
    else
    {
        b_try_passthrough = var_InheritBool( p_aout, "spdif" );
        // 双声道设备则最大声道数为2,否则默认最大为8
        i_max_channels = p_sys->at_dev == AT_DEV_STEREO ? 2 : AT_DEV_MAX_CHANNELS;
    }

    if( !( env = GET_ENV() ) )
        return VLC_EGENERIC;

    p_sys->fmt = *p_fmt;

    // 打印音频采样【样本】格式
    aout_FormatPrint( p_aout, "VLC is looking for:", &p_sys->fmt );

    // 低延迟标志
    bool low_latency = false;
    if (p_sys->fmt.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
    {// 立体环绕声道类型
    
        // 设置为位图类型
        p_sys->fmt.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;

        // 探测输出声道为立体声道
        /* TODO: detect sink channel layout */
        p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
        // 根据当前音频采样格式参数,初始化其音频通道(声道)数、量化精度【每个采样点使用的位数bits】、
        // 每压缩帧数据占字节数大小、每压缩帧数据包含的采样帧个数
        aout_FormatPrepare(&p_sys->fmt);
        // 标记该输出类型为低延迟
        low_latency = true;
    }

    // 该条件代码为宏定义,用于获取并判断音频量化精度是否不为0
    if( AOUT_FMT_LINEAR( &p_sys->fmt ) )
        // 不为0,执行开始PCM数据处理流程
        // 见2.1小节分析
        i_ret = StartPCM( env, p_aout, i_max_channels );
    else if( b_try_passthrough )
        // 否则执行编码音频流的输出给支持该格式的设备播放?
        i_ret = StartPassthrough( env, p_aout );
    else
        return VLC_EGENERIC;

    if( i_ret != 0 )
        return VLC_EGENERIC;

    // 估算缓冲区buffer中audiotrack最大样本数
    p_sys->i_max_audiotrack_samples = BYTES_TO_FRAMES( p_sys->audiotrack_args.i_size );

#ifdef AUDIOTRACK_HW_LATENCY
    if( jfields.AudioTimestamp.clazz )
    {
        // 创建java层【AudioTimestamp即当前播放时间】对象的JNi层对象引用
        /* create AudioTimestamp object */
        jobject p_obj = JNI_CALL( NewObject, jfields.AudioTimestamp.clazz,
                                 jfields.AudioTimestamp.ctor );
        if( p_obj )
        {
            p_sys->timestamp.p_obj = (*env)->NewGlobalRef( env, p_obj );
            (*env)->DeleteLocalRef( env, p_obj );
        }
        if( !p_sys->timestamp.p_obj )
            goto error;
    }
#endif

    // 重置AudioTrack平滑位置和时间戳位置
    AudioTrack_Reset( env, p_aout );

    // 获取写入数据类型
    if( p_sys->fmt.i_format == VLC_CODEC_FL32 )
    {
        msg_Dbg( p_aout, "using WRITE_FLOATARRAY");
        p_sys->i_write_type = WRITE_FLOATARRAY;
    }
    else if( p_sys->fmt.i_format == VLC_CODEC_SPDIFL )
    {
        assert( jfields.AudioFormat.has_ENCODING_IEC61937 );
        msg_Dbg( p_aout, "using WRITE_SHORTARRAYV23");
        p_sys->i_write_type = WRITE_SHORTARRAYV23;
    }
    else if( jfields.AudioTrack.writeV23 )
    {
        msg_Dbg( p_aout, "using WRITE_BYTEARRAYV23");
        p_sys->i_write_type = WRITE_BYTEARRAYV23;
    }
    else if( jfields.AudioTrack.writeBufferV21 )
    {
        msg_Dbg( p_aout, "using WRITE_BYTEBUFFER");
        p_sys->i_write_type = WRITE_BYTEBUFFER;
    }
    else
    {
        msg_Dbg( p_aout, "using WRITE_BYTEARRAY");
        p_sys->i_write_type = WRITE_BYTEARRAY;
    }

    p_sys->circular.i_read = p_sys->circular.i_write = 0;
    // 计算缓冲区循环buffer字节大小即计算码率大小:每个样本大小乘以采样率
    p_sys->circular.i_size = (int)p_sys->fmt.i_rate
                           * p_sys->fmt.i_bytes_per_frame
                           / p_sys->fmt.i_frame_length;
    if (low_latency)
    {
        // 由上面分析可知,立体环绕声道类型时进入,
        // 计算缓冲区循环buffer字节大小【码率大小】,可接受buffer数据缩小为40ms小的延迟(40毫秒内数据)
        /* 40 ms of buffering */
        p_sys->circular.i_size = p_sys->circular.i_size / 25;
    }
    else
    {
        // 默认缓冲区循环buffer字节大小【码率大小】可接受buffer数据扩大为2秒延迟
        /* 2 seconds of buffering */
        p_sys->circular.i_size = p_sys->circular.i_size * AOUT_MAX_PREPARE_TIME
                               / CLOCK_FREQ;
    }

    // 根据写入数据类型来分配缓冲区buffer内存
    /* Allocate circular buffer */
    switch( p_sys->i_write_type )
    {
        case WRITE_BYTEARRAY:
        case WRITE_BYTEARRAYV23:
        {
            jbyteArray p_bytearray;

            p_bytearray = (*env)->NewByteArray( env, p_sys->circular.i_size );
            if( p_bytearray )
            {
                p_sys->circular.u.p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );
                (*env)->DeleteLocalRef( env, p_bytearray );
            }

            if( !p_sys->circular.u.p_bytearray )
            {
                msg_Err(p_aout, "byte array allocation failed");
                goto error;
            }
            break;
        }
        case WRITE_SHORTARRAYV23:
        {
            jshortArray p_shortarray;

            // short比byte(8位)长度多1倍,因此将比特码率减小一倍
            p_shortarray = (*env)->NewShortArray( env,
                                                  p_sys->circular.i_size / 2 );
            if( p_shortarray )
            {
                p_sys->circular.u.p_shortarray = (*env)->NewGlobalRef( env, p_shortarray );
                (*env)->DeleteLocalRef( env, p_shortarray );
            }
            if( !p_sys->circular.u.p_shortarray )
            {
                msg_Err(p_aout, "short array allocation failed");
                goto error;
            }
            break;
        }
        case WRITE_FLOATARRAY:
        {
            jfloatArray p_floatarray;

            // float比byte(8位)长度多4倍,因此将比特码率减小一倍
            p_floatarray = (*env)->NewFloatArray( env,
                                                  p_sys->circular.i_size / 4 );
            if( p_floatarray )
            {
                p_sys->circular.u.p_floatarray = (*env)->NewGlobalRef( env, p_floatarray );
                (*env)->DeleteLocalRef( env, p_floatarray );
            }
            if( !p_sys->circular.u.p_floatarray )
            {
                msg_Err(p_aout, "float array allocation failed");
                goto error;
            }
            break;
        }
        case WRITE_BYTEBUFFER:
            // p_data就是byte
            p_sys->circular.u.bytebuffer.p_data = malloc( p_sys->circular.i_size );
            if( !p_sys->circular.u.bytebuffer.p_data )
            {
                msg_Err(p_aout, "bytebuffer allocation failed");
                goto error;
            }
            break;
    }

    // 运行AudioTrack线程,并标记当前线程状态
    /* Run AudioTrack_Thread */
    p_sys->b_thread_running = true;
    p_sys->b_thread_paused = false;
    // AudioTrack_Thread线程实现见第11小节分析
    if ( vlc_clone( &p_sys->thread, AudioTrack_Thread, p_aout,
                    VLC_THREAD_PRIORITY_LOW ) )
    {
        
        
        
        msg_Err(p_aout, "vlc clone failed");
        goto error;
    }

    // 调用AudioTrack的play()播放方法,等待写入播放数据进行播放
    JNI_AT_CALL_VOID( play );
    CHECK_AT_EXCEPTION( "play" );

    *p_fmt = p_sys->fmt;

    // 调节音量功能
    // 见第9小节分析    
    p_aout->volume_set(p_aout, p_sys->volume);
    if (p_sys->mute)
        // 需要静音则设置静音功能
        // 见第10小节分析
        p_aout->mute_set(p_aout, true);
    // 打印音频样本格式数据
    aout_FormatPrint( p_aout, "VLC will output:", &p_sys->fmt );

    return VLC_SUCCESS;

error:
    // 见第3小节分析
    Stop( p_aout );
    return VLC_EGENERIC;
}
           

2.1、StartPCM实现分析:

// [vlc/modules/audio_output/audiotrack.c]
static int
StartPCM( JNIEnv *env, audio_output_t *p_aout, unsigned i_max_channels )
{
    aout_sys_t *p_sys = p_aout->sys;
    unsigned i_nb_channels;
    int i_at_format, i_ret;

    // 通过AudioTrack的getNativeOutputSampleRate静态方法获取音频采样率值
    // Music类型通道
    if (jfields.AudioTrack.getNativeOutputSampleRate)
        p_sys->fmt.i_rate =
            JNI_AT_CALL_STATIC_INT( getNativeOutputSampleRate,
                                    jfields.AudioManager.STREAM_MUSIC );
    else
        // 否则根据音频输入的采样率来适配,最低4KHz,最高48KHz
        p_sys->fmt.i_rate = VLC_CLIP( p_sys->fmt.i_rate, 4000, 48000 );

    do
    {
        // 注:仅处理的PCM位深为此四个值【U8, S16N, FL32, and AC3】
        /* We can only accept U8, S16N, FL32, and AC3 */
        switch( p_sys->fmt.i_format )
        {
        case VLC_CODEC_U8:
            i_at_format = jfields.AudioFormat.ENCODING_PCM_8BIT;
            break;
        case VLC_CODEC_S16N:
            i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
            break;
        case VLC_CODEC_FL32:
            if( jfields.AudioFormat.has_ENCODING_PCM_FLOAT )
                i_at_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;
            else
            {
                p_sys->fmt.i_format = VLC_CODEC_S16N;
                i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
            }
            break;
        default:
            p_sys->fmt.i_format = VLC_CODEC_S16N;
            i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;
            break;
        }

        // 注:android可能需要将不支持的声道类型降音混合处理为双声道类型进行播放
        /* 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 == 0 )
            return VLC_EGENERIC;
        // 该条件代码为宏定义,用于获取并判断音频量化精度是否不为0    
        if( AOUT_FMT_LINEAR( &p_sys->fmt ) )
            // 选择最小的声道数作为目标声道类型
            i_nb_channels = __MIN( i_max_channels, i_nb_channels );
        if( i_nb_channels > 5 )
        {
            if( i_nb_channels > 7 && jfields.AudioFormat.has_CHANNEL_OUT_SIDE )
                // 若超出7声道数即8声道并且Audio格式有声道超出设置,则降为7.1物理声道模式
                p_sys->fmt.i_physical_channels = AOUT_CHANS_7_1;
            else
                // 否则降为5.1物理声道数
                p_sys->fmt.i_physical_channels = AOUT_CHANS_5_1;
        } else
        {
            if( i_nb_channels == 1 )
                // 若声道数为1,则处理为左声道
                p_sys->fmt.i_physical_channels = AOUT_CHAN_LEFT;
            else
                // 否则其他情况都默认处理为双声道模式
                p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
        }

        // 注:尝试创建AudioTrack对象,若失败则可能通过调整PCM位深和声道数来重新尝试
        // 见下面的分析
        /* Try to create an AudioTrack with the most advanced channel and
         * format configuration. If AudioTrack_Create fails, try again with a
         * less advanced format (PCM S16N). If it fails again, try again with
         * Stereo channels. */
        i_ret = AudioTrack_Create( env, p_aout, p_sys->fmt.i_rate, i_at_format,
                                   p_sys->fmt.i_physical_channels );
        if( i_ret != 0 )
        {
            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( p_sys->fmt.i_physical_channels & AOUT_CHANS_5_1 )
            {
                msg_Warn( p_aout, "5.1 or 7.1 configuration failed, "
                                  "fallback to Stereo" );
                p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
            }
            else
                break;
        }
    } while( i_ret != 0 );

    if( i_ret != VLC_SUCCESS )
        return i_ret;

    uint32_t p_chans_out[AOUT_CHAN_MAX];
    memset( p_chans_out, 0, sizeof(p_chans_out) );
    // 多声道数时重排序各声道位置【FL FR FC LFE BL BR BC SL SR 】
    AudioTrack_GetChanOrder( p_sys->fmt.i_physical_channels, p_chans_out );
    // 检查channel表重排序
    p_sys->i_chans_to_reorder =
        aout_CheckChannelReorder( NULL, p_chans_out,
                                  p_sys->fmt.i_physical_channels,
                                  p_sys->p_chan_table );
    // 根据当前音频采样格式参数,初始化其音频通道(声道)数、量化精度【每个采样点使用的位数bits】、
    // 每压缩帧数据占字节数大小、每压缩帧数据包含的采样帧个数
    aout_FormatPrepare( &p_sys->fmt );
    return VLC_SUCCESS;
}

// [vlc/modules/audio_output/audiotrack.c]
/**
 * Configure and create an Android AudioTrack.
 * returns -1 on configuration error, 0 on success.
 */
static int
AudioTrack_Create( JNIEnv *env, audio_output_t *p_aout,
                   unsigned int i_rate,
                   int i_format,
                   uint16_t i_physical_channels )
{
    aout_sys_t *p_sys = p_aout->sys;
    int i_size, i_min_buffer_size, i_channel_config;

    // 通道音频物理声道数获取java层AudioFormat类格式中声道数对应配置
    switch( i_physical_channels )
    {
        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;
        case AOUT_CHANS_STEREO:
            i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;
            break;
        default:
            vlc_assert_unreachable();
    }

    // 通过调用java层AudioTrack的getMinBufferSize静态方法来获取估算的最小数据缓冲区buffer大小
    i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, i_rate,
                                                i_channel_config, i_format );
    if( i_min_buffer_size <= 0 )
    {
        msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ;
        return -1;
    }
    // 默认设置目标缓冲区大小为最小buffer缓冲区的两倍
    i_size = i_min_buffer_size * 2;

    // 创建java层AudioTrack对象的JNI层对应引用
    // 见下面分析
    /* create AudioTrack object */
    if( AudioTrack_New( env, p_aout, i_rate, i_channel_config,
                        i_format , i_size ) != 0 )
        return -1;

    // 保存AudioTrack配置信息
    p_sys->audiotrack_args.i_rate = i_rate;
    p_sys->audiotrack_args.i_channel_config = i_channel_config;
    // PCM位深
    p_sys->audiotrack_args.i_format = i_format;
    p_sys->audiotrack_args.i_size = i_size;

    return 0;
}

// [vlc/modules/audio_output/audiotrack.c]
/**
 * Create an Android AudioTrack.
 * returns -1 on error, 0 on success.
 */
static int
AudioTrack_New( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,
                int i_channel_config, int i_format, int i_size )
{
    aout_sys_t *p_sys = p_aout->sys;
    // 获取audiotrack session id值,由组件初始化可知该值默认为0
    jint session_id = var_InheritInteger( p_aout, "audiotrack-session-id" );
    // 根据配置信息创建java层AudioTrack类对象的JNI类型引用
    jobject p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC,
                                       i_rate, i_channel_config, i_format,
                                       i_size, jfields.AudioTrack.MODE_STREAM,
                                       session_id );
    if( CHECK_AT_EXCEPTION( "AudioTrack<init>" ) || !p_audiotrack )
    {
        msg_Warn( p_aout, "AudioTrack Init failed" ) ;
        return -1;
    }
    // 调用【AudioTrack.getState】获取AudioTrack状态
    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 -1;
    }

    // 重新创建AudioTrack局部引用的全局引用并保存,释放局部变量
    p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack );
    (*env)->DeleteLocalRef( env, p_audiotrack );
    if( !p_sys->p_audiotrack )
        return -1;

    return 0;
}
           

由于当前章节内容分析非常多,将其分为了两部分分析发布,此为Part 1部分,Part 2部分的文章请查看如下:

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

继续阅读