Android audio 输出设备选择过程分析(下)

上篇的分析到audio_route,现在接个力,也算是7月的作业,再不交这个月就落下了。

audio_route_apply_and_update_path分两个过程,从函数名字都能看出来,一个是apply,一个是updata:

audio_route_apply_path

/* Apply an audio route path by name */
int audio_route_apply_path(struct audio_route *ar, const char *name)
{
    struct mixer_path *path;
    ...
    path = path_get_by_name(ar, name);
    ...
    path_apply(ar, path);
    return 0;
}

path是一个struct:

struct mixer_path {
    char *name;
    unsigned int size;
    unsigned int length;
    struct mixer_setting *setting;
};

内含struct mixer_setting:

struct mixer_setting {
    unsigned int ctl_index;
    unsigned int num_values;
    unsigned int type;
    union ctl_values value;
};

好吧,还套了个ctl_values:

union ctl_values {
    int *enumerated;
    long *integer;
    void *ptr;
    unsigned char *bytes;
};

背景知识先来这么多,先继续看代码

path_get_by_name(ar, name)这个调用返回了一个mixer_path指针。

static struct mixer_path *path_get_by_name(struct audio_route *ar,
                                           const char *name)
{
    unsigned int i;
​
    for (i = 0; i < ar->num_mixer_paths; i++)
        if (strcmp(ar->mixer_path[i].name, name) == 0)
            return &ar->mixer_path[i];
​
    return NULL;
}

看到代码简单,老夫长舒一口气!

无论怎么样我们还是需要去查证两个参数,ar和name.

name简单一些:

//audio_hw.c
strlcpy(mixer_path, use_case_table[usecase->id], MIXER_PATH_MAX_LENGTH);
...
audio_route_apply_and_update_path(adev->audio_route, mixer_path);//name的源头

mixer_path来自use_case_table[usecase->id],列举几个常见的(我常见的<(__)>):

