您的位置:首页 > 运维架构

Android Audio AudioHardwareALSA::openOutputStream函数

2014-09-24 22:45 393 查看
http://blog.csdn.net/hgl868/article/details/6872128

发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数。

以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些。

闲言少叙,跳入代码。

前两天看AudioTrack创建的时候,我们看到了AudioHardwareALSA::openOutputStream,并没有继续往下看。

今天就看看函数AudioHardwareALSA::openOutputStream的实现。

*****************************************源码*************************************************

AudioStreamOut *

AudioHardwareALSA::openOutputStream(uint32_t devices,

int *format,

uint32_t *channels,

uint32_t *sampleRate,

status_t *status)

{

AutoMutex lock(mLock);

LOGD("openOutputStream called for devices: 0x%08x", devices);

status_t err = BAD_VALUE;

AudioStreamOutALSA *out = 0;

if (devices & (devices - 1)) {

if (status) *status = err;

LOGD("openOutputStream called with bad devices");

return out;

}

// Find the appropriate alsa device

for(ALSAHandleList::iterator it = mDeviceList.begin();

it != mDeviceList.end(); ++it)

if (it->devices & devices) {

err = mALSADevice->open(&(*it), devices, mode());

if (err) break;

if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){

strcpy(mCurCard ,SPDIF);

mMixer = mMixerSpdif;

} else {

strcpy(mCurCard,SGTL5000);

mMixer = mMixerSgtl5000;

}

out = new AudioStreamOutALSA(this, &(*it));

err = out->set(format, channels, sampleRate);

break;

}

if (status) *status = err;

return out;

}

**********************************************************************************************

源码路径:

hardware\alsa_sound\AudioHardwareALSA.cpp

###########################################说明################################################

AudioStreamOut *

AudioHardwareALSA::openOutputStream(uint32_t devices,

int *format,

uint32_t *channels,

uint32_t *sampleRate,

status_t *status)

{

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

先回忆一下各个参数都是嘛意思。

devices

设备编号。也就是说打开的是第几个ALSA声卡。该参数是在函数AudioPolicyManagerBase::getOutput中产生的。

至于如何调到了函数AudioHardwareALSA::openOutputStream,可以参考文章:Android Audio代码分析4 - AudioSystem::getOutputSamplingRate。

函数AudioPolicyManagerBase::getOutput中直接调用的是AudioPolicyService::openOutput函数。调用代码如下:

output = mpClientInterface->openOutput(&outputDesc->mDevice,

&outputDesc->mSamplingRate,

&outputDesc->mFormat,

&outputDesc->mChannels,

&outputDesc->mLatency,

outputDesc->mFlags);

其中,outputDesc->mDevice就是devices。

outputDesc->mDevice是怎么来的呢?

继续往前看,outputDesc->mDevice = device;

device的出处在哪?

uint32_t device = getDeviceForStrategy(strategy);

strategy的来头是:

routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);

看看这两个函数吧。

先看看getStrategy。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(

AudioSystem::stream_type stream) {

// stream to strategy mapping

switch (stream) {

case AudioSystem::VOICE_CALL:

case AudioSystem::BLUETOOTH_SCO:

return STRATEGY_PHONE;

case AudioSystem::RING:

case AudioSystem::NOTIFICATION:

case AudioSystem::ALARM:

case AudioSystem::ENFORCED_AUDIBLE:

return STRATEGY_SONIFICATION;

case AudioSystem::DTMF:

return STRATEGY_DTMF;

default:

LOGE("unknown stream type");

case AudioSystem::SYSTEM:

// NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs

// while key clicks are played produces a poor result

case AudioSystem::TTS:

case AudioSystem::MUSIC:

return STRATEGY_MEDIA;

}

}

实现比较简单,根据stream类型,返回特定的策略。

----------------------------------------------------------------

再看看函数getDeviceForStrategy。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

看看其英文注释:

// return appropriate device for streams handled by the specified strategy according to current

// phone state, connected devices...

// if fromCache is true, the device is returned from mDeviceForStrategy[], otherwise it is determined

// by current state (device connected, phone state, force use, a2dp output...)

// This allows to:

// 1 speed up process when the state is stable (when starting or stopping an output)

// 2 access to either current device selection (fromCache == true) or

// "future" device selection (fromCache == false) when called from a context

// where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND

// before updateDeviceForStrategy() is called.

virtual uint32_t getDeviceForStrategy(routing_strategy strategy, bool fromCache = true);

顺便说一句,头文件AudioPolicyManagerBase.h不在frameworks中,而是在hardware中。

----------------------------------------------------------------

{

uint32_t device = 0;

// 函数注释中也有说,如果fromCache为true,直接返回内存中保存的信息。

// 我们调用的时候,只给了一个参数,所以fromCache肯定为true。

if (fromCache) {

LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);

return mDeviceForStrategy[strategy];

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

mDeviceForStrategy又是什么时候被赋值的呢?

在函数updateDeviceForStrategy中。。。

void AudioPolicyManagerBase::updateDeviceForStrategy()

{

for (int i = 0; i < NUM_STRATEGIES; i++) {

mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);

}

}

还是调用的getDeviceForStrategy函数,只不过不是从cache中取,而是根据当前状态来取。

函数updateDeviceForStrategy在以下几个地方被调用:

AudioPolicyManagerBase::setDeviceConnectionState函数。

AudioPolicyManagerBase::setPhoneState函数。

AudioPolicyManagerBase::setForceUse函数。

AudioPolicyManagerBase的构造函数。

也就是在可能发生改变的地方。

----------------------------------------------------------------

}

