audio_route 分析

audio_route.c 是 android 提供的一个 audio route的so 库, 其位于 /system/media/audio_route 目录下。

libaudioroute.so 这个动态库的主要功能有一下几点: 

1.解析 /system/etc/mixer_paths.xml 配置文件

2. 对audio 的ctl 访问方式进行封装,方便提供给hardware层的audio_hw 进行调用。 

 

driver 层的codec 会通过 alsa-driver 接口提供多个ctl 节点,用于配置codec的ctl节点 。

在android 系统中,可以将这些ctl 节点,设置在 mixer_paths.xml 。 可以组合成一个path,

在调用对用 path 的name的时候,所有的ctl 都会进行调用。 

 

audio_route.c 的源码:http://androidxref.com/5.1.1_r6/xref/system/media/audio_route/

mixer_paths.xml 距离:http://androidxref.com/5.1.1_r6/xref/device/htc/flounder/ 下 mixer_paths_0.xml 

调用 libaudioroute.so的hardware层代码。 位于 device/htc 目录下。

http://androidxref.com/5.1.1_r6/xref/device/htc/flounder/audio/hal/audio_hw.c

 

代码分析

//xml的回调函数
static void start_tag(void *data, const XML_Char *tag_name,
                      const XML_Char **attr)
{
    const XML_Char *attr_name = NULL;
    const XML_Char *attr_id = NULL;
    const XML_Char *attr_value = NULL;
    struct config_parse_state *state = data;
    struct audio_route *ar = state->ar;
    unsigned int i;
    unsigned int ctl_index;
    struct mixer_ctl *ctl;
    int value;
    unsigned int id;
    struct mixer_value mixer_value;
    enum mixer_ctl_type type;

    /* Get name, id and value attributes (these may be empty) */
    for (i = 0; attr[i]; i += 2) {
        if (strcmp(attr[i], "name") == 0)
            attr_name = attr[i + 1];
        if (strcmp(attr[i], "id") == 0)
            attr_id = attr[i + 1];
        else if (strcmp(attr[i], "value") == 0)
            attr_value = attr[i + 1];
    }
 //这里查找的是名字为path的tag
    /* Look at tags */
    if (strcmp(tag_name, "path") == 0) {
        if (attr_name == NULL) {
            ALOGE("Unnamed path!");
        } else {
            if (state->level == 1) {
                /* top level path: create and stash the path */
                //创建一个path
                state->path = path_create(ar, (char *)attr_name);
            } else {
                //如果是嵌套的path,则使用调用path_add_path
                /* nested path */
                struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
                path_add_path(ar, state->path, sub_path);
            }
        }
    }
//这里解析ctl tag ,ctl tag 是带有name  和value的
    else if (strcmp(tag_name, "ctl") == 0) {
        /* Obtain the mixer ctl and value */
        ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
        if (ctl == NULL) {
            ALOGE("Control '%s' doesn't exist - skipping", attr_name);
            goto done;
        }
        switch (mixer_ctl_get_type(ctl)) {
        case MIXER_CTL_TYPE_BOOL:
        case MIXER_CTL_TYPE_INT:
            value = (int) strtol((char *)attr_value, NULL, 0);
            break;
        case MIXER_CTL_TYPE_ENUM:
            value = mixer_enum_string_to_value(ctl, (char *)attr_value);
            break;
        default:
            value = 0;
            break;
        }

        /* locate the mixer ctl in the list */
        for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
            if (ar->mixer_state[ctl_index].ctl == ctl)
                break;
        }

        if (state->level == 1) {
            /* top level ctl (initial setting) */

            type = mixer_ctl_get_type(ctl);
            if (is_supported_ctl_type(type)) {
                /* apply the new value */
                if (attr_id) {
                    /* set only one value */
                    id = atoi((char *)attr_id);
                    if (id < ar->mixer_state[ctl_index].num_values)
                        ar->mixer_state[ctl_index].new_value[id] = value;
                    else
                        ALOGE("value id out of range for mixer ctl '%s'",
                              mixer_ctl_get_name(ctl));
                } else {
                    /* set all values the same */
                    for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
                        ar->mixer_state[ctl_index].new_value[i] = value;
                }
            }
        } else {
            /* nested ctl (within a path) */
            mixer_value.ctl_index = ctl_index;
            mixer_value.value = value;
            if (attr_id)
                mixer_value.index = atoi((char *)attr_id);
            else
                mixer_value.index = -1;
            path_add_value(ar, state->path, &mixer_value);
        }
    }

done:
    state->level++;
}