const char * const use_case_table[AUDIO_USECASE_MAX] = {
  ...
        [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback",
        [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",
        [USECASE_AUDIO_RECORD] = "audio-record",
        [USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
        [USECASE_AUDIO_HFP_SCO_UPLINK] = "hfp-sco",
        [USECASE_VOICE_CALL] = "voice-call",        
  ...
}

播放录音,电话,甚至FM都包括在其间了。从概念上来讲,这个就是一个使用场景,从代码上来讲,就是一个字符串而已。。。

再去看复杂一些的第一个参数ar。

首先,从audio_hw.c来看,ar = adev->audio_route, adev 来自 out_set_parameters函数的out->dev。

而out:

struct stream_out *out = (struct stream_out *)stream;//stream类型audio_stream

简单做个小结就是ar即:

stream->dev->audio_route

stream一路追溯上去,来自:

audio_hw.c

adev_open_output_stream函数。完全没找到ar被赋值,被构造的地方!郁闷。。。

前面说到path_get_by_name,完全不知道ar的num_mixer_paths哪来的?

一咬牙一跺脚,来个不要脸的方法,看看num_mixer_paths在哪赋值的,好家伙,又找出来个线索:

//audio_route.c
audio_route_init
    start_tag
        path_create
            num_mixer_paths赋值

还偷偷发现,audio_route_init里用到了XML_Parser。一阵阵窃喜)——)十有八九,mixer_path就是在这个过程中被解析的。

我们现在搜索下/在哪调用的吧:

小插曲,碰到一个问题:

./msm8974/platform.c./msm8916/platform.c./msm8960/platform.c

我们的平台是msm8996,audio hal用的到底是哪个platform.c?

只好打开audio/audio/hal/Android.mk看看了

...
#filter的结果不为空(TARGET_BOARD_PLATFORM为msm 8996)
ifneq ($(filter msm8974 msm8226 msm8610 apq8084 msm8994 msm8992 msm8996 msm8996_gvmq,$(TARGET_BOARD_PLATFORM)),)//注意逗号后面,第二个参数是空的
AUDIO_PLATFORM = msm8974
...
LOCAL_SRC_FILES := \
    audio_hw.c \
    voice.c \
    platform_info.c \
    $(AUDIO_PLATFORM)/platform.c
...

好的,我知道了,用的就是./msm8974/platform.c

随着时间一点点过去,我又捋出来这么一个过程:

AudioFlinger::loadHwModule_l
...
  audio_hw/adev_open//刚才百思不得其解的adev原来是这个时候初始化的
    ...
    adev->platform = platform_init(adev);

嗯,qcom

void *platform_init(struct audio_device *adev)
{
    ...
    int retry_num = 0, snd_card_num = 0, key = 0;
    ...
      //MAX_SND_CARD为8.
      while (snd_card_num < MAX_SND_CARD) {
            //external/tinyalsa/mixer.c
            //打开/dev/snd/controlC(snd_card_num)节点,关联到mixer上
            adev->mixer = mixer_open(snd_card_num);
            ...
            //取到mixer的名字赋值给snd_card_name
            //本例中是apq8096-adp-agave-snd-card
            snd_card_name = strdup(mixer_get_name(adev->mixer));
            ...
            //hw_info.c
            my_data->hw_info = hw_info_init(snd_card_name);        
            //本例中is_i2s_ext_modem:0
            //i2s外置?
            if (platform_is_i2s_ext_modem(snd_card_name, my_data)) {
                adev->audio_route = audio_route_init(snd_card_num,
                                                     MIXER_XML_PATH_I2S);
            } else {
              //(翻译一下这里的一段注释)
              //从声卡名称中获取编解码器内部名称,并动态形成混音器路径文件名.
              //这是将来挑选任何基于编解码器名称的混音器文件的通用方法,无需更改代码。
              //此代码假设混合器文件的格式为mixer_paths_internalcodecname.xml。
              //如果此动态读取混音器文件无法打开,则它将回退到默认混音器文件,即mixer_paths.xml.
              //这样做是为了保持向后兼容性,但不是强制性的,只要混合器文件按照上述假设命名即可。
              if (platform_is_guardian(snd_card_name, my_data)) {
                    //平台是监护人?护卫者?
                  ALOGD("%s:%s test", __func__, MIXER_XML_PATH_GUARDIAN);
              }
              //snd_internal_name=“apq8096” 剩下的tmp="adp-agave-snd-card"
                snd_internal_name = strtok_r(snd_card_name, "-", &tmp);
                if (snd_internal_name != NULL)
                    //snd_internal_name=“adp” tmp="agave-snd-card"
                    snd_internal_name = strtok_r(NULL, "-", &tmp);
                if (snd_internal_name != NULL) {
                    //mixer_xml_file = "/vendor/etc/mixer_paths"
                    strlcpy(mixer_xml_file, MIXER_XML_BASE_STRING,
                        MIXER_PATH_MAX_LENGTH);
                    //strcat, 连接字符串
                    //mixer_xml_file = "/vendor/etc/mixer_paths_"
                    strlcat(mixer_xml_file, MIXER_FILE_DELIMITER,
                        MIXER_PATH_MAX_LENGTH);
                    //mixer_xml_file = "/vendor/etc/mixer_paths_adp"
                    strlcat(mixer_xml_file, snd_internal_name,
                        MIXER_PATH_MAX_LENGTH);
                    //mixer_xml_file = "/vendor/etc/mixer_paths_adp.xml"
                    strlcat(mixer_xml_file, MIXER_FILE_EXT,
                        MIXER_PATH_MAX_LENGTH);
                } else {
                    strlcpy(mixer_xml_file, MIXER_XML_DEFAULT_PATH,
                        MIXER_PATH_MAX_LENGTH);
                }
                //access()检查该进程是否将被允许读,写或测试存在的文件
                if (F_OK == access(mixer_xml_file, 0)) {
                    ALOGD("%s: Loading mixer file: %s", __func__, mixer_xml_file);
                    if (audio_extn_read_xml(adev, snd_card_num, mixer_xml_file,
                                    MIXER_XML_PATH_AUXPCM) == -ENOSYS)
                        adev->audio_route = audio_route_init(snd_card_num,
                                                       mixer_xml_file);
                } else {
                  //由于mixer_paths_adp.xml文件并不存在,所以还是用默认的xml文件
                    ALOGD("%s: Loading default mixer file", __func__);
                    //AUXPCM_BT_ENABLED没定义,所以这个方法直接等同于-ENOSYS
                    if(audio_extn_read_xml(adev, snd_card_num, MIXER_XML_DEFAULT_PATH,
                                    MIXER_XML_PATH_AUXPCM) == -ENOSYS)
                        //终于要给audio_route赋值了。。。前文找了半天啊。
                        //在audio_route_init中完成了对mixer_paths.xml的解析
                        adev->audio_route = audio_route_init(snd_card_num,
                                                       MIXER_XML_DEFAULT_PATH);
                }
            }
            ...
            adev->snd_card = snd_card_num;
            break;
      }
      retry_num = 0;
      snd_card_num++;
      mixer_close(adev->mixer);
      ...
      //my_data初始化  
      my_data->adev = adev;
      my_data->fluence_in_spkr_mode = false;
     ...
      //高通的双mic降噪相关的一些设置项,涉及不到的就可以暂时不看了
      property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, "");
      ...
      //acdb处理
      my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
      ...
      platform_acdb_init(my_data);
      ...
      //底下的就是看着眼熟,不知道干嘛用的了
      
}