switch (strategy) {

case STRATEGY_DTMF:

if (!isInCall()) {

// 如果是in call,采用的是STRATEGY_PHONE的策略。如果是off call,采用的是STRATEGY_MEDIA的策略。

// 如何判断in call / off call的?

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

bool AudioPolicyManagerBase::isInCall()

{

return isStateInCall(mPhoneState);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

bool AudioPolicyManagerBase::isStateInCall(int state) {

return ((state == AudioSystem::MODE_IN_CALL) ||

(state == AudioSystem::MODE_IN_COMMUNICATION));

}

----------------------------------------------------------------

// 看看mPhoneState是在哪儿赋值的:

构造函数中有个对其的初始化: mPhoneState(AudioSystem::MODE_NORMAL)

setPhoneState函数中有对其赋值。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void AudioPolicyManagerBase::setPhoneState(int state)

{

LOGV("setPhoneState() state %d", state);

uint32_t newDevice = 0;

if (state < 0 || state >= AudioSystem::NUM_MODES) {

LOGW("setPhoneState() invalid state %d", state);

return;

}

if (state == mPhoneState ) {

LOGW("setPhoneState() setting same state %d", state);

return;

}

// if leaving call state, handle special case of active streams

// pertaining to sonification strategy see handleIncallSonification()

if (isInCall()) {

LOGV("setPhoneState() in call state management: new state is %d", state);

for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {

handleIncallSonification(stream, false, true);

}

}

// store previous phone state for management of sonification strategy below

int oldState = mPhoneState;

mPhoneState = state;

bool force = false;

// are we entering or starting a call

if (!isStateInCall(oldState) && isStateInCall(state)) {

LOGV(" Entering call in setPhoneState()");

// force routing command to audio hardware when starting a call

// even if no device change is needed

force = true;

} else if (isStateInCall(oldState) && !isStateInCall(state)) {

LOGV(" Exiting call in setPhoneState()");

// force routing command to audio hardware when exiting a call

// even if no device change is needed

force = true;

} else if (isStateInCall(state) && (state != oldState)) {

LOGV(" Switching between telephony and VoIP in setPhoneState()");

// force routing command to audio hardware when switching between telephony and VoIP

// even if no device change is needed

force = true;

}

// check for device and output changes triggered by new phone state

newDevice = getNewDevice(mHardwareOutput, false);

#ifdef WITH_A2DP

checkOutputForAllStrategies();

checkA2dpSuspend();

#endif

updateDeviceForStrategy();

AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);

// force routing command to audio hardware when ending call

// even if no device change is needed

if (isStateInCall(oldState) && newDevice == 0) {

newDevice = hwOutputDesc->device();

}

// when changing from ring tone to in call mode, mute the ringing tone

// immediately and delay the route change to avoid sending the ring tone

// tail into the earpiece or headset.

int delayMs = 0;

if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) {

// delay the device change command by twice the output latency to have some margin

// and be sure that audio buffers not yet affected by the mute are out when

// we actually apply the route change

delayMs = hwOutputDesc->mLatency*2;

setStreamMute(AudioSystem::RING, true, mHardwareOutput);

}

// change routing is necessary

setOutputDevice(mHardwareOutput, newDevice, force, delayMs);

// if entering in call state, handle special case of active streams

// pertaining to sonification strategy see handleIncallSonification()

if (isStateInCall(state)) {

LOGV("setPhoneState() in call state management: new state is %d", state);

// unmute the ringing tone after a sufficient delay if it was muted before

// setting output device above

if (oldState == AudioSystem::MODE_RINGTONE) {

setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);

}

for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {

handleIncallSonification(stream, true, true);

}

}

// Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE

if (state == AudioSystem::MODE_RINGTONE &&

(hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||

(systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {

mLimitRingtoneVolume = true;

} else {

mLimitRingtoneVolume = false;

}

}

----------------------------------------------------------------

函数AudioPolicyService::setPhoneState调用了函数AudioPolicyManagerBase::setPhoneState:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

status_t AudioPolicyService::setPhoneState(int state)

{

if (mpPolicyManager == NULL) {

return NO_INIT;

}

if (!checkPermission()) {

return PERMISSION_DENIED;

}

if (state < 0 || state >= AudioSystem::NUM_MODES) {

return BAD_VALUE;

}

LOGV("setPhoneState() tid %d", gettid());

// TODO: check if it is more appropriate to do it in platform specific policy manager

AudioSystem::setMode(state);

Mutex::Autolock _l(mLock);

mpPolicyManager->setPhoneState(state);

return NO_ERROR;

}

----------------------------------------------------------------

函数AudioSystem::setPhoneState调用了函数AudioPolicyService::setPhoneState:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

status_t AudioSystem::setPhoneState(int state)

{

const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();

if (aps == 0) return PERMISSION_DENIED;

return aps->setPhoneState(state);

}

----------------------------------------------------------------

函数android_media_AudioSystem_setPhoneState调用了函数AudioSystem::setPhoneState。

从名称可以看出,函数android_media_AudioSystem_setPhoneState应该是通过JNI提供给Java层的函数。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int

android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)

{

return check_AudioSystem_Command(AudioSystem::setPhoneState(state));

}

java侧的函数handleMessage中遇到MSG_MEDIA_SERVER_STARTED消息,以及函数binderDied中有对该函数的调用。

----------------------------------------------------------------

}

----------------------------------------------------------------

// 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:

// for phone strategy, we first consider the forced use and then the available devices by order

// of priority

switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {

case AudioSystem::FORCE_BT_SCO:

if (!isInCall() || strategy != STRATEGY_DTMF) {

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;

if (device) break;

}

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;

if (device) break;

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;

if (device) break;

// if SCO device is requested but no SCO device is available, fall back to default case

// FALL THROUGH

default: // FORCE_NONE

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;

if (device) break;

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;

if (device) break;

#ifdef WITH_A2DP

// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP

if (!isInCall()) {

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;

if (device) break;

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;

if (device) break;

}

#endif

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;

if (device == 0) {

LOGE("getDeviceForStrategy() earpiece device not found");

}

break;

case AudioSystem::FORCE_SPEAKER:

if (!isInCall() || strategy != STRATEGY_DTMF) {

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;

if (device) break;

}

#ifdef WITH_A2DP

// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to

// A2DP speaker when forcing to speaker output

if (!isInCall()) {

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;

if (device) break;

}

#endif

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;

if (device == 0) {

LOGE("getDeviceForStrategy() speaker device not found");

}

break;

}

break;

case STRATEGY_SONIFICATION:

// If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by

// handleIncallSonification().

if (isInCall()) {

device = getDeviceForStrategy(STRATEGY_PHONE, false);

break;

}

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;

if (device == 0) {

LOGE("getDeviceForStrategy() speaker device not found");

}

// The second device used for sonification is the same as the device used by media strategy

// FALL THROUGH

case STRATEGY_MEDIA: {

// 先看看MEDIA策略相关的

uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

mAvailableOutputDevices又是怎么来地?

函数AudioPolicyManagerBase::setDeviceConnectionState中有对其赋值。

也就是说设备连接状态改变时,会对其赋值。

AudioPolicyManagerBase的构造函数中也有对其赋值。

默认情况下,有三个设备可用。

两个输出,一个输入。

// 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;

audio device定义如下:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

enum audio_devices {

// output devices

DEVICE_OUT_EARPIECE = 0x1,

DEVICE_OUT_SPEAKER = 0x2,

DEVICE_OUT_WIRED_HEADSET = 0x4,

DEVICE_OUT_WIRED_HEADPHONE = 0x8,

DEVICE_OUT_BLUETOOTH_SCO = 0x10,

DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,

DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,

DEVICE_OUT_BLUETOOTH_A2DP = 0x80,

DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,

DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,

DEVICE_OUT_AUX_DIGITAL = 0x400,

DEVICE_OUT_FM_HEADPHONE = 0x800,

DEVICE_OUT_FM_SPEAKER = 0x1000,

DEVICE_OUT_TTY = 0x2000,

DEVICE_OUT_WIRED_HDMI = 0x4000,

DEVICE_OUT_DEFAULT = 0x8000,

DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |

DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |

DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |

DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_FM_HEADPHONE | DEVICE_OUT_FM_SPEAKER | DEVICE_OUT_TTY | DEVICE_OUT_WIRED_HDMI | DEVICE_OUT_DEFAULT),

DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |

DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),

// input devices

DEVICE_IN_COMMUNICATION = 0x10000,

DEVICE_IN_AMBIENT = 0x20000,

DEVICE_IN_BUILTIN_MIC = 0x40000,

DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,

DEVICE_IN_WIRED_HEADSET = 0x100000,

DEVICE_IN_AUX_DIGITAL = 0x200000,

DEVICE_IN_VOICE_CALL = 0x400000,

DEVICE_IN_BACK_MIC = 0x800000,

DEVICE_IN_DEFAULT = 0x80000000,

DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT | DEVICE_IN_BUILTIN_MIC |

DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET | DEVICE_IN_AUX_DIGITAL |

DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC | DEVICE_IN_DEFAULT)

};

----------------------------------------------------------------

----------------------------------------------------------------

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HDMI;

}

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;

}

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;

}

#ifdef WITH_A2DP

if (mA2dpOutput != 0) {

if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {

break;

}

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;

}

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;

}

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;

}

}

#endif

if (device2 == 0) {

device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;

}

// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise

device |= device2;

if (device == 0) {

LOGE("getDeviceForStrategy() speaker device not found");

}

} break;

default:

LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);

break;

}

LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);

return device;

}

----------------------------------------------------------------

回到函数头:

AudioStreamOut *

AudioHardwareALSA::openOutputStream(uint32_t devices,

int *format,

uint32_t *channels,

uint32_t *sampleRate,

status_t *status)

参数devices看完了。

后面的几个参数就比较简单了。

