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
//初始化设置 ,ctl 包含了 name 和value , 这里的name ,就是对用codec 提供的控件的名字
//定义的path 为headphones
在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的使用。