终于可以回到audio_route_apply_path

第一步:path = path_get_by_name(ar, name);

ar对应着mixer_paths.xml,path_get_by_name,name即前文分析到的usecase,比如录音时候的"audio_record".

取到的结果对应mixer_paths.xml中的:

<path name="audio-record">
    <ctl name="MultiMedia1 Mixer SEC_MI2S_TX" value="1" />
</path>

简单做个解释:

  • MultiMedia1:audio-record这个usecase对应的FE(front end) PCM.

  • SEC_MI2S_TX : device连接的BE(back end) DAI.

  • Mixer:表示 DSP 路由功能

  • value:1 表示连接,0 表示断开连接

这里有个很专业的文章可供参考(可能看完底下这个,你会发现我这篇文章没有任何存在的价值了!):

https://blog.csdn.net/azloong/article/details/79383323

这个ctl的意思是把MultiMedia1 PCM与SEC_MI2S_TX这个DAI连接起来。

第二步:path_apply(ar, path);

将第一步中取到的path传给path_apply

static int path_apply(struct audio_route *ar, struct mixer_path *path)
{
    unsigned int i;
    unsigned int ctl_index;
    struct mixer_ctl *ctl;
    enum mixer_ctl_type type;
​
    ALOGD("Apply path: %s", path->name != NULL ? path->name : "none");
    for (i = 0; i < path->length; i++) {
        ctl_index = path->setting[i].ctl_index;
        ctl = index_to_ctl(ar, ctl_index);
        type = mixer_ctl_get_type(ctl);
        if (!is_supported_ctl_type(type))
            continue;
        size_t value_sz = sizeof_ctl_type(type);
        //通过memcpy直接将path->setting[i].value放入ar->mixer_state[ctl_index].new_value
        memcpy(ar->mixer_state[ctl_index].new_value.ptr, path->setting[i].value.ptr,
                   path->setting[i].num_values * value_sz);
    }
​
    return 0;
}

前面apply的过程到这里算勉强结束了,接下来该update了。

audio_route_update_path

/*
 * Operates on the specified path .. controls will be updated in the
 * order listed in the XML file
 */
static int audio_route_update_path(struct audio_route *ar, const char *name, bool reverse)                                              
{
    struct mixer_path *path;
    int32_t i, end;
    unsigned int j;
    ...
    path = path_get_by_name(ar, name);//再取一遍
    ...
    i = reverse ? (path->length - 1) : 0;//前面传的false,所以这里i = 0;
    end = reverse ? -1 : (int32_t)path->length;//这里等于path的长度
    while (i != end) {
        unsigned int ctl_index;
        enum mixer_ctl_type type;
​
        ctl_index = path->setting[i].ctl_index;
​
        struct mixer_state * ms = &ar->mixer_state[ctl_index];
​
        type = mixer_ctl_get_type(ms->ctl);
        if (!is_supported_ctl_type(type)) {
            continue;
        }
        size_t value_sz = sizeof_ctl_type(type);
        /* if any value has changed, update the mixer */
        for (j = 0; j < ms->num_values; j++) {
            //用memcpy的方式更新ar->mixer_state中对应的值。
        }
      ...
}

猜你喜欢

转载自blog.csdn.net/bberdong/article/details/81210600