format是指音频格式,当前支持的有8PCMBIT和16PCMBIT.

channels是声道。

sampleRate是采样率。

status,用于返回错误信息。

----------------------------------------------------------------

AutoMutex lock(mLock);

LOGD("openOutputStream called for devices: 0x%08x", devices);

status_t err = BAD_VALUE;

AudioStreamOutALSA *out = 0;

// 从函数getDeviceForStrategy可知,得到只可能是一种设备。

// 而每种audio device只占一个bit。

// 此处是判断,若devices占一个以上的bit,则认为是参数错误。

if (devices & (devices - 1)) {

if (status) *status = err;

LOGD("openOutputStream called with bad devices");

return out;

}

// Find the appropriate alsa device

// 遍历mDeviceList,寻找合适的device。寻找的依据就是传进来的参数devices。

for(ALSAHandleList::iterator it = mDeviceList.begin();

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

mDeviceList是在何时被初始化的?

在类AudioHardwareALSA的构造函数中。

AudioHardwareALSA::AudioHardwareALSA() :

mMixer(0),

mMixerSpdif(0),

mMixerSgtl5000(0),

mALSADevice(0),

mAcousticDevice(0)

{

snd_lib_error_set_handler(&ALSAErrorHandler);

hw_module_t *module;

char snd_sgtl5000[32], snd_spdif[32];

char **cardname = new char*[MAXCARDSNUM];

for (int i = 0; i < MAXCARDSNUM; i++) {

cardname[i] = new char[128];

memset(cardname[i],0,128);

}

int id;

// 加载库

int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,

(hw_module_t const**)&module);

// ALSA_HARDWARE_MODULE_ID的定义:#define ALSA_HARDWARE_MODULE_ID "alsa"

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int hw_get_module(const char *id, const struct hw_module_t **module)

{

int status;

int i;

const struct hw_module_t *hmi = NULL;

char prop[PATH_MAX];

char path[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.

*/

// 从上面的注释可知,如果要加载的.so已经被加载,则只是对已加载的.so增加一个引用,而不是重新加载。

// 函数dlopen也是线程安全的。

/* Loop through the configuration variants looking for a module */

// 寻找合适的module,找不到,就用默认的。

for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {

if (i < HAL_VARIANT_KEYS_COUNT) {

if (property_get(variant_keys[i], prop, NULL) == 0) {

continue;

}

snprintf(path, sizeof(path), "%s/%s.%s.so",

HAL_LIBRARY_PATH1, id, prop);

// HAL_LIBRARY_PATH1的定义:#define HAL_LIBRARY_PATH1 "/system/lib/hw"

if (access(path, R_OK) == 0) break;

snprintf(path, sizeof(path), "%s/%s.%s.so",

HAL_LIBRARY_PATH2, id, prop);

// HAL_LIBRARY_PATH2的定义:#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"

if (access(path, R_OK) == 0) break;

} else {

snprintf(path, sizeof(path), "%s/%s.default.so",

HAL_LIBRARY_PATH1, id);

if (access(path, R_OK) == 0) break;

}

}

status = -ENOENT;

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(id, path, module);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

load函数的实现:

/**

* 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);

if (handle == NULL) {

char const *err_str = dlerror();

LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");

status = -EINVAL;

goto done;

}

/* 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);

if (hmi == NULL) {

LOGE("load: couldn't find symbol %s", sym);

status = -EINVAL;

goto done;

}

/* Check that the id matches */

if (strcmp(id, hmi->id) != 0) {

LOGE("load: id=%s != hmi->id=%s", id, hmi->id);

status = -EINVAL;

goto done;

}

hmi->dso = handle;

/* success */

status = 0;

done:

if (status != 0) {

hmi = NULL;

if (handle != NULL) {

dlclose(handle);

handle = NULL;

}

} else {

LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",

id, path, *pHmi, handle);

}

*pHmi = hmi;

return status;

}

----------------------------------------------------------------

}

return status;

}

----------------------------------------------------------------

if (err == 0) {

hw_device_t* device;

err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);

// module的定义:hw_module_t *module;

// ALSA_HARDWARE_NAME的定义:#define ALSA_HARDWARE_NAME "alsa"

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体hw_module_t的定义:

/**

* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM

* and the fields of this data structure must begin with hw_module_t

* followed by module specific information.

*/

typedef struct hw_module_t {

/** tag must be initialized to HARDWARE_MODULE_TAG */

uint32_t tag;

/** major version number for the module */

uint16_t version_major;

/** minor version number of the module */

uint16_t version_minor;

/** Identifier of module */

const char *id;

/** Name of this module */

const char *name;

/** Author/owner/implementor of the module */

const char *author;

/** Modules methods */

struct hw_module_methods_t* methods;

/** module's dso */

void* dso;

/** padding to 128 bytes, reserved for future use */

uint32_t reserved[32-7];

} hw_module_t;

----------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

其中包含的hw_module_methods_t结构体:

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;

----------------------------------------------------------------

看看对结构体hw_module_t赋值的一个地方:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static hw_module_methods_t s_module_methods = {

open : s_device_open

};

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 : "i.MX51 ALSA module",

author : "Freescale Semiconductor",

methods : &s_module_methods,

dso : 0,

reserved : {0,},

};

----------------------------------------------------------------

由此可见,我们调用的应该是函数s_device_open:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int s_device_open(const hw_module_t* module, const char* name,

hw_device_t** device)

{

alsa_device_t *dev;

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体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);

};

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体hw_device_t的定义:

/**

* Every device data structure must begin with hw_device_t

* followed by module specific public methods and attributes.

*/

typedef struct hw_device_t {

/** tag must be initialized to HARDWARE_DEVICE_TAG */

uint32_t tag;

/** version number for hw_device_t */

uint32_t version;

/** reference to the module this device belongs to */

// 此处保存了一个module的引用。

// 通过module,打开一个device,在设备中,保存其所属module的引用

struct hw_module_t* module;

/** padding reserved for future use */

uint32_t reserved[12];

/** Close this device */

int (*close)(struct hw_device_t* device);

} hw_device_t;

----------------------------------------------------------------

----------------------------------------------------------------

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;

// 这个函数,就是用来初始化mDeviceList的

dev->init = s_init;

// 这个函数马上就会被调用到

dev->open = s_open;

dev->close = s_close;

dev->route = s_route;

*device = &dev->common;

LOGD("i.MX51 ALSA module opened");

return 0;

}

----------------------------------------------------------------

if (err == 0) {

mALSADevice = (alsa_device_t *)device;

// 此处对mDeviceList进行了初始化。

// 我们已经知道,此处其实是调用的s_init函数

mALSADevice->init(mALSADevice, mDeviceList);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static status_t s_init(alsa_device_t *module, ALSAHandleList &list)

{

LOGD("Initializing devices for IMX51 ALSA module");

list.clear();

for (size_t i = 0; i < ARRAY_SIZE(_defaults); i++) {

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

_defaults的定义:

static alsa_handle_t _defaults[] = {

{

module : 0,

devices : IMX51_OUT_DEFAULT,

curDev : 0,

curMode : 0,

handle : 0,

format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT

channels : 2,

sampleRate : DEFAULT_SAMPLE_RATE,

latency : 200000, // Desired Delay in usec

bufferSize : 6144, // Desired Number of samples

modPrivate : (void *)&setDefaultControls,

},

{

module : 0,

devices : IMX51_IN_DEFAULT,

curDev : 0,

curMode : 0,

handle : 0,

format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT

channels : 2,

sampleRate : DEFAULT_SAMPLE_RATE,

latency : 250000, // Desired Delay in usec

bufferSize : 6144, // Desired Number of samples

modPrivate : (void *)&setDefaultControls,

},

};

----------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体alsa_handle_t的定义:

struct alsa_handle_t {

alsa_device_t * module;

uint32_t devices;

uint32_t curDev;

int curMode;

snd_pcm_t * handle;

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;

int mmap; // mmap flags

};

----------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体snd_pcm_t的定义:

其实就是结构体_snd_pcm:

struct _snd_pcm {

437 snd_card_t *card;

438 unsigned int device; /* device number */

439 unsigned int info_flags;

440 unsigned short dev_class;

441 unsigned short dev_subclass;

442 char id[64];

443 char name[80];

444 snd_pcm_str_t streams[2];

445 struct semaphore open_mutex;

446 wait_queue_head_t open_wait;

447 void *private_data;

448 void (*private_free) (snd_pcm_t *pcm);

449 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)

450 snd_pcm_oss_t oss;

451 #endif

452 };

----------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体snd_card_t的定义:

----------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结构体snd_pcm_format_t的定义:

这个东东不是结构体,是个枚举,表示pcm 采样格式。

----------------------------------------------------------------

// 将传入的module引用保存到_defaults中

_defaults[i].module = module;

// 将_defaults中的成员push到mDeviceList中

list.push_back(_defaults[i]);

}

return NO_ERROR;

}

