在一个设备平台上可能会支持很多种输出–包括蓝牙、功放、耳机等,音频来源也分很多种–闹钟、提示音、多媒体、电话、视频等等,对于这么多音频又如何正确的让音频从对应的设备播放出来呢?这就是音频播放策略所完成的事,本节围绕这个话题来分析一下源码。
将本节内容分为两节,一是方案商对平台音频接口的配置信息的加载和解析过程,组织成怎样的数据结构,这是音频播放策略的重要依据 , 二就是看一下如何根据播放的音频类型来选取对应的播放策略、选择合适的设备和输出接口。
1、音频接口配置信息的加载
开机过程中在拉起audio service时候,会去加载音频配置信息,然后读取内容,打开相应的输出通道,创建输出线程(playbackthread),支持的设备决定了音频播放策略。
首先,\frameworks\av\media\mediaserver\main_mediaserver.cpp会创建
AudioPolicyService::instantiate();
导致audiopolicyservice的构造函数中创建audio policy
\frameworks\av\services\audioflinger\AudioPolicyService.cpp
rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);//加载audio_policy.default.so,获取module信息(由audio_policy_hal.cpp编译而来)
rc = audio_policy_dev_open(module, &mpAudioPolicyDev); //调用加载的legacy_ap_module模块中中open函数,并获取struct audio_policy_device 结构体初始化信息
rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
&mpAudioPolicy);//create_audio_policy所对应的函数是create_legacy_ap
这里顺便说一下hw_get_module,接口实现在hardware\libhardware\hardware.c:
int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id, NULL, module); //通过hardware id获取 对应module信息
}
int hw_get_module_by_class(const char *class_id, const char *inst, //inst可以是库的部分名字
const struct hw_module_t **module)
{
...
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new copy of the library).
* We also assume that dlopen() is thread-safe.
*/
/* Loop through the configuration variants looking for a module */
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
//在初始化的variant_keys中查找想要的hardware
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
snprintf(path, sizeof(path), "%s/%s.%s.so", //优先在path2中查找
HAL_LIBRARY_PATH2, name, prop); //path2 /vendor/lib/hw
if (access(path, R_OK) == 0) break; //
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, prop);//path1 /system/lib/hw
if (access(path, R_OK) == 0) break;
} else {
snprintf(path, sizeof(path), "%s/%s.default.so",//没有在variant_keys找到库的部分名字内容就使用默认的,default
HAL_LIBRARY_PATH2, name);
if (access(path, R_OK) == 0) break;
...
}
}
if (i < HAL_VARIANT_KEYS_COUNT+1) {
/* load the module, if this fails, we're doomed, and we should not try
* to load a different variant. */
status = load(class_id, path, module); //找到对应的so时,使用load加载,获取信息
}
return status;
}
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW); //handle,打开动态库的句柄
...
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym); //获取 hardware module info的首地址,我们需要的最终结果,
//如以下形式,以audio为例
/*
struct legacy_ap_module HAL_MODULE_INFO_SYM = {
module: { //--->目标
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: AUDIO_POLICY_HARDWARE_MODULE_ID,
name: "LEGACY Audio Policy HAL",
author: "The Android Open Source Project",
methods: &legacy_ap_module_methods, //方法集合,用来初始胡硬件和获取硬件信息
dso : NULL,
reserved : {0},
},
},
};
/*
...
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
...
hmi->dso = handle;
...
*pHmi = hmi;
return status;
}
从上面的代码分析可知,HAL层有关硬件的初始化和信息描述,会被编译成对应的xxx.xxx.so放在系统的指定目录下,用于JNI层来获取这些信息。
继续看create_legacy_ap是HAL层文件\hardware\libhardware_legacy\audio\audio_policy_hal.cpp中定义的接口,大概内容如下:
struct legacy_audio_policy *lap;
...
lap->policy.set_device_connection_state = ap_set_device_connection_state;//初始化audio_policy结构体,音频策略
lap->policy.get_device_connection_state = ap_get_device_connection_state;
...
lap->policy.get_strategy_for_stream = ap_get_strategy_for_stream; //为不同音频stream获取策略
lap->policy.get_devices_for_stream = ap_get_devices_for_stream;//为不同音频stream获取devices
...
lap->policy.dump = ap_dump;
lap->policy.is_offload_supported = ap_is_offload_supported;
lap->service = service;
lap->aps_ops = aps_ops;
lap->service_client =
new AudioPolicyCompatClient(aps_ops, service); //创建对象
if (!lap->service_client) {
ret = -ENOMEM;
goto err_new_compat_client;
}
lap-