Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数
2011-10-11 16:53
531 查看
发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数。
以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些。
闲言少叙,跳入代码。
前两天看AudioTrack创建的时候,我们看到了AudioHardwareALSA::openOutputStream,并没有继续往下看。
今天就看看函数AudioHardwareALSA::openOutputStream的实现。
*****************************************源码*************************************************
**********************************************************************************************
源码路径:
hardware\alsa_sound\AudioHardwareALSA.cpp
###########################################说明################################################
###############################################################################################
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
函数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函数即可实现播放。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些。
闲言少叙,跳入代码。
前两天看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函数即可实现播放。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
相关文章推荐
- Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数(下)
- Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数
- Android Audio代码分析11 - AudioStreamOutALSA::write函数
- Android Audio代码分析11 - AudioStreamOutALSA::write函数
- android系统中ALSA架构audio -EPIPE错误分析及对策
- Android Audio代码分析9 - AudioTrack::write函数
- Android Audio代码分析18 - setSampleRate函数
- Android Audio代码分析20 - queryEffects函数
- Android Audio代码分析24 - AudioEffect::setEnabled函数
- Android Audio代码分析9 - AudioTrack::write函数
- Android Audio代码分析19 - setPosition函数
- Android Audio代码分析21 - 创建AudioEffect对象
- Android Audio代码分析24 - AudioEffect::setEnabled函数
- Android Audio代码分析3 - 创建AudioTrack对象
- Android Audio代码分析19 - setPosition函数
- Android Audio代码分析21 - 创建AudioEffect对象
- 通过rk 代码,分析android 及kernel 中audio 的控制以及binder的流程
- Android Audio代码分析1 - AudioTrack使用示例
- Android Audio AudioHardwareALSA::openOutputStream函数
- Android Audio代码分析13 - AudioTrack::getPosition函数