----------------------------------------------------------------

} else

LOGE("ALSA Module could not be opened!!!");

} else

LOGE("ALSA Module not found!!!");

/* found out sound cards in the system and new mixer controller for them*/

// cardname是一个指针数组,每一个成员指向一个char[128]数组。

err = findSoundCards(cardname);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

函数findSoundCards的实现:

static int findSoundCards(char **cardname)

{

int idx, dev, err;

snd_ctl_t *handle;

snd_hctl_t *hctlhandle;

snd_ctl_card_info_t *cardinfo;

snd_pcm_info_t *pcminfo;

char str[32];

snd_ctl_card_info_alloca(&cardinfo);

snd_pcm_info_alloca(&pcminfo);

snd_hctl_elem_t *elem;

snd_ctl_elem_id_t *id;

snd_ctl_elem_info_t *info;

snd_ctl_elem_id_alloca(&id);

snd_ctl_elem_info_alloca(&info);

idx = -1;

while (1) {

if ((err = snd_card_next(&idx)) < 0) {

LOGE("Card next error: %s\n", snd_strerror(err));

break;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

路径:external\alsa-lib\src\control\Cards.c

/**

* \brief Try to determine the next card.

* \param rcard pointer to card number

* \result zero if success, otherwise a negative error code

*

* Tries to determine the next card from given card number.

* If card number is -1, then the first available card is

* returned. If the result card number is -1, no more cards

* are available.

*/

int snd_card_next(int *rcard)

{

int card;

if (rcard == NULL)

return -EINVAL;

card = *rcard;

card = card < 0 ? 0 : card + 1;

for (; card < 32; card++) {

if (snd_card_load(card)) {

*rcard = card;

return 0;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Try to load the driver for a card.

* \param card Card number.

* \return 1 if driver is present, zero if driver is not present

*/

int snd_card_load(int card)

{

return !!(snd_card_load1(card) >= 0);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int snd_card_load1(int card)

{

int res;

char control[sizeof(SND_FILE_CONTROL) + 10];

// SND_FILE_CONTROL的定义:#define SND_FILE_CONTROLALSA_DEVICE_DIRECTORY
"controlC%i"

sprintf(control, SND_FILE_CONTROL, card);

res = snd_card_load2(control);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int snd_card_load2(const char *control)

{

int open_dev;

snd_ctl_card_info_t info;

open_dev = snd_open_device(control, O_RDONLY);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

路径:external\alsa-lib\include\Local.h

static inline int snd_open_device(const char *filename, int fmode)

{

int fd;

#ifdef O_CLOEXEC

fmode |= O_CLOEXEC;

#endif

fd = open(filename, fmode);

/* open with resmgr */

#ifdef SUPPORT_RESMGR

if (fd < 0) {

if (errno == EAGAIN || errno == EBUSY)

return fd;

if (! access(filename, F_OK))

fd = rsm_open_device(filename, fmode);

}

#endif

if (fd >= 0)

fcntl(fd, F_SETFD, FD_CLOEXEC);

return fd;

}

----------------------------------------------------------------

if (open_dev >= 0) {

// 调用的应该是kernel中的snd_ctl_ioctl函数

if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) {

int err = -errno;

close(open_dev);

return err;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

struct snd_ctl_file *ctl;

struct snd_card *card;

struct snd_kctl_ioctl *p;

void __user *argp = (void __user *)arg;

int __user *ip = argp;

int err;

ctl = file->private_data;

card = ctl->card;

if (snd_BUG_ON(!card))

return -ENXIO;

switch (cmd) {

case SNDRV_CTL_IOCTL_PVERSION:

return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;

case SNDRV_CTL_IOCTL_CARD_INFO:

return snd_ctl_card_info(card, ctl, cmd, argp);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,

unsigned int cmd, void __user *arg)

{

struct snd_ctl_card_info *info;

info = kzalloc(sizeof(*info), GFP_KERNEL);

if (! info)

return -ENOMEM;

down_read(&snd_ioctl_rwsem);

info->card = card->number;

strlcpy(info->id, card->id, sizeof(info->id));

strlcpy(info->driver, card->driver, sizeof(info->driver));

strlcpy(info->name, card->shortname, sizeof(info->name));

strlcpy(info->longname, card->longname, sizeof(info->longname));

strlcpy(info->mixername, card->mixername, sizeof(info->mixername));

strlcpy(info->components, card->components, sizeof(info->components));

up_read(&snd_ioctl_rwsem);

if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {

kfree(info);

return -EFAULT;

}

kfree(info);

return 0;

}

----------------------------------------------------------------

case SNDRV_CTL_IOCTL_ELEM_LIST:

return snd_ctl_elem_list(card, argp);

case SNDRV_CTL_IOCTL_ELEM_INFO:

return snd_ctl_elem_info_user(ctl, argp);

case SNDRV_CTL_IOCTL_ELEM_READ:

return snd_ctl_elem_read_user(card, argp);

case SNDRV_CTL_IOCTL_ELEM_WRITE:

return snd_ctl_elem_write_user(ctl, argp);

case SNDRV_CTL_IOCTL_ELEM_LOCK:

return snd_ctl_elem_lock(ctl, argp);

case SNDRV_CTL_IOCTL_ELEM_UNLOCK:

return snd_ctl_elem_unlock(ctl, argp);

case SNDRV_CTL_IOCTL_ELEM_ADD:

return snd_ctl_elem_add_user(ctl, argp, 0);

case SNDRV_CTL_IOCTL_ELEM_REPLACE:

return snd_ctl_elem_add_user(ctl, argp, 1);

case SNDRV_CTL_IOCTL_ELEM_REMOVE:

return snd_ctl_elem_remove(ctl, argp);

case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:

return snd_ctl_subscribe_events(ctl, ip);

case SNDRV_CTL_IOCTL_TLV_READ:

return snd_ctl_tlv_ioctl(ctl, argp, 0);

case SNDRV_CTL_IOCTL_TLV_WRITE:

return snd_ctl_tlv_ioctl(ctl, argp, 1);

case SNDRV_CTL_IOCTL_TLV_COMMAND:

return snd_ctl_tlv_ioctl(ctl, argp, -1);

case SNDRV_CTL_IOCTL_POWER:

return -ENOPROTOOPT;

case SNDRV_CTL_IOCTL_POWER_STATE:

#ifdef CONFIG_PM

return put_user(card->power_state, ip) ? -EFAULT : 0;

#else

return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;

#endif

}

down_read(&snd_ioctl_rwsem);

list_for_each_entry(p, &snd_control_ioctls, list) {

err = p->fioctl(card, ctl, cmd, arg);

if (err != -ENOIOCTLCMD) {

up_read(&snd_ioctl_rwsem);

return err;

}

}

up_read(&snd_ioctl_rwsem);

snd_printdd("unknown ioctl = 0x%x\n", cmd);

return -ENOTTY;

}

----------------------------------------------------------------

close(open_dev);

return info.card;

} else {

return -errno;

}

}

----------------------------------------------------------------

#ifdef SUPPORT_ALOAD

if (res < 0) {

// SND_FILE_LOAD的定义:#define SND_FILE_LOADALOAD_DEVICE_DIRECTORY
"aloadC%i"

char aload[sizeof(SND_FILE_LOAD) + 10];

sprintf(aload, SND_FILE_LOAD, card);

res = snd_card_load2(aload);

}

#endif

return res;

}

----------------------------------------------------------------

}

----------------------------------------------------------------

}

*rcard = -1;

return 0;

}

----------------------------------------------------------------

if (idx < 0)

break;

sprintf(str, "hw:CARD=%i", idx);

if ((err = snd_ctl_open(&handle, str, 0)) < 0) {

LOGE("Open error: %s\n", snd_strerror(err));

continue;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Opens a CTL

* \param ctlp Returned CTL handle

* \param name ASCII identifier of the CTL handle

* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)

* \return 0 on success otherwise a negative error code

*/

int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)

{

int err;

assert(ctlp && name);

err = snd_config_update();

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Updates #snd_config by rereading the global configuration files (if needed).

* \return 0 if #snd_config was up to date, 1 if #snd_config was

* updated, otherwise a negative error code.

*

* \warning Whenever #snd_config is updated, all string pointers and

* configuration node handles previously obtained from it may become

* invalid.

*

* \par Errors:

* Any errors encountered when parsing the input or returned by hooks or

* functions.

*

* \par Conforming to:

* LSB 3.2

*/

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);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Updates a configuration tree by rereading the configuration files (if needed).

* \param[in,out] _top Address of the handle to the top-level node.

* \param[in,out] _update Address of a pointer to private update information.

* \param[in] cfgs A list of configuration file names, delimited with ':'.

* If \p cfgs is \c NULL, the default global

* configuration file is used.

* \return 0 if \a _top was up to date, 1 if the configuration files

* have been reread, otherwise a negative error code.

*

* The variables pointed to by \a _top and \a _update can be initialized

* to \c NULL before the first call to this function. The private

* update information holds information about all used configuration

* files that allows this function to detects changes to them; this data

* can be freed with #snd_config_update_free.

*

* 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.

*

* \par Errors:

* Any errors encountered when parsing the input or returned by hooks or

* functions.

*/

int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)

