【九】【vlc-android】vlc-aout音频流输出端源码分析

由前章节分析可知aout音频输出端对象初始化和vout视频输出端类似,则由第七章分析可知:
aout输出端对象初始化方法为【CreateDecoder】中的【p_dec->pf_aout_format_update = aout_update_format;】方法指针赋值调用的。

// [vlc/src/input/decoder.c]
// vout赋值初始化方法指针赋值
static decoder_t * CreateDecoder( vlc_object_t *p_parent,
                                  input_thread_t *p_input,
                                  const es_format_t *fmt,
                                  input_resource_t *p_resource,
                                  sout_instance_t *p_sout )
{
    
    
     // ... 省略部分代码
     decoder_t *p_dec;
     
    // 此处赋值了aout/vout的初始化方法指针
    /* Set buffers allocation callbacks for the decoders */
    p_dec->pf_aout_format_update = aout_update_format;
    p_dec->pf_vout_format_update = vout_update_format;
    // 该方法实现:获取vout端图像队列中的buffer
    // 【注:该图像队列池缓冲区其实最终是从display展示层模块获取的】
    // 此处暂不展开分析 TODO
    p_dec->pf_vout_buffer_new = vout_new_buffer;
    p_dec->pf_spu_buffer_new  = spu_new_buffer;
    
    // ... 省略部分代码
}

1、【aout_update_format】实现分析:[vlc/src/input/decoder.c]

static int aout_update_format( decoder_t *p_dec )
{
    
    
    decoder_owner_sys_t *p_owner = p_dec->p_owner;

    if( p_owner->p_aout &&
       ( !AOUT_FMTS_IDENTICAL(&p_dec->fmt_out.audio, &p_owner->fmt.audio) ||
         p_dec->fmt_out.i_codec != p_dec->fmt_out.audio.i_format ||
         p_dec->fmt_out.i_profile != p_owner->fmt.i_profile ) )
    {
    
    
        audio_output_t *p_aout = p_owner->p_aout;

        /* Parameters changed, restart the aout */
        vlc_mutex_lock( &p_owner->lock );
        p_owner->p_aout = NULL;
        vlc_mutex_unlock( &p_owner->lock );
        // 若音频格式发生变化,则停止/删除所有涉及音频输出的模块
        // 如清空音频过滤器链功能、请求停止播放、去除音频功放模块、卸载增益模块、初始化数据等
        aout_DecDelete( p_aout );

        // 若p_resource->p_aout存在并和【p_aout】相同,则持续【p_resource->p_aout】的音频输出
        input_resource_PutAout( p_owner->p_resource, p_aout );
    }

    // 若输入音频播放增益参数发生变化则重新初始化输出增益参数
    /* Check if only replay gain has changed */
    if( aout_replaygain_changed( &p_dec->fmt_in.audio_replay_gain,
                                 &p_owner->fmt.audio_replay_gain ) )
    {
    
    
        p_dec->fmt_out.audio_replay_gain = p_dec->fmt_in.audio_replay_gain;
        if( p_owner->p_aout )
        {
    
    
            p_owner->fmt.audio_replay_gain = p_dec->fmt_in.audio_replay_gain;
            // 发送增益改变事件回调
            var_TriggerCallback( p_owner->p_aout, "audio-replay-gain-mode" );
        }
    }

    if( p_owner->p_aout == NULL )
    {
    
    // 若当前未有aout模块则进行创建和初始化
        p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec;

        audio_sample_format_t format = p_dec->fmt_out.audio;
        // 根据当前音频采样格式参数,初始化其音频通道(声道)数、量化精度【每个采样点使用的位数bits】、
        // 每压缩帧数据占字节数大小、每压缩帧数据包含的采样帧个数
        aout_FormatPrepare( &format );

        // 获取是否设置了开启杜比音效
        const int i_force_dolby = var_InheritInteger( p_dec, "force-dolby-surround" );
        if( i_force_dolby &&
            format.i_physical_channels == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) )
        {
    
    
            // 设置杜比声道模式
            if( i_force_dolby == 1 )
                format.i_chan_mode |= AOUT_CHANMODE_DOLBYSTEREO;
            else /* i_force_dolby == 2 */
                format.i_chan_mode &= ~AOUT_CHANMODE_DOLBYSTEREO;
        }

        aout_request_vout_t request_vout = {
    
    
            .pf_request_vout = aout_request_vout,
            .p_private = p_dec,
        };
        audio_output_t *p_aout;

        // 创建aout输出端对象,并加载vlc中的aout输出端模块,见2小节分析
        p_aout = input_resource_GetAout( p_owner->p_resource );
        if( p_aout )
        {
    
    
            /* TODO: 3.0 HACK: we need to put i_profile inside audio_format_t
             * for 4.0 */
            if( p_dec->fmt_out.i_codec == VLC_CODEC_DTS )
                var_SetBool( p_aout, "dtshd", p_dec->fmt_out.i_profile > 0 );

            // 见3小节分析
            if( aout_DecNew( p_aout, &format,
                             &p_dec->fmt_out.audio_replay_gain,
                             &request_vout ) )
            {
    
    // 此处表明该方法执行失败,则释放/清空p_aout对象
                input_resource_PutAout( p_owner->p_resource, p_aout );
                p_aout = NULL;
            }
        }

        vlc_mutex_lock( &p_owner->lock );
        p_owner->p_aout = p_aout;

        // 复制decoder层的输出格式信息给【p_owner->fmt】
        DecoderUpdateFormatLocked( p_dec );
        // 计算当前音频的输出格式信息
        aout_FormatPrepare( &p_owner->fmt.audio );
        vlc_mutex_unlock( &p_owner->lock );

        if( p_owner->p_input != NULL )
            // 发送aout变化事件【删除或创建】回调给输入端
            input_SendEventAout( p_owner->p_input );

        if( p_aout == NULL )
        {
    
    
            msg_Err( p_dec, "failed to create audio output" );
            return -1;
        }

        p_dec->fmt_out.audio.i_bytes_per_frame =
            p_owner->fmt.audio.i_bytes_per_frame;
        p_dec->fmt_out.audio.i_frame_length =
            p_owner->fmt.audio.i_frame_length;
    }
    return 0;
}

