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
//初始化设置 ,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的使用。
更多相关文章
- android initlogo.rle 在32位LCD上显示
- android 【点击输入框调出输入法前的】输入框获取焦点和输入法的
- Android菜鸟笔记-调用相机拍照后返回照片过小的问题
- android类作用整理
- android NDK JNI so文件的制作和使用
- Android中的Service使用
- Android(安卓)Camera 运行流程
- 将Gsensor lis301 driver 升级到 lis331 driver 过程总结,以及and
- Js Android(安卓)交互