当linux kenerl启动完成后,会启动android的init进程(system/core/init/init.c)。
int main(int argc, char **argv)
{
.....
init_parse_config_file("/init.rc");
.....
}
init.rc文件中保存了许多系统启动时需要启动的服务。其中就有多媒体服务mediaserver的启动:
service media /system/bin/mediaserver
此服务在文件frameworks/base/media/mediaserver/main_mediaserver.cpp中定义,而音频子系统的两大本地服务AudioFlinger和AudioPolicyService就是在此启动的。
int main(int argc, char** argv)
{
.....
AudioFlinger::instantiate(); //实例化AudioFlinger
.....
AudioPolicyService::instantiate(); //实例化AudioPolicyService
.....
}
根据上文分析,将调用AudioFlinger::instantiate()函数实例化AudioFlinger。但是AudioFlinger.cpp中并没有找到此函数,那必然在其父类中。AudioFlinger类有很多父类,一时难以确定instantiate()到底在哪个父类中定义的。直接搜索吧!
grep -rn "instantiate" frameworks/base/
很快找到instantiate()函数的定义处在./frameworks/base/include/binder/BinderService.h头文件中
template
class BinderService
{
public:
static status_t publish() {
sp sm(defaultServiceManager());
return sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
......
static void instantiate() { publish(); }
.....
}
}
这里用到了模板,需要确定SERVICE是什么东东。
AudioFlinger类是在AudioFlinger.h中定义的,而恰好包含了头文件BinderService.h。
class AudioFlinger :
public BinderService ,
public BnAudioFlinger
{
friend class BinderService;
public:
static char const* getServiceName() { return "media.audio_flinger"; }
.....
}
原来AudioFlinger类继承了BinderService类,同时把自己(AudioFlinger)传递给SERVICE。而addService函数第一个参数调用了AudioFlinger类的静态成员函数getServiceName()获取AudioFlinger的服务名称;其第二个参数便是创建了一个AudioFlinger的实例。至此,明白了实例化函数instantiate()就是要向服务管理器注册的服务是AudioFlinger。
既然此时实例化了AudioFlinger,那么看看AudioFlinger类的构造函数具体做了哪些初始化工作。
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
mBtNrecIsOff(false)
{
}
此构造函数做了一些无关紧要的事情,不管它。既然AudioFlinger服务是第一次启动,则将调到函数AudioFlinger::onFirstRef(至于为什么,我还没有搞明白,可以通过log信息确信确实是这么回事)。
void AudioFlinger::onFirstRef()
{
......
for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
const hw_module_t *mod;
audio_hw_device_t *dev;
rc = load_audio_interface(audio_interfaces[i], &mod,&dev);
.....
mAudioHwDevs.push(dev); // 把通过load_audio_interface()函数获得的设备存入元素为audio_hw_device_t
// 类型的模板变量mAudioHwDevs中
.....
}
看到load_audio_interface()函数的名字,知晓,应当是加载音频接口的。
static int load_audio_interface(const char *if_name, const hw_module_t **mod,
audio_hw_device_t **dev)
{
......
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, mod);
if (rc)
goto out;
rc = audio_hw_device_open(*mod, dev);
.....
}
首先通过函数hw_get_module_by_class获取ID号为AUDIO_HARDWARE_MODULE_ID的音频模块,此ID在头文件hardware/libhardware/include/hardware/audio.h中定义。此头文件中定义了一个十分重要的结构体struct audio_hw_device,其中包含了许多函数接口(函数指针):
struct audio_hw_device {
struct hw_device_t common;
/**
* used by audio flinger to enumerate what devices are supported by
* each audio_hw_device implementation.
*
* Return value is a bitmask of 1 or more values of audio_devices_t
*/
uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
/**
* check to see if the audio hardware interface has been initialized.
* returns 0 on success, -ENODEV on failure.
*/
int (*init_check)(const struct audio_hw_device *dev);
......
/* set/get global audio parameters */
int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
.....
/** This method creates and opens the audio hardware output stream */
int (*open_output_stream)(struct audio_hw_device *dev, uint32_t devices,
int *format, uint32_t *channels,
uint32_t *sample_rate,
struct audio_stream_out **out);
......
/** This method creates and opens the audio hardware input stream */
int (*open_input_stream)(struct audio_hw_device *dev, uint32_t devices,
int *format, uint32_t *channels,
uint32_t *sample_rate,
audio_in_acoustics_t acoustics,
struct audio_stream_in **stream_in);
.....
}
ID为AUDIO_HARDWARE_MODULE_ID的音频模块到底在哪儿定义的那?既然是HAL层模块,必定在hardware目录下定义的
$ grep -rn AUDIO_HARDWARE_MODULE_ID hardware/
hardware/libhardware_legacy/audio/audio_hw_hal.cpp:602:id: AUDIO_HARDWARE_MODULE_ID,
hardware/libhardware/modules/audio/audio_hw.c:435: .id = AUDIO_HARDWARE_MODULE_ID,
hardware/libhardware/include/hardware/audio.h:37:
#define AUDIO_HARDWARE_MODULE_ID "audio"
/** convenience API for opening and closing a supported device */
static inline int audio_hw_device_open(const struct hw_module_t* module,
struct audio_hw_device** device)
{
return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
(struct hw_device_t**)device);
}
struct hw_module_t是在hardware/libhardware/include/hardware/hardware.h头文件中定义的,其中嵌套了struct hw_module_methods_t。此结构体很简单,只有一个函数指针open
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
在确定具体模块后,很容易确定open函数指针的具体实现
struct legacy_audio_module HAL_MODULE_INFO_SYM = {
module: {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: AUDIO_HARDWARE_MODULE_ID,
name: "LEGACY Audio HW HAL",
author: "The Android Open Source Project",
methods: &legacy_audio_module_methods,
dso : NULL,
reserved : {0},
},
},
};
static struct hw_module_methods_t legacy_audio_module_methods = {
open: legacy_adev_open
};
static int legacy_adev_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
struct legacy_audio_device *ladev;
int ret;
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) // 看来对应宏AUDIO_HARDWARE_INTERFACE,除了用来判断希望打开的是否音频硬件接口外,并没有做什么更多的事情
return -EINVAL;
.....
ladev->device.get_supported_devices = adev_get_supported_devices;
ladev->device.init_check = adev_init_check;
.....
ladev->device.open_output_stream = adev_open_output_stream;
ladev->device.close_output_stream = adev_close_output_stream;
ladev->device.open_input_stream = adev_open_input_stream;
ladev->device.close_input_stream = adev_close_input_stream;
.....
ladev->hwif = createAudioHardware();
.....
*device = &ladev->device.common; // 将当前设备信息层层返回给回调函数,最后返回到
// load_audio_interface()函数,并保存在
// mAudioHwDevs模板变量中,供AudioFlinger类使用。
return 0;
}
这里主要做了一些初始化工作,即给函数指针提供具体实现函数;但createAudioHardware()应该做了更多的事情。
先从函数createAudioHardware()的返回值入手。struct legacy_audio_device的定义如下:
struct legacy_audio_device {
struct audio_hw_device device;
struct AudioHardwareInterface *hwif;
};
原来createAudioHardware()的返回值是一个硬件设备接口AudioHardwareInterface。
类AudioHardwareInterface正好在audio_hw_hal.cpp文件中所包含的头文件hardware_legacy/AudioHardwareInterface.h中定义的虚类(结构体能调到类,还是头一遭见到,虽然结构体和类长得很象)。那么我很想知道createAudioHardware()具体做了哪些事情。
首先需要确定函数createAudioHardware()的定义在哪儿?有几处定义?调用的具体是哪一个?
AudioHardwareInterface.h头文件中对createAudioHardware函数的声明,没有包含在任何类中,而仅仅包含在名字空间android_audio_legacy中,这和audio_hw_hal.cpp同在一个名字空间中。
namespace android_audio_legacy {
.....
extern "C" AudioHardwareInterface* createAudioHardware(void);
}; // namespace android
经搜索,发现createAudioHardware()函数有四处定义。
$ grep -rn createAudioHardware hardware/ --exclude-dir=.svn
hardware/alsa_sound/AudioHardwareALSA.cpp:45:
android_audio_legacy::AudioHardwareInterface *createAudioHardware(void) {
hardware/msm7k/libaudio-qsd8k/AudioHardware.cpp:2021:
extern "C" AudioHardwareInterface* createAudioHardware(void) {
hardware/msm7k/libaudio-qdsp5v2/AudioHardware.cpp:337:
extern "C" AudioHardwareInterface* createAudioHardware(void) {
hardware/msm7k/libaudio/AudioHardware.cpp:1132:
extern "C" AudioHardwareInterface* createAudioHardware(void) {
只有AudioHardwareALSA.cpp文件中包含了头文件hardware_legacy/AudioHardwareInterface.h,并且返回值是android_audio_legacy名字空间的AudioHardwareInterface类对象。则createAudioHardware函数的具体实现很可能是它了,通过log信息证明了这一点。
进入AudioHardwareALSA.cpp,不难看出,此函数,最后会通过执行代码如下代码创建AudioHardwareALSA类对象。
return new AudioHardwareALSA();
AudioHardwareALSA类的构造函数如下:
AudioHardwareALSA::AudioHardwareALSA() :
mALSADevice(0),
mAcousticDevice(0)
{
......
int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);
if (err == 0) {
mALSADevice = (alsa_device_t *)device;
mALSADevice->init(mALSADevice, mDeviceList);
.....
err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
.....
}
宏ALSA_HARDWARE_MODULE_ID是在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的。
模块所对应的结构体类型为hw_module_t,在头文件hardware/libhardware/include/hardware/hardware.h中定义。
在构造函数中,首先调用函数hw_get_module()获取ID为ALSA_HARDWARE_MODULE_ID的ALSA硬件模块,看来即将进入庞大而又功能强大的ALSA音频子系统了!
经过搜索,很快确定ID为ALSA_HARDWARE_MODULE_ID的ALSA硬件抽象层的具体实现在文件hardware/alsa_sound/alsa_default.cpp中。
$ grep -rn ALSA_HARDWARE_MODULE_ID hardware/ --exclude-dir=.svn
hardware/alsa_sound/AudioHardwareALSA.h:39:#define ALSA_HARDWARE_MODULE_ID "alsa"
hardware/alsa_sound/alsa_default.cpp:59:
id : ALSA_HARDWARE_MODULE_ID,
hardware/alsa_sound/AudioHardwareALSA.cpp:150:
int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,
则很快找到此模块的具体内容如下:
extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : ALSA_HARDWARE_MODULE_ID,
name : "ALSA module",
author : "Wind River",
methods : &s_module_methods,
dso : 0,
reserved : { 0, },
};
s_module_methods函数的实现如下:
static hw_module_methods_t s_module_methods = {
open : s_device_open
};
s_device_open函数的实现如下:
static int s_device_open(const hw_module_t* module, const char* name, //有些困惑,
// 此open函数实现中并没有对调用者传递下来的name(ALSA_HARDWARE_NAME)作如何处理。
hw_device_t** device) //device存储返回的模块信息
{
alsa_device_t *dev;
dev = (alsa_device_t *) malloc(sizeof(*dev));
if (!dev) return -ENOMEM;
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t *) module;
dev->common.close = s_device_close;
dev->init = s_init;
dev->open = s_open;
dev->close = s_close;
dev->route = s_route;
*device = &dev->common; // 把此模块信息返回给调用者
return 0;
}
经过上述分析,知道了module->methods->open函数具体调用流程了。
然后对ALSA硬件抽象层模块做了初始化的工作。
这里用到一个结构体变量mALSADevice,它在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的struct alsa_device_t变量。
struct alsa_device_t {
hw_device_t common;
status_t (*init)(alsa_device_t *, ALSAHandleList &);
status_t (*open)(alsa_handle_t *, uint32_t, int);
status_t (*close)(alsa_handle_t *);
status_t (*route)(alsa_handle_t *, uint32_t, int);
};
此结构体仅仅提供了一些函数调用接口,在这里都有了具体的实现。则mALSADevice->init()将调到s_init()函数中。
static status_t s_init(alsa_device_t *module, ALSAHandleList &list)
{
list.clear();
snd_pcm_uframes_t bufferSize = _defaultsOut.bufferSize;
for (size_t i = 1; (bufferSize & ~i) != 0; i <<= 1)
bufferSize &= ~i;
_defaultsOut.module = module;
_defaultsOut.bufferSize = bufferSize;
list.push_back(_defaultsOut);
bufferSize = _defaultsIn.bufferSize;
.....
list.push_back(_defaultsIn);
.....
}
这里会把_defaultsOut和_defaultsIn东东保存在ALSA句柄列表ALSAHandleList中。
首先需要明确_defaultsOut和_defaultsIn具体是什么东东。
static alsa_handle_t _defaultsOut = {
module : 0,
devices : android_audio_legacy::AudioSystem::DEVICE_OUT_ALL, // 支持的所有
// 输出音频设备
curDev : 0,
curMode : 0,
handle : 0, // PCM节点
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channels : 2,
sampleRate : DEFAULT_SAMPLE_RATE,
latency : 200000, // Desired Delay in usec
bufferSize : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples
modPrivate : 0,
};
static alsa_handle_t _defaultsIn = {
module : 0,
devices : android_audio_legacy::AudioSystem::DEVICE_IN_ALL, // 支持的所有
// 输入音频设备
curDev : 0,
curMode : 0,
handle : 0, // PCM节点
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channels : 2, // 声道数: 1表示单声道,2表示立体声。如果与实际使用的USB AUDIO设备参数
// 的不一致,将导致USB AUDIO设备不能使用。
sampleRate : DEFAULT_SAMPLE_RATE, // 采样率,如果与实际使用的USB AUDIO设备参数
// 的不一致,将导致声音失真
latency : 250000, // Desired Delay in usec
bufferSize : 2048, // Desired Number of samples
modPrivate : 0,
};
那ALSAHandleList又是什么东东?
ALSAHandleList在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的List模板变量。
typedef List
原来就是struct asla_handle_t的一个列表而已,而_defaultsOut和_defaultsIn正是这样的结构体变量。
struct alsa_handle_t {
alsa_device_t * module;
uint32_t devices;
uint32_t curDev;
int curMode;
snd_pcm_t * handle; // PCM节点
snd_pcm_format_t format;
uint32_t channels;
uint32_t sampleRate;
unsigned int latency; // Delay in usec
unsigned int bufferSize; // Size of sample buffer
void * modPrivate;
};
ALSA硬件抽象层正是这样获得了输出音频通道和输入音频通道的相关初始化硬件参数,以后在使用中并不试图改变这些硬件参数(针对真能手机和平板来说,也却是不需要改变)。因此,在扩展android系统功能,为其添加对USB AUDIO设备的支持时,就不得不考虑时事改变channels和sampleRate这两个硬件参数的值。
至此,AudioFlinger服务首次启动过程分析完毕!
AudioPolicyService服务的启动流程类似于AudioFlinger服务的启动过程,将简要分析。
先看下AudioPolicyService类的定义(AudioPolicyService.h)(提供此类的定义,主要是为下面instantiate()函数服务的):
class AudioPolicyService :
public BinderService , // 继承了BinderService类,
// 并把自己(AudioPolicyService)传递给
// BinderService。
public BnAudioPolicyService,
// public AudioPolicyClientInterface,
public IBinder::DeathRecipient
{
friend class BinderService;
public:
// for BinderService
static const char *getServiceName() { return "media.audio_policy"; }
.....
}
根据前面的分析,晓得将通过调用如下代码启动AudioPolicyService服务。
AudioPolicyService::instantiate();
此代码最后将调到AudioPolicyService类的构造函数
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
{
char value[PROPERTY_VALUE_MAX];
const struct hw_module_t *module;
int forced_val;
int rc;
Mutex::Autolock _l(mLock);
// start tone playback thread
mTonePlaybackThread = new AudioCommandThread(String8(""));
// start audio commands thread
mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
/* instantiate the audio policy manager */
rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
if (rc)
return;
rc = audio_policy_dev_open(module, &mpAudioPolicyDev);
LOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc));
if (rc)
return;
rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
&mpAudioPolicy);
LOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc));
if (rc)
return;
rc = mpAudioPolicy->init_check(mpAudioPolicy);
.....
}
AudioCommandThread类在头文件frameworks/base/services/audioflinger/AudioPolicyService.h中定义
class AudioCommandThread : public Thread {
是AudioPolicyService类的私有子类。
AudioCommandThread线程类创建了对象后,将进入死循环中,等待要处理的事件传来。
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
nsecs_t waitTime = INT64_MAX;
mLock.lock();
while (!exitPending())
{
while(!mAudioCommands.isEmpty()) {
.....
switch (command->mCommand) {
.....
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
LOGV("AudioCommandThread() processing set parameters string %s, io %d",
data->mKeyValuePairs.string(), data->mIO);
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
.....
}
ID号为AUDIO_POLICY_HARDWARE_MODULE_ID的模块也有两处具体实现,同样通过log信息,确认调用的是libhardware_legacy模块中的AUDIO_POLICY_HARDWARE_MODULE_ID子模块的具体实现。
$ grep -rn AUDIO_POLICY_HARDWARE_MODULE_ID hardware/ --exclude-dir=.svn
hardware/libhardware_legacy/audio/audio_policy_hal.cpp:414:
id: AUDIO_POLICY_HARDWARE_MODULE_ID,
hardware/libhardware/modules/audio/audio_policy.c:318:
.id = AUDIO_POLICY_HARDWARE_MODULE_ID,
audio_policy_hal.cpp文件中定义的AUDIO_POLICY_HARDWARE_MODULE_ID模块如下:
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},
},
},
};
(3)再然后调用audio_policy_dev_open()函数(在头文件hardware/libhardware/include/hardware/audio_policy.h中定义)。
首先分析函数参数:第一个参数就是上面获取的模块,第二个参数mpAudioPolicyDev是struct audio_policy_device 指针变量,在头文件AudioPolicyService.h中定义。而struct audio_policy_device是在头文件audio_policy.h中定义的。
struct audio_policy_device {
struct hw_device_t common;
int (*create_audio_policy)(const struct audio_policy_device *device,
struct audio_policy_service_ops *aps_ops,
void *service,
struct audio_policy **ap);
.....
}
最后看下audio_policy_dev_open()函数的实现
/** convenience API for opening and closing a supported device */
static inline int audio_policy_dev_open(const hw_module_t* module,
struct audio_policy_device** device)
{
return module->methods->open(module, AUDIO_POLICY_INTERFACE,
(hw_device_t**)device);
}
由上述分析可知,open函数指针就指向legacy_ap_dev_open()函数。
static int legacy_ap_dev_open(const hw_module_t* module, const char* name,
hw_device_t** device) // 参数device保存返回的模块信息
{
struct legacy_ap_device *dev;
if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0)// 参数name(AUDIO_POLICY_INTERFACE)
// 就这点用处
return -EINVAL;
dev = (struct legacy_ap_device *)calloc(1, sizeof(*dev));
if (!dev)
return -ENOMEM;
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast(module);
dev->device.common.close = legacy_ap_dev_close;
dev->device.create_audio_policy = create_legacy_ap;
dev->device.destroy_audio_policy = destroy_legacy_ap;
*device = &dev->device.common; // 将当前模块具体信息赋值给device,并回馈给调用者
return 0;
}
namespace {
struct audio_policy_service_ops aps_ops = {
open_output : aps_open_output,
open_duplicate_output : aps_open_dup_output,
close_output : aps_close_output,
suspend_output : aps_suspend_output,
restore_output : aps_restore_output,
open_input : aps_open_input,
close_input : aps_close_input,
set_stream_volume : aps_set_stream_volume,
set_stream_output : aps_set_stream_output,
set_parameters : aps_set_parameters,
get_parameters : aps_get_parameters,
start_tone : aps_start_tone,
stop_tone : aps_stop_tone,
set_voice_volume : aps_set_voice_volume,
move_effects : aps_move_effects,
};
}; // namespace
struct audio_policy {
/*
* configuration functions
*/
/* indicate a change in device connection status */
int (*set_device_connection_state)(struct audio_policy *pol,
audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address);
.....
/* check proper initialization */
int (*init_check)(const struct audio_policy *pol);
.....
}
接下来看看create_audio_policy()函数指针的具体实现:
static int create_legacy_ap(const struct audio_policy_device *device,
struct audio_policy_service_ops *aps_ops,
void *service,
struct audio_policy **ap)
{
struct legacy_audio_policy *lap;
int ret;
if (!service || !aps_ops)
return -EINVAL;
lap = (struct legacy_audio_policy *)calloc(1, sizeof(*lap));
if (!lap)
return -ENOMEM;
lap->policy.set_device_connection_state = ap_set_device_connection_state;
......
lap->policy.init_check = ap_init_check;
lap->policy.get_output = ap_get_output;
lap->policy.start_output = ap_start_output;
lap->policy.stop_output = ap_stop_output;
lap->policy.release_output = ap_release_output;
lap->policy.get_input = ap_get_input;
lap->policy.start_input = ap_start_input;
lap->policy.stop_input = ap_stop_input;
lap->policy.release_input = ap_release_input;
.....
lap->service = service; // APS
lap->aps_ops = aps_ops; // 在APS中实现
lap->service_client =
new AudioPolicyCompatClient(aps_ops, service);
if (!lap->service_client) {
ret = -ENOMEM;
goto err_new_compat_client;
}
lap->apm = createAudioPolicyManager(lap->service_client);
......
*ap = &lap->policy; // 将当前音频策略的配置的地址赋值给*ap,并返回给mpAudioPolicy
......
}
此函数中创建了重要对象:AudioPolicyCompatClient类对象和createAudioPolicyManager函数创建音频策略管理器,需要分析下。
先分析AudioPolicyCompatClient类对象的创建。此类在头文件hardware/libhardware_legacy/audio/AudioPolicyCompatClient.h中定义。
namespace android_audio_legacy {
class AudioPolicyCompatClient : public AudioPolicyClientInterface {
// 父类是AudioPolicyClientInterface,与下文中提到的
// AudioPolicyManagerBase::
// AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)类型一致
public:
AudioPolicyCompatClient(struct audio_policy_service_ops *serviceOps,
void *service) :
mServiceOps(serviceOps) , mService(service) {} // serviceOps = aps_ops,
// service = this(APS)
......
private:
struct audio_policy_service_ops* mServiceOps;
void* mService;
......
}
此构造函数主要初始化两个私有化变量mServiceOps和mService,并把创建的对象作为函数createAudioPolicyManager的唯一参数,来创建音频策略管理器。
然而,函数createAudioPolicyManager有多个定义,搜索结果如下:
$ grep -rn createAudioPolicyManager hardware/ --exclude-dir=.svn
hardware/alsa_sound/AudioPolicyManagerALSA.cpp:31:
extern "C" android_audio_legacy::AudioPolicyInterface*
createAudioPolicyManager(
android_audio_legacy::AudioPolicyClientInterface *clientInterface)
hardware/libhardware_legacy/audio/AudioPolicyManagerDefault.cpp:24:
extern "C" AudioPolicyInterface*
createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
hardware/msm7k/libaudio-qsd8k/AudioPolicyManager.cpp:39:
extern "C" AudioPolicyInterface*
createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
hardware/msm7k/libaudio-qdsp5v2/AudioPolicyManager.cpp:39:
extern "C" AudioPolicyInterface*
createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
hardware/msm7k/libaudio/AudioPolicyManager.cpp:35:
extern "C" AudioPolicyInterface*
createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
函数createAudioPolicyManager虽然有多个定义,但是它们最后将创建AudioPolicyManagerBase类对象,以AudioPolicyManagerALSA类为例分析
AudioPolicyManagerALSA::AudioPolicyManagerALSA(
android_audio_legacy::AudioPolicyClientInterface *clientInterface)
: AudioPolicyManagerBase(clientInterface) // clientInterface正是
// AudioPolicyCompatClient类对象,而AudioPolicyCompatClient类具体实现了
// AudioPolicyClientInterface类的需函数接口
{
}
AudioPolicyManagerBase::AudioPolicyManagerBase(
AudioPolicyClientInterface *clientInterface)
:
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
mA2dpSuspended(false)
{
mpClientInterface = clientInterface; // mpClientInterface:将用它回调到APS
for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
mForceUse[i] = AudioSystem::FORCE_NONE;
}
initializeVolumeCurves();
// devices available by default are speaker, ear piece and microphone
mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
AudioSystem::DEVICE_OUT_SPEAKER; // 可用的输出音频设备
mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; // 可用的输入音频设备
......
mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
......
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER,
true);
......
}
mpClientInterface->openOutput()函数先回掉到AudioPolicyCompatClient类的openOutput()函数。
audio_io_handle_t AudioPolicyCompatClient::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
AudioSystem::output_flags flags)
{
return mServiceOps->open_output(mService, pDevices, pSamplingRate, pFormat,
pChannels, pLatencyMs,
(audio_policy_output_flags_t)flags);
}
由前面分析可知,在创建AudioPolicyCompatClient类对象时,mServiceOps被初始化为APS的struct audio_policy_service_ops变量aps_ops;则将回调到ops中的open_output()函数,具体调到aps_open_output()函数:
static audio_io_handle_t aps_open_output(void *service,
uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
audio_policy_output_flags_t flags)
{
sp af = AudioSystem::get_audio_flinger();
if (af == NULL) {
LOGW("%s: could not get AudioFlinger", __func__);
return 0;
}
return af->openOutput(pDevices, pSamplingRate, pFormat, pChannels,
pLatencyMs, flags);
}
不难看出,将调到AudioFlinger的openOutput()函数。
int AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags)
{
......
audio_hw_device_t *outHwDev;
......
outHwDev = findSuitableHwDev_l(*pDevices);
if (outHwDev == NULL)
return 0;
status = outHwDev->open_output_stream(outHwDev, *pDevices, (int *)&format,
&channels, &samplingRate, &outStream);
......
return 0;
}
struct audio_hw_device_t是在头文件hardware/libhardware/include/hardware/audio.h中定义的一个结构体。
typedef struct audio_hw_device audio_hw_device_t;
前面在分析AudioFlinger类的load_audio_interface函数时,已经分析过struct audio_hw_device。
audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(uint32_t devices)
{
/* first matching HW device is returned */
for (size_t i = 0; i < mAudioHwDevs.size(); i++) { // 前文分析过,mAudioHwDevs变量保存了HAL层可用音频设备
audio_hw_device_t *dev = mAudioHwDevs[i];
if ((dev->get_supported_devices(dev) & devices) == devices)
return dev;
}
return NULL;
}
由前文分析可知,此处的get_supported_devices()函数指针将具体调到audio_hw_hal.cpp文件中的adev_get_supported_devices(),如下所示,这里列出了系统所支持的所有输出/输入音频设备。因此,我们要也要仿照此音频设备的定义名称,在这里添加USB AUDIO音频设备的名称,以及它们在别处的定义。
static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev)
{
/* XXX: The old AudioHardwareInterface interface is not smart enough to
* tell us this, so we'll lie and basically tell AF that we support the
* below input/output devices and cross our fingers. To do things properly,
* audio hardware interfaces that need advanced features (like this) should
* convert to the new HAL interface and not use this wrapper. */
return (/* OUT */
AUDIO_DEVICE_OUT_EARPIECE |
AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_WIRED_HEADSET |
AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
AUDIO_DEVICE_OUT_AUX_DIGITAL |
AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
AUDIO_DEVICE_OUT_ALL_SCO |
AUDIO_DEVICE_OUT_DEFAULT |
/* IN */
AUDIO_DEVICE_IN_COMMUNICATION |
AUDIO_DEVICE_IN_AMBIENT |
AUDIO_DEVICE_IN_BUILTIN_MIC |
AUDIO_DEVICE_IN_WIRED_HEADSET |
AUDIO_DEVICE_IN_AUX_DIGITAL |
AUDIO_DEVICE_IN_BACK_MIC |
AUDIO_DEVICE_IN_ALL_SCO |
AUDIO_DEVICE_IN_DEFAULT);
}
当找到合适的设备之后,将调用outHwDev->open_output_stream()函数打开相应设备的输出流;同样的道理,将具体调到audio_hw_hal.cpp文件中的adev_open_output_stream()函数。
static int adev_open_output_stream(struct audio_hw_device *dev,
uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sample_rate,
struct audio_stream_out **stream_out)
{
struct legacy_audio_device *ladev = to_ladev(dev);
status_t status;
struct legacy_stream_out *out;
int ret;
out = (struct legacy_stream_out *)calloc(1, sizeof(*out));
if (!out)
return -ENOMEM;
out->legacy_out = ladev->hwif->openOutputStream(devices, format, channels,
sample_rate, &status);
......
}
由前文分析可知,ladev->hwif具体指AudioHardwareALSA类;则ladev->hwif->openOutputStream()函数调到AudioHardwareALSA::openOutputStream()函数。
android_audio_legacy::AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
{
......
// Find the appropriate alsa device
for(ALSAHandleList::iterator it = mDeviceList.begin(); // mDeviceList是用来存储
// 输出/输入音频通道信息的
// 句柄的,总共两个句柄,
// 分别对应输出和输入音频通道
it != mDeviceList.end(); ++it)
if (it->devices & devices) {
err = mALSADevice->open(&(*it), devices, mode()); // 当调用open()函数时,
// 就已经知道要打开的是
// 输入音频通道还是输出
// 音频通道
if (err) break;
out = new AudioStreamOutALSA(this, &(*it)); // 此类对象的创建本可不必理会,
// 但它的父类ALSAStreamOps类的对象也会随之创建;而ALSAStreamOps
// 类将在后面用到(mParent = this(AudioHardwareALSA))
err = out->set(format, channels, sampleRate);
break;
}
......
}
由前文对AudioHardwareALSA类的启动流程分析可知,mDeviceList是用来存储输出/输入音频通道信息的句柄的。
mALSADevice表示在初始化ALSA设备时所指向的一个具体ALSA设备的操作接口。则mALSADevice->open()函数,将具体调到ALSA模块的s_open()函数。
static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
{
// Close off previously opened device.
// It would be nice to determine if the underlying device actually
// changes, but we might be recovering from an error or manipulating
// mixer settings (see asound.conf).
//
s_close(handle); // 先关闭先前打开的音频通道
LOGD("open called for devices %08x in mode %d...", devices, mode);
const char *stream = streamName(handle);
const char *devName = deviceName(handle, devices, mode);
int err;
for (;;) {
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
err = snd_pcm_open(&handle->handle, devName, direction(handle),
// handle->handle:保存从ALSA-LIB中获得的PCM节点
SND_PCM_ASYNC);
if (err == 0) break;
// See if there is a less specific name we can try.
// Note: We are changing the contents of a const char * here.
char *tail = strrchr(devName, '_');
if (!tail) break;
*tail = 0;
}
if (err < 0) {
// None of the Android defined audio devices exist. Open a generic one.
devName = "default";
err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
}
if (err < 0) {
LOGE("Failed to Initialize any ALSA %s device: %s",
stream, strerror(err));
return NO_INIT;
}
err = setHardwareParams(handle);
if (err == NO_ERROR) err = setSoftwareParams(handle);
LOGI("Initialized ALSA %s device %s", stream, devName);
handle->curDev = devices;
handle->curMode = mode;
return err;
}
(1) 调用函数streamName()函数获取音频流名称。
const char *streamName(alsa_handle_t *handle)
{
return snd_pcm_stream_name(direction(handle));
}
snd_pcm_stream_name()函数是ALSA-LIB API,在external/alsa-lib/src/pcm/pcm.c文件中定义。
要想获得音频流名称,不得不先分析direction()函数。
snd_pcm_stream_t direction(alsa_handle_t *handle)
{
return (handle->devices & android_audio_legacy::AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE;
}
原来direction()函数就是用来返回PCM流的方向(放音或者录音)。
direction()函数的返回值将作为snd_pcm_stream_name()的参数,
const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
{
if (stream > SND_PCM_STREAM_LAST)
return NULL;
return snd_pcm_stream_names[stream];
}
static const char *const snd_pcm_stream_names[] = {
STREAM(PLAYBACK),
STREAM(CAPTURE),
};
(2)接下来调用deviceName()函数获取设备名称。这点很重要,将为我们在asound.conf为新添加的USB AUDIO音频设备命名提供规则。
const char *deviceName(alsa_handle_t *handle, uint32_t device, int mode)
{
static char devString[ALSA_NAME_MAX];
int hasDevExt = 0;
strcpy(devString, devicePrefix[direction(handle)]);
for (int dev = 0; device && dev < deviceSuffixLen; dev++)
if (device & deviceSuffix[dev].device) {
ALSA_STRCAT (devString, deviceSuffix[dev].suffix);
device &= ~deviceSuffix[dev].device;
hasDevExt = 1;
}
if (hasDevExt) switch (mode) {
case android_audio_legacy::AudioSystem::MODE_NORMAL:
ALSA_STRCAT (devString, "_normal")
;
break;
case android_audio_legacy::AudioSystem::MODE_RINGTONE:
ALSA_STRCAT (devString, "_ringtone")
;
break;
case android_audio_legacy::AudioSystem::MODE_IN_CALL:
ALSA_STRCAT (devString, "_incall")
;
break;
};
return devString;
}
static const char *devicePrefix[SND_PCM_STREAM_LAST + 1] = {
/* SND_PCM_STREAM_PLAYBACK : */"AndroidPlayback",
/* SND_PCM_STREAM_CAPTURE : */"AndroidCapture",
};
接下来从deviceSuffix数组中查找合适的后缀,追加到devString字符数组中。
/* The following table(s) need to match in order of the route bits
*/
static const device_suffix_t deviceSuffix[] = {
{android_audio_legacy::AudioSystem::DEVICE_OUT_EARPIECE, "_Earpiece"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_SPEAKER, "_Speaker"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "_Bluetooth"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_WIRED_HEADSET, "_Headset"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "_Bluetooth-A2DP"},
};
struct device_suffix_t的定义如下:
struct device_suffix_t {
const android_audio_legacy::AudioSystem::audio_devices device;
const char *suffix;
};
PS: 我们也要在此数组中添加USB AUDIO音频设备的相关信息。同时也要在定义了类似DEVICE_OUT_EARPIECE设备的类中定义USB AUDIO音频设备:
1.frameworks/base/media/java/android/media/AudioSystem.java
2.frameworks/base/media/java/android/media/AudioManager.java
3.hardware/libhardware_legacy/include/hardware_legacy/AudioSystemLegacy.h
4.system/core/include/system/audio.h
题外话:android系统中对音频设备的定义如下(AudioSystem.java):
public static final int DEVICE_OUT_EARPIECE = 0x1; // 0x1 << 0
public static final int DEVICE_OUT_SPEAKER = 0x2; // 0x1 << 1
public static final int DEVICE_OUT_WIRED_HEADSET = 0x4; // 0x1 << 2
public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8; // 0x1 << 3
public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10; // 0x1 << 4
public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20; // 0x1 << 5
public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40; // 0x1 << 6
public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80; // 0x1 << 7
public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
// 0x100 << 0
public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200; // 0x100 << 1
public static final int DEVICE_OUT_AUX_DIGITAL = 0x400; // 0x100 << 2
public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800; // 0x100 << 3
public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000; // 0x1000 << 0
public static final int DEVICE_OUT_DEFAULT = 0x8000; // 0x1000 << 3
// input devices
public static final int DEVICE_IN_COMMUNICATION = 0x10000; // 0x10000 << 0
public static final int DEVICE_IN_AMBIENT = 0x20000; // 0x10000 << 1
public static final int DEVICE_IN_BUILTIN_MIC1 = 0x40000; // 0x10000 << 2
public static final int DEVICE_IN_BUILTIN_MIC2 = 0x80000; // 0x10000 << 3
public static final int DEVICE_IN_MIC_ARRAY = 0x100000; // 0x100000 << 0
public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x200000;
// 0x100000 << 1
public static final int DEVICE_IN_WIRED_HEADSET = 0x400000; // 0x100000 << 2
public static final int DEVICE_IN_AUX_DIGITAL = 0x800000; // 0x100000 << 3
当设备越来越多时,很难保证等号右边的数字中零的个数不写错。采用注释部分的定义方式较好。
当找到了设备后缀后,将对变量hasDevExt赋值为1,表示还会有扩展名称(_normal,_ringtone或者_incall)。
至此,一个设备的PCM节点名称就形成了!
(3)程序将执行到调用snd_pcm_open() ALSA-LIB API,并把刚才得到的设备名称devName作为参数之一,调到ALSA-LIB,进而调到ALSA-DRIVER,去打开所指定的音频设备。如果打开指定的音频设备失败了,将打开默认的音频设备。
(4)如果成功打开音频设备,程序继续往下执行,将调用setHardwareParams()函数设置硬件参数。这些硬件参数包括缓冲区大小,采样率,声道数和音频格式等。其实这些硬件参数都在struct alsa_handle_t中定义,在分析初始化函数s_init()函数时已有分析,在默认音频设备配置_defaultsOut和_defaultsIn中已经指定。
但是,在使用USB AUDIO输入音频设备时,默认的输入音频配置中的声道数和采样率很有可能与实际使用的USB AUDIO的不一致,导致USB AUDIO设备不可用或者音频失真。因此,需要在执行setHardwareParams()函数前,并且知道是要打开输入音频通道时(输出音频通道的硬件参数配置可用),需要检测时间使用的USB AUDIO音频设备的这两个硬件参数,重新对_defaultsIn中声道数和采样率进行赋值。将在后面做详细分析。
status_t setHardwareParams(alsa_handle_t *handle)
{
......
unsigned int requestedRate = handle->sampleRate;
......
err = snd_pcm_hw_params_set_channels(handle->handle, hardwareParams,
handle->channels);
......
err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,
&requestedRate, 0);
......
}
(5)最后程序会执行到设置软件参数的函数setHardwareParams()中。在添加USB AUDIO音频设备时,这里没有遇到问题,不再分析。
把最复杂的两大本地服务分析完后,后面的任务就很轻了!
JAVA API setDeviceConnectionState()在文件frameworks/base/media/java/android/media/AudioSystem.java中定义。
public static native int setDeviceConnectionState(int device, int state, String device_address);
第一个参数就是要打开的音频设备的标识符,将一路传递下去,直到ALSA模块(alsa_default.cpp);
第二个参数表示第一个参数所指的音频设备是否可用;
第三个参数表示设备地址,一般为空。
看到java关键字native,晓得将调到对应的JNI(frameworks/base/core/jni/android_media_AudioSystem.cpp)代码。
static int
android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz,
jint device, jint state, jstring device_address)
{
const char *c_address = env->GetStringUTFChars(device_address, NULL);
int status = check_AudioSystem_Command(
AudioSystem::setDeviceConnectionState(static_cast (device),
static_cast (state),
c_address));
env->ReleaseStringUTFChars(device_address, c_address);
return status;
}
显然,又调到libmedia库(frameworks/base/media/libmedia/AudioSystem.cpp)中setDeviceConnectionState()函数。
status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address)
{
const sp& aps = AudioSystem::get_audio_policy_service();
const char *address = "";
if (aps == 0) return PERMISSION_DENIED;
if (device_address != NULL) {
address = device_address;
}
return aps->setDeviceConnectionState(device, state, address);
}
显然,调到APS的setDeviceConnectionState()函数。
status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address)
{
......
return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device,
state, device_address);
}
根据前面对AudioPolicyService服务的启动流程分析可知,mpAudioPolicy指向在文件audio_policy_hal.cpp中定义的音频策略模块。则mpAudioPolicy->set_device_connection_state()函数具体调到函数ap_set_device_connection_state()函数。
static int ap_set_device_connection_state(struct audio_policy *pol,
audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address)
{
struct legacy_audio_policy *lap = to_lap(pol);
return lap->apm->setDeviceConnectionState(
(AudioSystem::audio_devices)device,
(AudioSystem::device_connection_state)state,
device_address);
}
同样,由前面对AudioPolicyService服务的启动流程分析可知,lap->apm指向AudioPolicyManagerBase类对象。则lap->apm->setDeviceConnectionState()函数将调到AudioPolicyManagerBase::setDeviceConnectionState()函数。
status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
AudioSystem::device_connection_state state,
const char *device_address)
{
......
// handle output devices
if (AudioSystem::isOutputDevice(device)) { // 如果是输出设备
......
switch (state)
{
// handle output device connection
case AudioSystem::DEVICE_STATE_AVAILABLE:
if (mAvailableOutputDevices & device) {
LOGW("setDeviceConnectionState() device already connected: %x", device);
return INVALID_OPERATION;
}
LOGV("setDeviceConnectionState() connecting device %x", device);
// register new device as available
mAvailableOutputDevices |= device; // 把当前设备加入到可用设备变量中
.....
// handle output device disconnection
case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
if (!(mAvailableOutputDevices & device)) {
LOGW("setDeviceConnectionState() device not connected: %x", device);
return INVALID_OPERATION;
}
LOGV("setDeviceConnectionState() disconnecting device %x", device);
// remove device from available output devices
mAvailableOutputDevices &= ~device; // 把当前设备从可用设备变量中去除
......
}
// request routing change if necessary
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
......
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
// 如果输出音频设备是USB AUDIO(USB 放音),那么应该知道输入音频设备为SUB MIC
if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
} else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else {
return NO_ERROR;
}
}
// handle input devices
if (AudioSystem::isInputDevice(device)) { // 如果是输入设备
switch (state)
{
// handle input device connection
case AudioSystem::DEVICE_STATE_AVAILABLE: {
if (mAvailableInputDevices & device) {
LOGW("setDeviceConnectionState() device already connected: %d", device);
return INVALID_OPERATION;
}
mAvailableInputDevices |= device;
}
break;
// handle input device disconnection
case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
if (!(mAvailableInputDevices & device)) {
LOGW("setDeviceConnectionState() device not connected: %d", device);
return INVALID_OPERATION;
}
mAvailableInputDevices &= ~device;
} break;
default:
LOGE("setDeviceConnectionState() invalid state: %x", state);
return BAD_VALUE;
}
audio_io_handle_t activeInput = getActiveInput();
if (activeInput != 0) {
AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
if (newDevice != inputDesc->mDevice) {
LOGV("setDeviceConnectionState() changing device from %x to %x for input %d",
inputDesc->mDevice, newDevice, activeInput);
inputDesc->mDevice = newDevice;
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
mpClientInterface->setParameters(activeInput, param.toString());
}
}
return NO_ERROR;
}
LOGW("setDeviceConnectionState() invalid device: %x", device);
return BAD_VALUE;
}
(1) 当前设备是输出设备时,程序执行到getNewDevice()函数,将获得新设备,作为设置输出设备函数setOutputDevice()的第二个参数。
uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
{
uint32_t device = 0;
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
// check the following by order of priority to request a routing change if necessary:
// 1: the strategy enforced audible is active on the output:
// use device for strategy enforced audible
// 2: we are in call or the strategy phone is active on the output:
// use device for strategy phone
// 3: the strategy sonification is active on the output:
// use device for strategy sonification
// 4: the strategy media is active on the output:
// use device for strategy media
// 5: the strategy DTMF is active on the output:
// use device for strategy DTMF
if (outputDesc->isUsedByStrategy(STRATEGY_ENFORCED_AUDIBLE)) { // 判断是否使用了这五种音频策略之一的
// STRATEGY_ENFORCED_AUDIBLE
device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); // 若使用了STRATEGY_ENFORCED_AUDIBLE,
// 并获得相关设备,需要增加对USB AUDIO设备的支持
} else if (isInCall() ||
outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
} else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
} else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
} else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
}
......
return device;
}
......
uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
{
uint32_t device = 0;
if (fromCache) {
LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
return mDeviceForStrategy[strategy];
}
switch (strategy) {
case STRATEGY_DTMF:
if (!isInCall()) {
// when off call, DTMF strategy follows the same rules as MEDIA strategy
device = getDeviceForStrategy(STRATEGY_MEDIA, false);
break;
}
// when in call, DTMF and PHONE strategies follow the same rules
// FALL THROUGH
case STRATEGY_PHONE: // 由于我BOX不支持通话功能,所以可以不在此策略下添加对USB AUDIO设备的支持
// for phone strategy, we first consider the forced use and then the available devices by order
// of priority
......
break;
case STRATEGY_SONIFICATION: // 由于我BOX不支持通话功能,所以可以不在此策略下添加对USB AUDIO设备的支持
// If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
// handleIncallSonification().
......
case STRATEGY_ENFORCED_AUDIBLE:
// strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION
// except when in call where it doesn't default to STRATEGY_PHONE behavior
......
case STRATEGY_MEDIA: { // 多媒体播放策略,需要添加对USB AUDIO设备的支持
uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
.......
// 添加对USB AUDIO设备支持的代码
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_USB_AUDIO;
}
// end
.......
}
(2)获取到新设备后,程序继续向下执行到updateDeviceForStrategy()函数,根据音频策略更新了相应的设备。
void AudioPolicyManagerBase::updateDeviceForStrategy()
{
for (int i = 0; i < NUM_STRATEGIES; i++) {
mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
}
}
由于此函数仍将调到刚刚分析的函数getDeviceForStrategy(),故不再深入分析。
(3)更新完设备后,程序继续向下执行到setOutputDevice()函数,用新获取到的设备名称作为第二个参数(第一个参数是在创建AudioPolicyManagerBase类对象时获得的输出音频通道),来设置输出设备。此函数很重要,它将调到ALSA模块(alsa_default.cpp)。
void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
{
LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
......
// do the routing
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)device); // 第一个参数,暗示将重新选择路由,
// 第二个参数就是要打开的音频设备的标识符
mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs); // 将从HAL层调回到frameworks层
......
}
调用函数setOutputDevice()时,明明只有两个参数,怎么函数原型却有4个参数了那。有点儿诡异吧!我也困惑了好大会儿。最后才发现此函数的声明(AudioPolicyManagerBase.h)中,最后两个参数(第三个和第四个参数)已经有了默认值。原来,C++中有了默认值的参数,在调用时可以不写出来!!!
void setOutputDevice(audio_io_handle_t output, uint32_t device, bool force = false, int delayMs = 0);
此函数的重点在于调用了函数mpClientInterface->setParameters()。
第一个参数mHardwareOutput:表示输出音频通道,
第三个参数delayMs:表示等待时间,值为默认值0。
通过前文分析可知,mpClientInterface就是AudioPolicyCompatClient类的对象,则mpClientInterface->setParameters()函数将调到AudioPolicyCompatClient类的setParameters()函数。
void AudioPolicyCompatClient::setParameters(audio_io_handle_t ioHandle,
const String8& keyValuePairs,
int delayMs)
{
mServiceOps->set_parameters(mService, ioHandle, keyValuePairs.string(),
// 在创建APS类对象时,调用create_audio_policy()第三个参数this(即APS)一路传递下来,并在创建
// AudioPolicyCompatClient类对象时对mService进行了初始化
delayMs);
}
而mServiceOps在创建AudioPolicyCompatClient类对象时,指向APS的struct audio_policy_service_ops变量aps_ops。则mServiceOps->set_parameters()函数将调到APS的aps_set_parameters()函数。
static void aps_set_parameters(void *service, audio_io_handle_t io_handle,
const char *kv_pairs, int delay_ms)
{
AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms);
}
显然将调到APS类的setParameters()函数。
void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
const char *keyValuePairs,
int delayMs)
{
mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs,
delayMs);
}
mAudioCommandThread就是在创建APS类对象时,启动的一个音频命令线程。函数mAudioCommandThread->parametersCommand()将根据第二个参数产生一个设置音频参数的命令,并发给此线程的处理函数threadLoop()。
status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
const char *keyValuePairs,
int delayMs)
{
status_t status = NO_ERROR;
AudioCommand *command = new AudioCommand();
command->mCommand = SET_PARAMETERS;
ParametersData *data = new ParametersData();
data->mIO = ioHandle;
data->mKeyValuePairs = String8(keyValuePairs); // keyValuePairs保存了从AudioPolicyManagerBase类传递的音频参数param
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs); // 调用函数insertCommand_l()把命令插入到此线程处理函数threadLoop()中
......
}
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
......
// acquire wake lock to make sure delayed commands are processed
if (mName != "" && mAudioCommands.isEmpty()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
}
// check same pending commands with later time stamps and eliminate them
for (i = mAudioCommands.size()-1; i >= 0; i--) {
AudioCommand *command2 = mAudioCommands[i];
......
switch (command->mCommand) {
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
ParametersData *data2 = (ParametersData *)command2->mParam;
if (data->mIO != data2->mIO) break;
LOGV("Comparing parameter command %s to new command %s",
data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
AudioParameter param = AudioParameter(data->mKeyValuePairs);
AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
for (size_t j = 0; j < param.size(); j++) {
String8 key;
String8 value;
param.getAt(j, key, value);
for (size_t k = 0; k < param2.size(); k++) {
String8 key2;
String8 value2;
param2.getAt(k, key2, value2);
if (key2 == key) {
param2.remove(key2);
LOGV("Filtering out parameter %s", key2.string());
break;
}
}
}
// if all keys have been filtered out, remove the command.
// otherwise, update the key value pairs
if (param2.size() == 0) {
removedCommands.add(command2);
} else {
data2->mKeyValuePairs = param2.toString();
}
} break;
......
}
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
nsecs_t waitTime = INT64_MAX;
mLock.lock();
while (!exitPending())
{
while(!mAudioCommands.isEmpty()) {
nsecs_t curTime = systemTime();
// commands are sorted by increasing time stamp: execute them from index 0 and up
if (mAudioCommands[0]->mTime <= curTime) {
AudioCommand *command = mAudioCommands[0];
mAudioCommands.removeAt(0);
mLastCommand = *command;
switch (command->mCommand) {
......
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
LOGV("AudioCommandThread() processing set parameters string %s, io %d",
data->mKeyValuePairs.string(), data->mIO);
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
......
}
此线程处理函数并没有真正去设置参数,而是把设置参数的实际操作交给了函数AudioSystem::setParameters()。
status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
const sp& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setParameters(ioHandle, keyValuePairs);
}
又调到AudioFlinger了!
status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
{
......
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
// ioHandle == 0 means the parameters are global to the audio hardware interface
if (ioHandle == 0) { // ioHandle就是在函数AudioPolicyManagerBase::setOutputDevice()内调用
// mpClientInterface->setParameters()函数时,
// 传递的第一个参数mHardwareOutput,其值有其中可能;经查看log信息确认,当前值是0。
// 将改变全局输入/输出音频设备
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_PARAMETER;
status_t final_result = NO_ERROR;
for (size_t i = 0; i < mAudioHwDevs.size(); i++) { // 将对所有的音频设备的参数都重新设置
audio_hw_device_t *dev = mAudioHwDevs[i];
result = dev->set_parameters(dev, keyValuePairs.string());
final_result = result ?: final_result;
}
......
}
由前面对AudioFlinger服务和AudioPolicyService服务的启动流程分析可知,mAudioHwDevs具体指在文件audio_hw_hal.cpp所实现的音频模块中定义的设备。则dev->set_parameters()函数就是adev_set_parameters()函数。
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
struct legacy_audio_device *ladev = to_ladev(dev);
return ladev->hwif->setParameters(String8(kvpairs));
}
同样根据前面AudioFlinger服务分析可知,ladev->hwif具体指ALSA音频类AudioHardwareALSA的对象。则函数ladev->hwif->setParameters()就是函数AudioHardwareALSA类的setParameters()函数。此函数在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义。
class ALSAStreamOps
{
public:
ALSAStreamOps(AudioHardwareALSA *parent, alsa_handle_t *handle);
virtual ~ALSAStreamOps();
status_t set(int *format, uint32_t *channels, uint32_t *rate);
status_t setParameters(const String8& keyValuePairs);
......
}
此setParameters()函数的实现在提供了ALSA流操作接口的文件hardware/alsa_sound/ALSAStreamOps.cpp中。
status_t ALSAStreamOps::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int device;
LOGV("setParameters() %s", keyValuePairs.string());
if (param.getInt(key, device) == NO_ERROR) {
AutoMutex lock(mLock);
mParent->mALSADevice->route(mHandle, (uint32_t)device, mParent->mode());
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
struct alsa_device_t {
hw_device_t common;
status_t (*init)(alsa_device_t *, ALSAHandleList &);
status_t (*open)(alsa_handle_t *, uint32_t, int);
status_t (*close)(alsa_handle_t *);
status_t (*route)(alsa_handle_t *, uint32_t, int);
};
而这些ALSA函数接口已经指向了具体的函数实现(alsa_default.cpp)。则调到HAL层ALSA音频模块(alsa_default.cpp)中s_route()函数。
static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode)
{
LOGD("route called for devices %08x in mode %d...", devices, mode);
//@eric:20110316
//When RingCall come,AudioHardWareALSA Set RINGMODE and will call this func.
//FIXME:I think Our Audio Device only has one handle, so we can not reopen it
if (handle->handle && handle->curDev == devices /*&& handle->curMode == mode*/) return NO_ERROR;
return s_open(handle, devices, mode);
}
进而调到s_open()函数。前面已经对s_open()函数做过分析,这里不再重述。
ALSA(Advanced Linux Sound Architecture)音频驱动是一个十分庞大,复杂和功能强大的音频系统,应用领域远远高于先前的OSS (Open Sound System)音频驱动。由于ALSA-DRIVER过于庞杂,给开发者使用ALSA-DRIVER带来许多不变,故ALSA-DRIVER的API ALSA-LIB应运而生。我们可以通过调用ALSA-LIB的API间接与ALSA-DRIVER打交道。
关于ALSA的更多更详尽的介绍请参阅其官网(http://www.alsa-project.org/main/index.php/Main_Page)。
前文在分析ALSA音频模块(alsa_default.cpp)时,已经提到会在s_open()函数中调到ALSA-LIB的API函数snd_pcm_open()函数。就是此函数来实际实现音频通道的打开的!
snd_pcm_open(&handle->handle, devName, direction(handle), SND_PCM_ASYNC);
函数snd_pcm_open()在文件external/alsa-lib/src/pcm/pcm.c中定义。
/**
* \brief Opens a PCM
* \param pcmp Returned PCM handle
* \param name ASCII identifier of the PCM handle
* \param stream Wanted stream
* \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
int err;
assert(pcmp && name);
err = snd_config_update();
if (err < 0)
return err;
return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
}
(1)更新配置文件函数snd_config_update()。
/**
* \brief Updates #snd_config by rereading the global configuration files (if needed).
* \return A non-negative value if successful, otherwise a negative error code.
* \retval 0 No action is needed.
* \retval 1 The configuration tree has been rebuilt.
*
* The global configuration files are specified in the environment variable
* \c ALSA_CONFIG_PATH. If this is not set, the default value is
* "/usr/share/alsa/alsa.conf".
*
* \warning If the configuration tree is reread, all string pointers and
* configuration node handles previously obtained from this tree become invalid.
*/
int snd_config_update(void)
{
int err;
#ifdef HAVE_LIBPTHREAD
pthread_mutex_lock(&snd_config_update_mutex);
#endif
err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
#ifdef HAVE_LIBPTHREAD
pthread_mutex_unlock(&snd_config_update_mutex);
#endif
return err;
}
又调到snd_config_update_r()函数了。
/**
* \brief Updates a configuration tree by rereading the configuration files (if needed).
* \param _top Address of the handle to the top level node.
* \param _update Address of a pointer to private update information.
* \param cfgs A list of configuration file names, delimited with ':'.
* If \p cfgs is set to \c NULL, the default global configuration
* file is used ("/usr/share/alsa/alsa.conf").
* \return A non-negative value if successful, otherwise a negative error code.
* \retval 0 No action is needed.
* \retval 1 The configuration tree has been rebuilt.
*
* The global configuration files are specified in the environment variable
* \c ALSA_CONFIG_PATH.
*
* \warning If the configuration tree is reread, all string pointers and
* configuration node handles previously obtained from this tree become invalid.
*/
int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs) // cfgs = NULL
{
......
configs = cfgs;
if (!configs) {
configs = getenv(ALSA_CONFIG_PATH_VAR); // ???
if (!configs || !*configs) {
configs = ALSA_CONFIG_PATH_DEFAULT; // 宏ALSA_CONFIG_PATH_DEFAULT
// 便是/usr/share/alsa/alsa.conf
.......
}
pcm.name {
type hw # Kernel PCM
card INT/STR # Card name (string) or number (integer)
[device INT] # Device number (default 0)
[subdevice INT] # Subdevice number (default -1: first available)
[sync_ptr_ioctl BOOL] # Use SYNC_PTR ioctl rather than the direct mmap access for control structures
[nonblock BOOL] # Force non-blocking open mode
[format STR] # Restrict only to the given format
[channels INT] # Restrict only to the given channels
[rate INT] # Restrict only to the given rate
}
仅举一例
pcm.AndroidCapture_Usb-audio_normal { // 此PCM节点为录音设备的PCM节点
type hooks
slave.pcm "hw:1,0" // 1:表示声卡1(声卡0就是系统内嵌的声卡),0表示声卡1上的设备编号为0的设备
}
AndroidCapture_Usb-audio_normal:前缀"AndroidCapture"和扩展"_normal"是固定的,后缀"_Usb-audio"是要在alsa_default.cpp中设置的。
/* The following table(s) need to match in order of the route bits
*/
static const device_suffix_t deviceSuffix[] = {
{android_audio_legacy::AudioSystem::DEVICE_OUT_EARPIECE, "_Earpiece"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_SPEAKER, "_Speaker"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "_Bluetooth"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_WIRED_HEADSET, "_Headset"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "_Bluetooth-A2DP"},
{android_audio_legacy::AudioSystem::DEVICE_OUT_USB_AUDIO, "_Usb-audio"},
{android_audio_legacy::AudioSystem::DEVICE_IN_USB_AUDIO, "_Usb-audio"},
};
前文已经提到,可在alsa_default.cpp文件中的s_open()函数在调用setHardwareParams()函数设置硬件参数之前重设声道数channels或采样率rate。
第一种方法: 自己写个字符串处理函数,从文件/proc/asound/card1/stream0中读取(只需读取录音部分的)。
$ cat /proc/asound/card1/stream0
SAGE Technology SAGE AirMouse at usb-0000:00:1d.3-2, full speed : USB Audio
Playback:
Status: Stop
Interface 2
Altset 1
Format: S16_LE
Channels: 1
Endpoint: 6 OUT (NONE)
Rates: 16000
Capture:
Status: Stop
Interface 1
Altset 1
Format: S16_LE
Channels: 1 // 这个声道数与默认值2不一致
Endpoint: 5 IN (NONE)
Rates: 16000 // 这个采样率与默认值48000不一致
第二种方法:自己写代码,从内核USB驱动中读取。主要涉及到如下文件(也是我们项目所采用的方法)
sound/usb/card.c
drivers/hid/hid-input.c
第三种方法:因为是直接调用系统现有接口,是比较可靠的方法:调用ALSA-LIB库的API。主要涉及到如下文件
external/alsa-lib/src/pcm/pcm_rate.c
当打开指定PCM节点的音频设备失败后,系统会去打开默认的音频通道(alsa_default.cpp)。
for (;;) {
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
err = snd_pcm_open(&handle->handle, devName, direction(handle),
.......
}
if (err < 0) {
//要在此处把输入音频通道的声道数和采样率改回默认值
// None of the Android defined audio devices exist. Open a generic one.
devName = "default";
err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
}
PS:只需调用setDeviceConnectionState()函数打开输出音频通道,不要去打开输入音频通道。因为用户使用的音频聊天工具(如Skypee)会自动打开。我们只需在打开USB AUDIO输出音频通道时,强制设置USB MIC为输入音频通道。这样就能实现用USB MIC 进行语音聊天了!
结贴~~~~~~~