2、input_resource_GetAout实现分析:

// [vlc/src/input/resource.c]
audio_output_t *input_resource_GetAout( input_resource_t *p_resource )
{
    
    
    audio_output_t *p_aout;

    vlc_mutex_lock( &p_resource->lock_hold );
    p_aout = p_resource->p_aout;

    if( p_aout == NULL || p_resource->b_aout_busy )
    {
    
    // 若当前没有可用aout输出端对象,则创建它
        msg_Dbg( p_resource->p_parent, "creating audio output" );
        vlc_mutex_unlock( &p_resource->lock_hold );

        // 创建方法,见该小节后续分析
        p_aout = aout_New( p_resource->p_parent );
        if( p_aout == NULL )
            return NULL; /* failed */

        vlc_mutex_lock( &p_resource->lock_hold );
        // 赋值给resource对象
        if( p_resource->p_aout == NULL )
            p_resource->p_aout = p_aout;
    }
    else
        msg_Dbg( p_resource->p_parent, "reusing audio output" );

    if( p_resource->p_aout == p_aout )
    {
    
    // 若相同则【b_aout_busy】肯定会为false,将其设置为true
        assert( !p_resource->b_aout_busy );
        p_resource->b_aout_busy = true;
    }
    vlc_mutex_unlock( &p_resource->lock_hold );
    return p_aout;
}