//audio_route的初始化函数,传递参数为card 和 xml_paths  
struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
{
    struct config_parse_state state;
    XML_Parser parser;
    FILE *file;
    int bytes_read;
    void *buf;
    int i;
  //解析的结果保存在ar中
    struct audio_route *ar;

    ar = calloc(1, sizeof(struct audio_route));
    if (!ar)
        goto err_calloc;

 // 调用tinyalsa 提供的mixer_open 函数
    ar->mixer = mixer_open(card);
    if (!ar->mixer) {
        ALOGE("Unable to open the mixer, aborting.");
        goto err_mixer_open;
    }

    ar->mixer_path = NULL;
    ar->mixer_path_size = 0;
    ar->num_mixer_paths = 0;

    /* allocate space for and read current mixer settings */
    if (alloc_mixer_state(ar) < 0)
        goto err_mixer_state;

//根据传递进来的参数 mixer_paths 决定是否使用默认的 /system/etc/mixer_paths.xml 文件
    /* use the default XML path if none is provided */
    if (xml_path == NULL)
        xml_path = MIXER_XML_PATH;

    file = fopen(xml_path, "r");

    if (!file) {
        ALOGE("Failed to open %s", xml_path);
        goto err_fopen;
    }
 // 创建一个 xml 的handler,
    parser = XML_ParserCreate(NULL);
    if (!parser) {
        ALOGE("Failed to create XML parser");
        goto err_parser_create;
    }

    memset(&state, 0, sizeof(state));
    state.ar = ar;
    XML_SetUserData(parser, &state);
// 设置解析的callback 函数,分别是 start_tag and end_tag 
    XML_SetElementHandler(parser, start_tag, end_tag);

    for (;;) {
        buf = XML_GetBuffer(parser, BUF_SIZE);
        if (buf == NULL)
            goto err_parse;

        bytes_read = fread(buf, 1, BUF_SIZE, file);
        if (bytes_read < 0)
            goto err_parse;

 // 进行数据的解析
        if (XML_ParseBuffer(parser, bytes_read,
                            bytes_read == 0) == XML_STATUS_ERROR) {
            ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
            goto err_parse;
        }

        if (bytes_read == 0)
            break;
    }

    /* apply the initial mixer values, and save them so we can reset the
       mixer to the original values */
    audio_route_update_mixer(ar);
    save_mixer_state(ar);

    XML_ParserFree(parser);
    fclose(file);
    return ar;

err_parse:
    XML_ParserFree(parser);
err_parser_create:
    fclose(file);
err_fopen:
    free_mixer_state(ar);
err_mixer_state:
    mixer_close(ar->mixer);
err_mixer_open:
    free(ar);
    ar = NULL;
err_calloc:
    return NULL;
}

下边分析一下 mixer_paths.xml 

<mixer>
//初始化设置 ,ctl 包含了 name 和value , 这里的name ,就是对用codec 提供的控件的名字
 <!-- These are the initial mixer settings -->
 <ctl name="Headphone Jack Switch" value="0"/>
 <ctl name="Int Mic Switch" value="0"/>
 <ctl name="Mic Jack Switch" value="0"/>
 <ctl name="Mono ADC MIXR ADC1 Switch" value="0"/>
 <ctl name="Mono ADC MIXL ADC2 Switch" value="0"/>
 <ctl name="TDM1 Mode" value="Normal"/>
 <ctl name="Mono ADC Capture Volume" value="77"/>

  <!-- TODO other defaults -->

  <path name="speaker">
  </path>

//定义的path 为headphones 
  <path name="headphones">
    <ctl name="Headphone Jack Switch" value="1"/>
  </path>

  <path name="speaker-and-headphones">
    <ctl name="Headphone Jack Switch" value="1"/>
  </path>

  <path name="bt-sco-headset">
  </path>

  <path name="bt-sco-mic">
  </path>

  <path name="speaker-mic">
    <ctl name="Mono ADC Capture Volume" value="93"/>
    <ctl name="Mono ADC MIXL ADC2 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="LL Copy"/>
    <ctl name="Int Mic Switch" value="1"/>
  </path>

  <path name="handset-mic">
    <ctl name="Mono ADC Capture Volume" value="93"/>
    <ctl name="Mono ADC MIXL ADC2 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="LL Copy"/>
    <ctl name="Int Mic Switch" value="1"/>
  </path>

  <path name="headset-mic">
    <ctl name="IN2 Boost" value="1"/>
    <ctl name="Mono ADC Capture Volume" value="82"/>
    <ctl name="Mono ADC MIXR ADC1 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="RR Copy"/>
    <ctl name="Mic Jack Switch" value="1"/>
  </path>

  <path name="camcorder-mic">
    <ctl name="Mono ADC MIXL ADC2 Switch" value="1"/>
    <ctl name="Mono ADC MIXR ADC2 Switch" value="1"/>
    <ctl name="Mono DMIC L Mux" value="DMIC2"/>
    <ctl name="Mono DMIC R Mux" value="DMIC1"/>
    <ctl name="TDM1 Mode" value="Normal"/>
    <ctl name="Int Mic Switch" value="1"/>
  </path>

  <path name="voice-rec-headset-mic">
    <ctl name="IN2 Boost" value="1"/>
    <ctl name="Mono ADC Capture Volume" value="66"/>
    <ctl name="Mono ADC MIXR ADC1 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="RR Copy"/>
    <ctl name="Mic Jack Switch" value="1"/>
  </path>

  <path name="voice-rec-mic">
    <ctl name="Mono ADC MIXL ADC2 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="LL Copy"/>
    <ctl name="Int Mic Switch" value="1"/>
  </path>

  <path name="voice-speaker-mic">
    <ctl name="Mono ADC Capture Volume" value="47"/>
    <ctl name="Mono ADC MIXL ADC2 Switch" value="1"/>
    <ctl name="Mono ADC MIXR ADC2 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="Normal"/>
    <ctl name="Int Mic Switch" value="1"/>
  </path>

  <path name="voice-headset-mic">
    <ctl name="IN2 Boost" value="1"/>
    <ctl name="Mono ADC Capture Volume" value="66"/>
    <ctl name="Mono ADC MIXR ADC1 Switch" value="1"/>
    <ctl name="TDM1 Mode" value="RR Copy"/>
    <ctl name="Mic Jack Switch" value="1"/>
  </path>

</mixer>

 

在hardware 层调用

audio_route_apply_and_update_path 或者 audio_route_reset_and_update_path 的时候,会根据提供的名字更新对应path下的ctl控件的值。 

实例代码,可以查看http://androidxref.com/5.1.1_r6/xref/hardware/qcom/audio/hal/msm8974/platform.c  里边对audio_route的使用。

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值