{

int err;

const char *configs, *c;

unsigned int k;

size_t l;

snd_config_update_t *local;

snd_config_update_t *update;

snd_config_t *top;

assert(_top && _update);

top = *_top;

update = *_update;

configs = cfgs;

if (!configs) {

configs = getenv(ALSA_CONFIG_PATH_VAR);

if (!configs || !*configs)

configs = ALSA_CONFIG_PATH_DEFAULT;

}

for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {

c += l;

k++;

if (!*c)

break;

c++;

}

if (k == 0) {

local = NULL;

goto _reread;

}

local = (snd_config_update_t *)calloc(1, sizeof(snd_config_update_t));

if (!local)

return -ENOMEM;

local->count = k;

local->finfo = calloc(local->count, sizeof(struct finfo));

if (!local->finfo) {

free(local);

return -ENOMEM;

}

for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {

char name[l + 1];

memcpy(name, c, l);

name[l] = 0;

err = snd_user_file(name, &local->finfo[k].name);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#ifdef HAVE_WORDEXP_H

#include <wordexp.h>

#include <assert.h>

int snd_user_file(const char *file, char **result)

{

wordexp_t we;

int err;

assert(file && result);

err = wordexp(file, &we, WRDE_NOCMD);

switch (err) {

case WRDE_NOSPACE:

return -ENOMEM;

case 0:

if (we.we_wordc == 1)

break;

/* fall thru */

default:

wordfree(&we);

return -EINVAL;

}

*result = strdup(we.we_wordv[0]);

if (*result == NULL)

return -ENOMEM;

wordfree(&we);

return 0;

}

#else /* !HAVE_WORDEXP_H */

/* just copy the string - would be nicer to expand by ourselves, though... */

int snd_user_file(const char *file, char **result)

{

*result = strdup(file);

if (! *result)

return -ENOMEM;

return 0;

}

#endif /* HAVE_WORDEXP_H */

----------------------------------------------------------------

if (err < 0)

goto _end;

c += l;

k++;

if (!*c)

break;

c++;

}

for (k = 0; k < local->count; ++k) {

struct stat st;

struct finfo *lf = &local->finfo[k];

if (stat(lf->name, &st) >= 0) {

lf->dev = st.st_dev;

lf->ino = st.st_ino;

lf->mtime = st.st_mtime;

} else {

SNDERR("Cannot access file %s", lf->name);

free(lf->name);

memmove(&local->finfo[k], &local->finfo[k+1], sizeof(struct finfo) * (local->count - k - 1));

k--;

local->count--;

}

}

if (!update)

goto _reread;

if (local->count != update->count)

goto _reread;

for (k = 0; k < local->count; ++k) {

struct finfo *lf = &local->finfo[k];

struct finfo *uf = &update->finfo[k];

if (strcmp(lf->name, uf->name) != 0 ||

lf->dev != uf->dev ||

lf->ino != uf->ino ||

lf->mtime != uf->mtime)

goto _reread;

}

err = 0;

_end:

if (err < 0) {

if (top) {

snd_config_delete(top);

*_top = NULL;

}

if (update) {

snd_config_update_free(update);

*_update = NULL;

}

}

if (local)

snd_config_update_free(local);

return err;

_reread:

*_top = NULL;

*_update = NULL;

if (update) {

snd_config_update_free(update);

update = NULL;

}

if (top) {

snd_config_delete(top);

top = NULL;

}

err = snd_config_top(&top);

if (err < 0)

goto _end;

if (!local)

goto _skip;

for (k = 0; k < local->count; ++k) {

snd_input_t *in;

err = snd_input_stdio_open(&in, local->finfo[k].name, "r");

if (err >= 0) {

err = snd_config_load(top, in);

snd_input_close(in);

if (err < 0) {

SNDERR("%s may be old or corrupted: consider to remove or fix it", local->finfo[k].name);

goto _end;

}

} else {

SNDERR("cannot access file %s", local->finfo[k].name);

}

}

_skip:

err = snd_config_hooks(top, NULL);

if (err < 0) {

SNDERR("hooks failed, removing configuration");

goto _end;

}

*_top = top;

*_update = local;

return 1;

}

----------------------------------------------------------------

#ifdef HAVE_LIBPTHREAD

pthread_mutex_unlock(&snd_config_update_mutex);

#endif

return err;

}

----------------------------------------------------------------

if (err < 0)

return err;

return snd_ctl_open_noupdate(ctlp, snd_config, name, mode);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const char *name, int mode)