// [vlc/src/audio_output/output.c]
// 创建一个音频输出端对象并初始化一个对应的输出端模块
audio_output_t *aout_New (vlc_object_t *parent)
{
    
    
    vlc_value_t val, text;

    audio_output_t *aout = vlc_custom_create (parent, sizeof (aout_instance_t),
                                              "audio output");
    if (unlikely(aout == NULL))
        return NULL;

    aout_owner_t *owner = aout_owner (aout);

    vlc_mutex_init (&owner->lock);
    vlc_mutex_init (&owner->req.lock);
    vlc_mutex_init (&owner->dev.lock);
    vlc_mutex_init (&owner->vp.lock);
    vlc_viewpoint_init (&owner->vp.value);
    atomic_init (&owner->vp.update, false);
    owner->req.device = (char *)unset_str;
    owner->req.volume = -1.f;
    owner->req.mute = -1;

    vlc_object_set_destructor (aout, aout_Destructor);

    // 创建“变量”参数,并添加“变量”变化或设置的回调监听方法
    /* Audio output module callbacks */
    var_Create (aout, "volume", VLC_VAR_FLOAT);
    var_AddCallback (aout, "volume", var_Copy, parent);
    var_Create (aout, "mute", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
    var_AddCallback (aout, "mute", var_Copy, parent);
    var_Create (aout, "device", VLC_VAR_STRING);
    var_AddCallback (aout, "device", var_CopyDevice, parent);
    /* TODO: 3.0 HACK: only way to signal DTS_HD to aout modules. */
    var_Create (aout, "dtshd", VLC_VAR_BOOL);

    // 设置音频输出端相关配置信息变化的通知事件即通知java端等
    aout->event.volume_report = aout_VolumeNotify;
    aout->event.mute_report = aout_MuteNotify;
    aout->event.policy_report = aout_PolicyNotify;
    aout->event.device_report = aout_DeviceNotify;
    aout->event.hotplug_report = aout_HotplugNotify;
    aout->event.gain_request = aout_GainNotify;
    aout->event.restart_request = aout_RestartNotify;

    /* Audio output module initialization */
    aout->start = NULL;
    aout->stop = NULL;
    aout->volume_set = NULL;
    aout->mute_set = NULL;
    aout->device_select = NULL;
    // 加载音频输出模块【"audio output"】,后续章节中分析具体模块实现
    owner->module = module_need (aout, "audio output", "$aout", false);
    if (owner->module == NULL)
    {
    
    
        msg_Err (aout, "no suitable audio output module");
        vlc_object_release (aout);
        return NULL;
    }

    // 以下代码实现均为音频输出相关字段参数创建/更新/初始化:
    // 主要有音频过滤器、播放增益模式、声道输出模式、均衡器设置等
    /*
     * Persistent audio output variables
     */
    module_config_t *cfg;
    char *str;

    /* Visualizations */
    var_Create (aout, "visual", VLC_VAR_STRING);
    text.psz_string = _("Visualizations");
    var_Change (aout, "visual", VLC_VAR_SETTEXT, &text, NULL);
    val.psz_string = (char *)"";
    text.psz_string = _("Disable");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"spectrometer";
    text.psz_string = _("Spectrometer");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"scope";
    text.psz_string = _("Scope");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"spectrum";
    text.psz_string = _("Spectrum");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"vuMeter";
    text.psz_string = _("VU meter");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    /* Look for goom plugin */
    if (module_exists ("goom"))
    {
    
    
        val.psz_string = (char *)"goom";
        text.psz_string = (char *)"Goom";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
    /* Look for libprojectM plugin */
    if (module_exists ("projectm"))
    {
    
    
        val.psz_string = (char *)"projectm";
        text.psz_string = (char*)"projectM";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
    /* Look for VSXu plugin */
    if (module_exists ("vsxu"))
    {
    
    
        val.psz_string = (char *)"vsxu";
        text.psz_string = (char*)"Vovoid VSXu";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
    /* Look for glspectrum plugin */
    if (module_exists ("glspectrum"))
    {
    
    
        val.psz_string = (char *)"glspectrum";
        text.psz_string = (char*)"3D spectrum";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
    str = var_GetNonEmptyString (aout, "effect-list");
    if (str != NULL)
    {
    
    
        var_SetString (aout, "visual", str);
        free (str);
    }

    var_Create (aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
    var_AddCallback (aout, "audio-filter", FilterCallback, NULL);
    text.psz_string = _("Audio filters");
    var_Change (aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL);

    var_Create (aout, "viewpoint", VLC_VAR_ADDRESS );
    var_AddCallback (aout, "viewpoint", ViewpointCallback, NULL);

    var_Create (aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
    text.psz_string = _("Audio visualizations");
    var_Change (aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL);

    /* Replay gain */
    var_Create (aout, "audio-replay-gain-mode",
                VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    text.psz_string = _("Replay gain");
    var_Change (aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL);
    cfg = config_FindConfig("audio-replay-gain-mode");
    if (likely(cfg != NULL))
        for (unsigned i = 0; i < cfg->list_count; i++)
        {
    
    
            val.psz_string = (char *)cfg->list.psz[i];
            text.psz_string = vlc_gettext(cfg->list_text[i]);
            var_Change (aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
                            &val, &text);
        }

    /* Stereo mode */
    var_Create (aout, "stereo-mode", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    owner->initial_stereo_mode = var_GetInteger (aout, "stereo-mode");

    var_AddCallback (aout, "stereo-mode", StereoModeCallback, NULL);
    vlc_value_t txt;
    txt.psz_string = _("Stereo audio mode");
    var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);

    /* Equalizer */
    var_Create (aout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (aout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
    var_Create (aout, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);

    return aout;
}

3、aout_DecNew实现分析: [vlc/src/input/decoder.c]

/**
 * Creates an audio output
 */
int aout_DecNew( audio_output_t *p_aout,
                 const audio_sample_format_t *p_format,
                 const audio_replay_gain_t *p_replay_gain,
                 const aout_request_vout_t *p_request_vout )
{
    
    
    if( p_format->i_bitspersample > 0 )
    {
    
    
        // 检查音频格式声道是否有效
        /* Sanitize audio format, input need to have a valid physical channels
         * layout or a valid number of channels. */
        int i_map_channels = aout_FormatNbChannels( p_format );
        if( ( i_map_channels == 0 && p_format->i_channels == 0 )
           || i_map_channels > AOUT_CHAN_MAX || p_format->i_channels > INPUT_CHAN_MAX )
        {
    
    
            msg_Err( p_aout, "invalid audio channels count" );
            return -1;
        }
    }

    // 音频采样率不能超过384000Hz即最大384KHz
    if( p_format->i_rate > 384000 )
    {
    
    
        msg_Err( p_aout, "excessive audio sample frequency (%u)",
                 p_format->i_rate );
        return -1;
    }
    // 采样率最低不能低于4KHz
    if( p_format->i_rate < 4000 )
    {
    
    
        msg_Err( p_aout, "too low audio sample frequency (%u)",
                 p_format->i_rate );
        return -1;
    }

    aout_owner_t *owner = aout_owner(p_aout);

    /* TODO: reduce lock scope depending on decoder's real need */
    aout_OutputLock (p_aout);

    // 创建音频输出流模块
    /* Create the audio output stream */
    owner->volume = aout_volume_New (p_aout, p_replay_gain);

    atomic_store (&owner->restart, 0);
    owner->input_format = *p_format;
    owner->mixer_format = owner->input_format;
    owner->request_vout = *p_request_vout;

    var_Change (p_aout, "stereo-mode", VLC_VAR_SETVALUE,
                &(vlc_value_t) {
    
     .i_int = owner->initial_stereo_mode }, NULL);

    owner->filters_cfg = AOUT_FILTERS_CFG_INIT;
    // 加载aout输出端信息,启动aout输出端组件模块,见3.1小节分析
    if (aout_OutputNew (p_aout, &owner->mixer_format, &owner->filters_cfg))
        goto error;
    // 设置当前音频播放格式给功放软件模块,见3.2小节分析    
    aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);

    // 创建音频过滤"输入"通道:如重采样设置等
    /* Create the audio filtering "input" pipeline */
    owner->filters = aout_FiltersNew (p_aout, p_format, &owner->mixer_format,
                                      &owner->request_vout,
                                      &owner->filters_cfg);
    if (owner->filters == NULL)
    {
    
    
        aout_OutputDelete (p_aout);
error:
        aout_volume_Delete (owner->volume);
        owner->volume = NULL;
        aout_OutputUnlock (p_aout);
        return -1;
    }


    owner->sync.end = VLC_TS_INVALID;
    owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
    owner->sync.discontinuity = true;
    aout_OutputUnlock (p_aout);

    atomic_init (&owner->buffers_lost, 0);
    atomic_init (&owner->buffers_played, 0);
    atomic_store (&owner->vp.update, true);
    return 0;
}

3.1、aout_OutputNew实现分析:

/**
 * Starts an audio output stream.
 * \param fmt audio output stream format [IN/OUT]
 * \warning The caller must hold the audio output lock.
 */
int aout_OutputNew (audio_output_t *aout, audio_sample_format_t *restrict fmt,
                    aout_filters_cfg_t *filters_cfg)
{
    
    
    aout_OutputAssertLocked (aout);

    // 音频通道类型
    audio_channel_type_t input_chan_type = fmt->channel_type;
    // 是否强制立体声模式,默认0
    int i_forced_stereo_mode = AOUT_VAR_CHAN_UNSET;
    // 输入音频格式通道数
    unsigned i_nb_input_channels = fmt->i_channels;

    /* Ideally, the audio filters would be created before the audio output,
     * and the ideal audio format would be the output of the filters chain.
     * But that scheme would not really play well with digital pass-through. */
    // 该条件代码为宏定义,用于获取并判断音频量化精度是否不为0
    if (AOUT_FMT_LINEAR(fmt))
    {
    
    // 不为0
        if (fmt->channel_type == AUDIO_CHANNEL_TYPE_BITMAP
         && aout_FormatNbChannels(fmt) == 0)
        {
    
    
            // 输出格式声道类型未知,则默认使用WAVE声道类型的物理参数
            /* The output channel map is unknown, use the WAVE one. */
            assert(fmt->i_channels > 0);
            aout_SetWavePhysicalChannels(fmt);
        }

        if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
        {
    
    
            // 立体环绕声道,设置最大的7.1物理声道类型
            // 注:输出模块也可以自由选择少些声道来渲染
            /* Set the maximum of channels to render ambisonics contents. The
             * aout module will still be free to select less channels in order
             * to respect the sink setup. */
            fmt->i_physical_channels = AOUT_CHANS_7_1;
        }

        // 根据采样精度来选择设置为vlc标记格式类型
        /* Try to stay in integer domain if possible for no/slow FPU. */
        fmt->i_format = (fmt->i_bitspersample > 16) ? VLC_CODEC_FL32
                                                    : VLC_CODEC_S16N;

        // 获取输入端是否设置了立体声道模式
        i_forced_stereo_mode = var_GetInteger (aout, "stereo-mode");
        if (i_forced_stereo_mode != AOUT_VAR_CHAN_UNSET)
        {
    
    
            // 若设置了立体声道模式,则根据类型来设置物理声道数类型
            if (i_forced_stereo_mode == AOUT_VAR_CHAN_LEFT
             || i_forced_stereo_mode == AOUT_VAR_CHAN_RIGHT)
                fmt->i_physical_channels = AOUT_CHAN_CENTER;
            else
                // 双声道
                fmt->i_physical_channels = AOUT_CHANS_STEREO;
        }

        // 初始化准备采样格式的信息:帧数、每帧大小等
        aout_FormatPrepare (fmt);
        assert (aout_FormatNbChannels(fmt) > 0);
    }

    // 当前输出端是否启用耳机
    aout->current_sink_info.headphones = false;

    // 启动aout输出端组件开始方法执行,由第2小节中【aout_New】创建aout对象流程中知晓:
    // 该方法是通过加载了aout输出组件模块进行赋值了,
    // 因此该分析会在后续aout输出端模块组件章节中分析 TODO
    if (aout->start (aout, fmt))
    {
    
    
        msg_Err (aout, "module not functional");
        return -1;
    }

    // 初始化准备立体声模式即声道数的确定并发送通知消息出去
    aout_PrepareStereoMode (aout, fmt, filters_cfg, input_chan_type,
                            i_nb_input_channels, i_forced_stereo_mode);

    aout_FormatPrepare (fmt);
    assert (fmt->i_bytes_per_frame > 0 && fmt->i_frame_length > 0);
    aout_FormatPrint (aout, "output", fmt);
    return 0;
}

3.2、aout_volume_SetFormat实现分析:

/**
 * Selects the current sample format for software amplification.
 */
int aout_volume_SetFormat(aout_volume_t *vol, vlc_fourcc_t format)
{
    
    
    if (unlikely(vol == NULL))
        return -1;

    audio_volume_t *obj = &vol->object;
    if (vol->module != NULL)
    {
    
    // 若当前音频输出模块组件存在并且它持有的音频格式
    // 和当前待播放音频格式相同则继续使用当前模块组件。
    // 否则卸载当前模块组件
        if (obj->format == format)
        {
    
    
            msg_Dbg (obj, "retaining sample format");
            return 0;
        }
        msg_Dbg (obj, "changing sample format");
        module_unneed(obj, vol->module);
    }

    obj->format = format;
    // 重新加载新的音频播放组件模块
    vol->module = module_need(obj, "audio volume", NULL, false);
    if (vol->module == NULL)
        return -1;
    return 0;
}

由此此前第五七章分析中涉及aout输出端的TODO部分均已基本分析完成了。

分析结束,aout输出端组件模块分析请见后续分析

猜你喜欢

转载自blog.csdn.net/u012430727/article/details/110691850