{

int err;

snd_config_t *ctl_conf;

err = snd_config_search_definition(root, "ctl", name, &ctl_conf);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Searches for a definition in a configuration tree, using

* aliases and expanding hooks and arguments.

* \param[in] config Handle to the configuration (sub)tree to search.

* \param[in] base Implicit key base, or \c NULL for none.

* \param[in] name Key suffix, optionally with arguments.

* \param[out] result The function puts the handle to the expanded found

* node at the address specified by \a result.

* \return A non-negative value if successful, otherwise a negative error code.

*

* This functions searches for a child node of \a config, allowing

* aliases and expanding hooks, like #snd_config_search_alias_hooks.

*

* If \a name contains a colon (:), the rest of the string after the

* colon contains arguments that are expanded as with

* #snd_config_expand.

*

* In any case, \a result is a new node that must be freed by the

* caller.

*

* \par Errors:

* <dl>

* <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.

* <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is

* not a compound node.

* </dl>

* Additionally, any errors encountered when parsing the hook

* definitions or arguments, or returned by (hook) functions.

*/

int snd_config_search_definition(snd_config_t *config,

const char *base, const char *name,

snd_config_t **result)

{

snd_config_t *conf;

char *key;

const char *args = strchr(name, ':');

int err;

if (args) {

args++;

key = alloca(args - name);

memcpy(key, name, args - name - 1);

key[args - name - 1] = '\0';

} else {

key = (char *) name;

}

/*

* if key contains dot (.), the implicit base is ignored

* and the key starts from root given by the 'config' parameter

*/

err = snd_config_search_alias_hooks(config, strchr(key, '.') ? NULL : base, key, &conf);

if (err < 0)

return err;

return snd_config_expand(conf, config, args, NULL, result);

}

----------------------------------------------------------------

if (err < 0) {

SNDERR("Invalid CTL %s", name);

return err;

}

err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,

snd_config_t *ctl_root, snd_config_t *ctl_conf, int mode)

{

const char *str;

char *buf = NULL, *buf1 = NULL;

int err;

snd_config_t *conf, *type_conf = NULL;

snd_config_iterator_t i, next;

const char *lib = NULL, *open_name = NULL;

const char *id;

int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;

#ifndef PIC

extern void *snd_control_open_symbols(void);

#endif

void *h = NULL;

if (snd_config_get_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) {

if (name)

SNDERR("Invalid type for CTL %s definition", name);

else

SNDERR("Invalid type for CTL definition");

return -EINVAL;

}

err = snd_config_search(ctl_conf, "type", &conf);

if (err < 0) {

SNDERR("type is not defined");

return err;

}

err = snd_config_get_id(conf, &id);

if (err < 0) {

SNDERR("unable to get id");

return err;

}

err = snd_config_get_string(conf, &str);

if (err < 0) {

SNDERR("Invalid type for %s", id);

return err;

}

err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);

if (err >= 0) {

if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {

SNDERR("Invalid type for CTL type %s definition", str);

goto _err;

}

snd_config_for_each(i, next, type_conf) {

snd_config_t *n = snd_config_iterator_entry(i);

const char *id;

if (snd_config_get_id(n, &id) < 0)

continue;

if (strcmp(id, "comment") == 0)

continue;

if (strcmp(id, "lib") == 0) {

err = snd_config_get_string(n, &lib);

if (err < 0) {

SNDERR("Invalid type for %s", id);

goto _err;

}

continue;

}

if (strcmp(id, "open") == 0) {

err = snd_config_get_string(n, &open_name);

if (err < 0) {

SNDERR("Invalid type for %s", id);

goto _err;

}

continue;

}

SNDERR("Unknown field %s", id);

err = -EINVAL;

goto _err;

}

}

if (!open_name) {

buf = malloc(strlen(str) + 32);

if (buf == NULL) {

err = -ENOMEM;

goto _err;

}

open_name = buf;

sprintf(buf, "_snd_ctl_%s_open", str);

}

if (!lib) {

const char *const *build_in = build_in_ctls;

while (*build_in) {

if (!strcmp(*build_in, str))

break;

build_in++;

}

if (*build_in == NULL) {

buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);

if (buf1 == NULL) {

err = -ENOMEM;

goto _err;

}

lib = buf1;

sprintf(buf1, "%s/libasound_module_ctl_%s.so", ALSA_PLUGIN_DIR, str);

}

}

#ifndef PIC

snd_control_open_symbols();

#endif

open_func = snd_dlobj_cache_lookup(open_name);

if (open_func) {

err = 0;

goto _err;

}

h = snd_dlopen(lib, RTLD_NOW);

if (h)

open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_CONTROL_DLSYM_VERSION));

err = 0;

if (!h) {

SNDERR("Cannot open shared library %s", lib);

err = -ENOENT;

} else if (!open_func) {

SNDERR("symbol %s is not defined inside %s", open_name, lib);

snd_dlclose(h);

err = -ENXIO;

}

_err:

if (type_conf)

snd_config_delete(type_conf);

if (err >= 0) {

err = open_func(ctlp, name, ctl_root, ctl_conf, mode);

if (err >= 0) {

if (h /*&& (mode & SND_CTL_KEEP_ALIVE)*/) {

snd_dlobj_cache_add(open_name, h, open_func);

h = NULL;

}

(*ctlp)->dl_handle = h;

err = 0;

} else {

if (h)

snd_dlclose(h);

}

}

free(buf);

free(buf1);

return err;

}

----------------------------------------------------------------

snd_config_delete(ctl_conf);

return err;

}

----------------------------------------------------------------

}

----------------------------------------------------------------

// 函数snd_ctl_card_info前面已经见过

if ((err = snd_ctl_card_info(handle, cardinfo)) < 0) {

LOGE("HW info error: %s\n", snd_strerror(err));

continue;

}

LOGD("Soundcard #%i:\n", idx + 1);

LOGD(" card - %i\n", snd_ctl_card_info_get_card(cardinfo));

LOGD(" id - '%s'\n", snd_ctl_card_info_get_id(cardinfo));

LOGD(" driver - '%s'\n", snd_ctl_card_info_get_driver(cardinfo));

LOGD(" name - '%s'\n", snd_ctl_card_info_get_name(cardinfo));

LOGD(" longname - '%s'\n", snd_ctl_card_info_get_longname(cardinfo));

LOGD(" mixername - '%s'\n", snd_ctl_card_info_get_mixername(cardinfo));

LOGD(" components - '%s'\n", snd_ctl_card_info_get_components(cardinfo));

strcpy(cardname[idx], snd_ctl_card_info_get_name(cardinfo));

LOGD("\n\n-----get cart name and id: %s : %d",cardname[idx],idx);

snd_ctl_close(handle);

if ((err = snd_hctl_open(&hctlhandle, str, 0)) < 0) {

LOGE("Control %s open error: %s", str, snd_strerror(err));

return err;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Opens an HCTL

* \param hctlp Returned HCTL handle

* \param name ASCII identifier of the underlying CTL handle

* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)

* \return 0 on success otherwise a negative error code

*/

int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode)

{

snd_ctl_t *ctl;

int err;

if ((err = snd_ctl_open(&ctl, name, mode)) < 0)

return err;

err = snd_hctl_open_ctl(hctlp, ctl);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Opens an HCTL

* \param hctlp Returned HCTL handle

* \param ctl underlying CTL handle

* \return 0 on success otherwise a negative error code

*/

int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl)

{

snd_hctl_t *hctl;

assert(hctlp);

*hctlp = NULL;

if ((hctl = (snd_hctl_t *)calloc(1, sizeof(snd_hctl_t))) == NULL)

return -ENOMEM;

INIT_LIST_HEAD(&hctl->elems);

hctl->ctl = ctl;

*hctlp = hctl;

return 0;

}

----------------------------------------------------------------

if (err < 0)

snd_ctl_close(ctl);

return err;

}

----------------------------------------------------------------

if ((err = snd_hctl_load(hctlhandle)) < 0) {

LOGE("Control %s local error: %s\n", str, snd_strerror(err));

return err;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Load an HCTL with all elements and sort them

* \param hctl HCTL handle

* \return 0 on success otherwise a negative error code

*/

int snd_hctl_load(snd_hctl_t *hctl)

{

snd_ctl_elem_list_t list;

int err = 0;

unsigned int idx;

assert(hctl);

assert(hctl->ctl);

assert(hctl->count == 0);

assert(list_empty(&hctl->elems));

memset(&list, 0, sizeof(list));

if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)

goto _end;

while (list.count != list.used) {

err = snd_ctl_elem_list_alloc_space(&list, list.count);

if (err < 0)

goto _end;

if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)

goto _end;

}

if (hctl->alloc < list.count) {

hctl->alloc = list.count;

free(hctl->pelems);

hctl->pelems = malloc(hctl->alloc * sizeof(*hctl->pelems));

if (!hctl->pelems) {

err = -ENOMEM;

goto _end;

}

}

for (idx = 0; idx < list.count; idx++) {

snd_hctl_elem_t *elem;

elem = calloc(1, sizeof(snd_hctl_elem_t));

if (elem == NULL) {

snd_hctl_free(hctl);

err = -ENOMEM;

goto _end;

}

elem->id = list.pids[idx];

elem->hctl = hctl;

elem->compare_weight = get_compare_weight(&elem->id);

hctl->pelems[idx] = elem;

list_add_tail(&elem->list, &hctl->elems);

hctl->count++;

}

if (!hctl->compare)

hctl->compare = snd_hctl_compare_default;

snd_hctl_sort(hctl);

for (idx = 0; idx < hctl->count; idx++) {

int res = snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD,

hctl->pelems[idx]);

if (res < 0)

return res;

}

err = snd_ctl_subscribe_events(hctl->ctl, 1);

_end:

free(list.pids);

return err;

}

----------------------------------------------------------------

for (elem = snd_hctl_first_elem(hctlhandle); elem; elem = snd_hctl_elem_next(elem)) {

if ((err = snd_hctl_elem_info(elem, info)) < 0) {

LOGE("Control %s snd_hctl_elem_info error: %s\n", str, snd_strerror(err));

return err;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Get information for an HCTL element

* \param elem HCTL element

* \param info HCTL element information

* \return 0 otherwise a negative error code on failure

*/

int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)

{

assert(elem);

assert(elem->hctl);

assert(info);

info->id = elem->id;

return snd_ctl_elem_info(elem->hctl->ctl, info);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Get CTL element information

* \param ctl CTL handle

* \param info CTL element id/information pointer

* \return 0 on success otherwise a negative error code

*/

int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)

{

assert(ctl && info && (info->id.name[0] || info->id.numid));

return ctl->ops->element_info(ctl, info);

}

----------------------------------------------------------------

}

----------------------------------------------------------------

snd_hctl_elem_get_id(elem, id);

show_control_id(id);

}

snd_hctl_close(hctlhandle);

}

snd_config_update_free_global();

return 0;

}

----------------------------------------------------------------

// 根据cardname创建一个mixer

if (err == 0) {

for (id = 0; id < MAXCARDSNUM; id++) {

if(cardname[id] && strstr(cardname[id],SPDIF)){

LOGD(" CARD NAME: %s ID %d", cardname[id],id);

sprintf(snd_spdif,"hw:0%d",id);

sprintf(snd_spdif,"hw:CARD=%d",id);

mMixerSpdif = new ALSAMixer(snd_spdif);

}else if (cardname[id] && strstr(cardname[id],SGTL5000)){

LOGD(" CARD NAME: %s ID %d", cardname[id],id);

sprintf(snd_sgtl5000,"hw:0%d",id);

sprintf(snd_sgtl5000,"hw:CARD=%d",id);

mMixerSgtl5000 = new ALSAMixer(snd_sgtl5000);

}

}

} else {

LOGE("Don't find any Sound cards, use default");

mMixerSgtl5000 = new ALSAMixer("hw:00");

mMixerSpdif = new ALSAMixer("hw:00");

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ALSAMixer的构造函数:

ALSAMixer::ALSAMixer(const char *sndcard)

{

int err;

LOGI(" Init ALSAMIXER for SNDCARD : %s",sndcard);

mixerMasterProp =

ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");

mixerProp = {

ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"),

ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker", ""),

ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"),

ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"),

ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"),

ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL)

};

initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], sndcard);

initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], sndcard);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

函数initMixer的实现:

static int initMixer (snd_mixer_t **mixer, const char *name)

{

int err;

if ((err = snd_mixer_open(mixer, 0)) < 0) {

LOGE("Unable to open mixer: %s", snd_strerror(err));

return err;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Opens an empty mixer

* \param mixerp Returned mixer handle

* \param mode Open mode

* \return 0 on success otherwise a negative error code

*/

int snd_mixer_open(snd_mixer_t **mixerp, int mode ATTRIBUTE_UNUSED)

{

snd_mixer_t *mixer;

assert(mixerp);

mixer = calloc(1, sizeof(*mixer));

if (mixer == NULL)

return -ENOMEM;

INIT_LIST_HEAD(&mixer->slaves);

INIT_LIST_HEAD(&mixer->classes);

INIT_LIST_HEAD(&mixer->elems);

mixer->compare = snd_mixer_compare_default;

*mixerp = mixer;

return 0;

}

----------------------------------------------------------------

if ((err = snd_mixer_attach(*mixer, name)) < 0) {

LOGW("Unable to attach mixer to device %s: %s",

name, snd_strerror(err));

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Attach an HCTL specified with the CTL device name to an opened mixer

* \param mixer Mixer handle

* \param name HCTL name (see #snd_hctl_open)

* \return 0 on success otherwise a negative error code

*/

int snd_mixer_attach(snd_mixer_t *mixer, const char *name)

{

snd_hctl_t *hctl;

int err;

err = snd_hctl_open(&hctl, name, 0);

if (err < 0)

return err;

err = snd_mixer_attach_hctl(mixer, hctl);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Attach an HCTL to an opened mixer

* \param mixer Mixer handle

* \param hctl the HCTL to be attached

* \return 0 on success otherwise a negative error code

*/

int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)

{

snd_mixer_slave_t *slave;

int err;

assert(hctl);

slave = calloc(1, sizeof(*slave));

if (slave == NULL)

return -ENOMEM;

err = snd_hctl_nonblock(hctl, 1);

if (err < 0) {

snd_hctl_close(hctl);

free(slave);

return err;

}

snd_hctl_set_callback(hctl, hctl_event_handler);

snd_hctl_set_callback_private(hctl, mixer);

slave->hctl = hctl;

list_add_tail(&slave->list, &mixer->slaves);

return 0;

}

----------------------------------------------------------------

if (err < 0) {

snd_hctl_close(hctl);

return err;

}

return 0;

}

----------------------------------------------------------------

if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {

LOGE("Unable to attach mixer to device default: %s",

snd_strerror(err));

snd_mixer_close (*mixer);

*mixer = NULL;

return err;

}

}

if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {

LOGE("Unable to register mixer elements: %s", snd_strerror(err));

snd_mixer_close (*mixer);

*mixer = NULL;

return err;

}

// Get the mixer controls from the kernel

if ((err = snd_mixer_load(*mixer)) < 0) {

LOGE("Unable to load mixer elements: %s", snd_strerror(err));

snd_mixer_close (*mixer);

*mixer = NULL;

return err;

}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \brief Load a mixer elements

* \param mixer Mixer handle

* \return 0 on success otherwise a negative error code

*/

int snd_mixer_load(snd_mixer_t *mixer)

{

struct list_head *pos;

list_for_each(pos, &mixer->slaves) {

int err;

snd_mixer_slave_t *s;

s = list_entry(pos, snd_mixer_slave_t, list);

err = snd_hctl_load(s->hctl);

if (err < 0)

return err;

}

return 0;

}

----------------------------------------------------------------

return 0;

}

----------------------------------------------------------------

snd_mixer_selem_id_t *sid;

snd_mixer_selem_id_alloca(&sid);

for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {

mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;

property_get (mixerMasterProp[i].propName,

info->name,

mixerMasterProp[i].propDefault);

for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);

elem;

elem = snd_mixer_elem_next(elem)) {

if (!snd_mixer_selem_is_active(elem))

continue;

snd_mixer_selem_get_id(elem, sid);

// Find PCM playback volume control element.

const char *elementName = snd_mixer_selem_id_get_name(sid);

if (info->elem == NULL &&

strcmp(elementName, info->name) == 0 &&

hasVolume[i] (elem)) {

info->elem = elem;

getVolumeRange[i] (elem, &info->min, &info->max);

info->volume = info->max;

setVol[i] (elem, info->volume);

if (i == SND_PCM_STREAM_PLAYBACK &&

snd_mixer_selem_has_playback_switch (elem))

snd_mixer_selem_set_playback_switch_all (elem, 1);

break;

}

}

LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");

for (int j = 0; mixerProp[j][i].device; j++) {

mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;

property_get (mixerProp[j][i].propName,

info->name,

mixerProp[j][i].propDefault);

for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);

elem;

elem = snd_mixer_elem_next(elem)) {

if (!snd_mixer_selem_is_active(elem))

continue;

snd_mixer_selem_get_id(elem, sid);

// Find PCM playback volume control element.

const char *elementName = snd_mixer_selem_id_get_name(sid);

if (info->elem == NULL &&

strcmp(elementName, info->name) == 0 &&

hasVolume[i] (elem)) {

info->elem = elem;

getVolumeRange[i] (elem, &info->min, &info->max);

info->volume = info->max;

setVol[i] (elem, info->volume);

if (i == SND_PCM_STREAM_PLAYBACK &&

snd_mixer_selem_has_playback_switch (elem))

snd_mixer_selem_set_playback_switch_all (elem, 1);

break;

}

}

LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");

}

}

LOGV("mixer initialized.");

}

----------------------------------------------------------------

for (int i = 0; i < MAXCARDSNUM; i++) {

delete []cardname[i];

}

delete []cardname;

mCurCard = new char[128];

if (!mCurCard)

LOGE("allocate memeory to store current sound card name fail");

memset(mCurCard,0,sizeof(mCurCard));

/* set current card as sgtl5000 default */

if(mMixerSgtl5000)

{

strcpy(mCurCard,SGTL5000);

mMixer = mMixerSgtl5000;

}else if(mMixerSpdif)

{

strcpy(mCurCard,SPDIF);

mMixer = mMixerSpdif;

}

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);

if (err == 0)

mAcousticDevice = (acoustic_device_t *)device;

else

LOGE("Acoustics Module not found.");

}

}

----------------------------------------------------------------

it != mDeviceList.end(); ++it)

if (it->devices & devices) {

// 此处调用的其实是函数s_open

// devices就是我们刚开始看参数时看的那个东东

err = mALSADevice->open(&(*it), devices, mode());

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

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);

// 在这儿使用到了devices

const char *devName = deviceName(handle, devices, mode, 1);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//card_device =0, return the card name, card_device=1, return the card device name

const char *deviceName(alsa_handle_t *alsa_handle, uint32_t device, int mode, int card_device)

{

snd_ctl_t *handle;

int card, err, dev, idx;

snd_ctl_card_info_t *info;

snd_pcm_info_t *pcminfo;

snd_ctl_card_info_alloca(&info);

snd_pcm_info_alloca(&pcminfo);

int cardnum = 0;

char value[PROPERTY_VALUE_MAX];

snd_pcm_stream_t stream = direction(alsa_handle);

bool havespdifdevice = false;

bool havesgtldevice = false;

card = -1;

if (snd_card_next(&card) < 0 || card < 0) {

LOGD("no soundcards found...");

return "default";

}

LOGD("**** List of %s Hardware Devices ****\n",

snd_pcm_stream_name(stream));

while (card >= 0) {

char name[32];

sprintf(name, "hw:%d", card);

if ((err = snd_ctl_open(&handle, name, 0)) < 0) {

LOGD("control open (%i): %s", card, snd_strerror(err));

goto next_card;

}

if ((err = snd_ctl_card_info(handle, info)) < 0) {

LOGD("control hardware info (%i): %s", card, snd_strerror(err));

snd_ctl_close(handle);

goto next_card;

}

dev = -1;

while (1) {

unsigned int count;

if (snd_ctl_pcm_next_device(handle, &dev)<0)

LOGD("snd_ctl_pcm_next_device");

if (dev < 0)

break;

snd_pcm_info_set_device(pcminfo, dev);

snd_pcm_info_set_subdevice(pcminfo, 0);

snd_pcm_info_set_stream(pcminfo, stream);

if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {

if (err != -ENOENT)

LOGD("control digital audio info (%i): %s", card, snd_strerror(err));

continue;

}

LOGD("card %i: %s [%s], device %i: %s [%s]\n",

card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),

dev,

snd_pcm_info_get_id(pcminfo),

snd_pcm_info_get_name(pcminfo));

if(strcmp(snd_pcm_info_get_id(pcminfo),"IMX SPDIF mxc spdif-0")==0) {

if(card_device==0) sprintf(spdifcardname, "hw:0%d", card);

else sprintf(spdifcardname,
"hw:%d,%d", card, dev);

havespdifdevice = true;

}

if(strcmp(snd_pcm_info_get_id(pcminfo),"SGTL5000 SGTL5000-0")==0) {

if(card_device==0) sprintf(sgtlcardname, "hw:0%d", card);

else sprintf(sgtlcardname, "hw:%d,%d", card, dev);

havesgtldevice = true;

}

cardnum++;

}

snd_ctl_close(handle);

next_card:

if (snd_card_next(&card) < 0) {

LOGD("snd_card_next");

break;

}

}

property_get("ro.HDMI_AUDIO_OUTPUT", value, "");

// 只是在这儿用了一下

if((device & AudioSystem::DEVICE_OUT_WIRED_HDMI) && havespdifdevice && (strcmp(value, "1") == 0))

{

return spdifcardname;

}else if(havesgtldevice)

{

return sgtlcardname;

}

return "default";

}

----------------------------------------------------------------

// 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.

int err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/**

* \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);

// 函数snd_config_update前面已经见过

err = snd_config_update();

if (err < 0)

return err;

// 前面已经见过函数snd_ctl_open_noupdate

return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 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;

}

----------------------------------------------------------------

if (err) break;

if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){

strcpy(mCurCard ,SPDIF);

mMixer = mMixerSpdif;

} else {

strcpy(mCurCard,SGTL5000);

mMixer = mMixerSgtl5000;

}

out = new AudioStreamOutALSA(this, &(*it));

err = out->set(format, channels, sampleRate);

break;

}

if (status) *status = err;

return out;

}

###############################################################################################

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

函数openOutputStream的功能:

1、从mDeviceList中寻找匹配的设备。

寻找的依据是传入的参数devices。

参数devices是首先根据stream type获得strategy,然后再根据strategy取得的。

mDeviceList的内容是从_defaults[]数组中copy过来的。

若要使自己的声卡工作,需要在_defaults[]中加入自己声卡的信息,或者修改函数s_init的实现方式。

2、调用函数s_open,得到一个alsa_handle_t指针。

3、根据得到的alsa_handle_t指针创建一个AudioStreamOutALSA对象。

4、调用AudioStreamOutALSA对象的set函数设置stream的格式,声道和采样率。

5、至此,就完成了output stream的创建。

6、调用AudioStreamOutALSA对象的write函数即可实现播放。

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: