在一个设备平台上可能会支持很多种输出–包括蓝牙、功放、耳机等,音频来源也分很多种–闹钟、提示音、多媒体、电话、视频等等,对于这么多音频又如何正确的让音频从对应的设备播放出来呢?这就是音频播放策略所完成的事,本节围绕这个话题来分析一下源码。
将本节内容分为两节,一是方案商对平台音频接口的配置信息的加载和解析过程,组织成怎样的数据结构,这是音频播放策略的重要依据 , 二就是看一下如何根据播放的音频类型来选取对应的播放策略、选择合适的设备和输出接口。

1、音频接口配置信息的加载

开机过程中在拉起audio service时候,会去加载音频配置信息,然后读取内容,打开相应的输出通道,创建输出线程(playbackthread),支持的设备决定了音频播放策略。
首先,\frameworks\av\media\mediaserver\main_mediaserver.cpp会创建

AudioPolicyService::instantiate();

导致audiopolicyservice的构造函数中创建audio policy
\frameworks\av\services\audioflinger\AudioPolicyService.cpp

rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);//加载audio_policy.default.so,获取module信息(由audio_policy_hal.cpp编译而来)
rc = audio_policy_dev_open(module, &mpAudioPolicyDev); //调用加载的legacy_ap_module模块中中open函数,并获取struct audio_policy_device 结构体初始化信息
rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,&mpAudioPolicy);//create_audio_policy所对应的函数是create_legacy_ap

这里顺便说一下hw_get_module,接口实现在hardware\libhardware\hardware.c:

int hw_get_module(const char *id, const struct hw_module_t **module)
{return hw_get_module_by_class(id, NULL, module); //通过hardware id获取 对应module信息
}int hw_get_module_by_class(const char *class_id, const char *inst, //inst可以是库的部分名字const struct hw_module_t **module)
{   ...if (inst)snprintf(name, PATH_MAX, "%s.%s", class_id, inst);elsestrlcpy(name, class_id, PATH_MAX);/** Here we rely on the fact that calling dlopen multiple times on* the same .so will simply increment a refcount (and not load* a new copy of the library).* We also assume that dlopen() is thread-safe.*//* Loop through the configuration variants looking for a module */for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {if (i < HAL_VARIANT_KEYS_COUNT) { //在初始化的variant_keys中查找想要的hardwareif (property_get(variant_keys[i], prop, NULL) == 0) {continue;}snprintf(path, sizeof(path), "%s/%s.%s.so", //优先在path2中查找HAL_LIBRARY_PATH2, name, prop); //path2 /vendor/lib/hwif (access(path, R_OK) == 0) break; //snprintf(path, sizeof(path), "%s/%s.%s.so",HAL_LIBRARY_PATH1, name, prop);//path1 /system/lib/hwif (access(path, R_OK) == 0) break;} else {snprintf(path, sizeof(path), "%s/%s.default.so",//没有在variant_keys找到库的部分名字内容就使用默认的,defaultHAL_LIBRARY_PATH2, name);if (access(path, R_OK) == 0) break;...}}if (i < HAL_VARIANT_KEYS_COUNT+1) {/* load the module, if this fails, we're doomed, and we should not try* to load a different variant. */status = load(class_id, path, module); //找到对应的so时,使用load加载,获取信息}return status;
}
/*** Load the file defined by the variant and if successful* return the dlopen handle and the hmi.* @return 0 = success, !0 = failure.*/
static int load(const char *id,const char *path,const struct hw_module_t **pHmi)
{int status;void *handle;struct hw_module_t *hmi;/** load the symbols resolving undefined symbols before* dlopen returns. Since RTLD_GLOBAL is not or'd in with* RTLD_NOW the external symbols will not be global*/handle = dlopen(path, RTLD_NOW); //handle,打开动态库的句柄.../* Get the address of the struct hal_module_info. */const char *sym = HAL_MODULE_INFO_SYM_AS_STR;hmi = (struct hw_module_t *)dlsym(handle, sym); //获取 hardware module info的首地址,我们需要的最终结果,//如以下形式,以audio为例/*struct legacy_ap_module HAL_MODULE_INFO_SYM = {module: {  //--->目标common: {tag: HARDWARE_MODULE_TAG,version_major: 1,version_minor: 0,id: AUDIO_POLICY_HARDWARE_MODULE_ID,name: "LEGACY Audio Policy HAL",author: "The Android Open Source Project",methods: &legacy_ap_module_methods, //方法集合,用来初始胡硬件和获取硬件信息dso : NULL,reserved : {0},},},};/* .../* Check that the id matches */if (strcmp(id, hmi->id) != 0) {...hmi->dso = handle;...*pHmi = hmi;return status;
}

从上面的代码分析可知,HAL层有关硬件的初始化和信息描述,会被编译成对应的xxx.xxx.so放在系统的指定目录下,用于JNI层来获取这些信息。
继续看create_legacy_ap是HAL层文件\hardware\libhardware_legacy\audio\audio_policy_hal.cpp中定义的接口,大概内容如下:

struct legacy_audio_policy *lap;
...
lap->policy.set_device_connection_state = ap_set_device_connection_state;//初始化audio_policy结构体,音频策略
lap->policy.get_device_connection_state = ap_get_device_connection_state;
...
lap->policy.get_strategy_for_stream = ap_get_strategy_for_stream; //为不同音频stream获取策略
lap->policy.get_devices_for_stream = ap_get_devices_for_stream;//为不同音频stream获取devices
...
lap->policy.dump = ap_dump;
lap->policy.is_offload_supported = ap_is_offload_supported;
lap->service = service;
lap->aps_ops = aps_ops;
lap->service_client =new AudioPolicyCompatClient(aps_ops, service); //创建对象
if (!lap->service_client) {ret = -ENOMEM;goto err_new_compat_client;
}
lap->apm = createAudioPolicyManager(lap->service_client);//创建AudioPolicy管理对象

重要内容在createAudioPolicyManager中,其创建对象的基类是AudioPolicyManagerBase,所以会导致其构造函数的执行,而加载音频策略和设备就是在这里实现。一起来看下:

//这里面所做的内容就是加载配置文件,获取支持的音频接口(以primary为例,还有usb、a2dp..)
if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR){}
...
// must be done after reading the policy --在读取策略后必须做的事,初始化音量调节表,不同音频流的音量调节表是不同的
initializeVolumeCurves();
...for (size_t i = 0; i < mHwModules.size(); i++) { //向量mHwModules(单元是usb、a2dp每个节点的信息)在loadHwModule函数中被填充//这里的loadHwModule被一层层包装,最终在AudioPolicyService.cpp被实现,指向AudioFlinger中的loadHwModule函数,后续我们再详细分析做了什么mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName);if (mHwModules[i]->mHandle == 0) {ALOGW("could not open HW module %s", mHwModules[i]->mName);continue;}// open all output streams needed to access attached devices --打开绑定在设备上的所以输出流// except for direct output streams that are only opened when they are actually// required by an app. //除了直接输出流是在真正被app使用的时候再打开 ???为什么?使用率低?for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++){  //遍历同一个音频接口(比如primary)中的所有outputsconst IOProfile *outProfile = mHwModules[i]->mOutputProfiles[j];if ((outProfile->mSupportedDevices & mAttachedOutputDevices) && //支持的设备,并且可以一直被访问到的((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) { //不是direct device,可以是fast、primary...AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile);outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice &outProfile->mSupportedDevices);audio_io_handle_t output = mpClientInterface->openOutput(...//打开此outputs,同样指向了AudioFlinger中的 openOutput,抽象的输出音频接口

我们来看一下,加载的audio_policy.conf里到底是什么内容:
在手头上的平台,配置文件路径为 /vendor/etc/audio_policy.conf,里面定义了平台支持的音频接口(primary、a2dp、usb、r_submix…),每个音频接口都又包括若干个output(primary/fast/compress_offload/direct…)和input接口。源码中用IOprofile数据结构来描述每一个输入或者输出接口,数据结构中存储着这个接口支持的采样率、声道数、位深、以及支持的设备。具体内容如下–只展示了其中一种接口primary:
如下的图中,outputs就包含3个IOProfile(primary、compress_offload、fast)

audio_policy.conf所描述的关系图如下图所示:
图片来源:https://blog.csdn.net/yangwen123/article/details/39497375


对照上面的文件内容和图示,我们可以更好的理解loadAudioPolicyConfig所实现的功能,就是把conf文件里的内容使用我们定义的数据结构描述出来并存储到指定的变量中(AudioPolicyManagerBase中的Vector <HwModule *> mHwModules,每一个audio interface是一个module(比如a2dp是其中一个))。

说到这里,audio_policy.conf文件的解析是已经完成了。但是好像还是没有明白打开这个文件获取这下配置信息有什么作用?还有就是陷进AudioFlinger中的loadHwModule和openOutput都做了什么?
我们从conf里获取到了平台支持的音频接口,有primary、a2dp、usb…(假设),以及他们的outputs和inputs信息,接着就是需要打开对应的音频接口,获取对应接口的module信息,上面说了每个so都对应一个module。
所以我们也许可以从AudioFlinger中的loadHwModule找到答案。
现在来看一下AudioFlinger:loadHwModule做了什么事:

audio_module_handle_t AudioFlinger::loadHwModule(const char *name)
{if (!settingsAllowed()) {return 0;}Mutex::Autolock _l(mLock);return loadHwModule_l(name); //封装的loadHwModule_l函数
}// loadHwModule_l() must be called with AudioFlinger::mLock held
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{//mAudioHwDevs:是AudioFlinger的成员变量,用于存储当前支持的音频接口的硬件设备信息(AudioHwDevice),针对每一个AudioHwDevice都有一个唯一的handle与其对应for (size_t i = 0; i < mAudioHwDevs.size(); i++) { //检查是不是已经添加过了if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {ALOGW("loadHwModule() module %s already loaded", name);return mAudioHwDevs.keyAt(i);}}audio_hw_device_t *dev;//!!!!重点,加载so库文件,从module信息中获取audio_hw_device_t --AudioHwDevice的成员之一int rc = load_audio_interface(name, &dev);if (rc) {ALOGI("loadHwModule() error %d loading module %s ", rc, name);return 0;}// Check and cache this HAL's level of support for master mute and master// volume.  If this is the first HAL opened, and it supports the get// methods, use the initial values provided by the HAL as the current// master mute and volume settings.AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);{  // scope for auto-lock patternAutoMutex lock(mHardwareLock);if (0 == mAudioHwDevs.size()) {mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;if (NULL != dev->get_master_volume) { float mv;if (OK == dev->get_master_volume(dev, &mv)) { //获取主音量mMasterVolume = mv;}}mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;if (NULL != dev->get_master_mute) {bool mm;if (OK == dev->get_master_mute(dev, &mm)) { //获取mutemMasterMute = mm;}}}mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;if ((NULL != dev->set_master_volume) &&(OK == dev->set_master_volume(dev, mMasterVolume))) { //设置主音量flags = static_cast<AudioHwDevice::Flags>(flags |AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);}mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;if ((NULL != dev->set_master_mute) &&(OK == dev->set_master_mute(dev, mMasterMute))) {flags = static_cast<AudioHwDevice::Flags>(flags |   //设置muteAudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);}mHardwareStatus = AUDIO_HW_IDLE;}audio_module_handle_t handle = nextUniqueId(); //创建handlemAudioHwDevs.add(handle, new AudioHwDevice(name, dev, flags)); //加入mAudioHwDevsreturn handle;
}

说一下AudioHwDevice,用于描述当前音频接口的名字,能否设置,masterVolume和mute,以及音频设备的通常操作方法,比如设置master/voice volume,获取音量、获取和设置mute、设置一些参数、打开输入和输出stream等等。
至此,已经加载完成所有的音频接口和支持的音频参数,可供AudioPolicyService提供音频输出策略。

为了能正常输出音频,我们还需要创建抽象音频接口对象,即AudioFlinger中openOutput实现的功能。
frameworks\av\services\audioflinger\AudioFlinger.cpp

audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
....
PlaybackThread *thread = NULL;// playbackthread线程
audio_stream_out_t *outStream = NULL; //创建stream out,控制输出流?
AudioHwDevice *outHwDev; //从mAudioHwDevs中获取当前audio接口的AudioHwDevice
...
outHwDev = findSuitableHwDev_l(module, *pDevices);//根据module id从mAudioHwDevs中获取当前outputs对应的AudioHwDevice
//每一个音频接口对应一个hwDevHal,描述和获取此音频接口的信息
audio_hw_device_t *hwDevHal = outHwDev->hwDevice();
audio_io_handle_t id = nextUniqueId();//回放线程的id,全局唯一
//此接口是编译成audio_xxx_default.so(例如audio_primary_default.so)的对应文件中(audio_hw.c)定义的对设备的操作函数集
status_t status = hwDevHal->open_output_stream(hwDevHal,//打开一个stream out,用于输出此设备上的音频数据流?id,*pDevices,(audio_output_flags_t)flags,&config,&outStream);
//
AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);//创建streamout,使用AudioStreamOut来封装音频输出流audio_stream_out_t//对于outputs中的每一一个output(IoProfile),都会创建对应的playbackthread
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {thread = new OffloadThread(this, output, id, *pDevices); //compress_offload,硬件解码ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||(config.format != AUDIO_FORMAT_PCM_16_BIT) ||(config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {thread = new DirectOutputThread(this, output, id, *pDevices); //不需要混音等操作,直接连接到一个track?在AudioPolicyManagerBase筛选outProfile时已经被过滤掉了,在AudioPolicyManagerBase构造函数中此thread不会被构建ALOGV("openOutput() created direct output: ID %d thread %p", id, thread);
} else {thread = new MixerThread(this, output, id, *pDevices); //mixer 支持 fast(低延迟)、deep_buffer、primaryALOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
}
mPlaybackThreads.add(id, thread);//添加到成员变量中进行管理,方便后续查找和使用

打开音频输出流过程其实就是创建AudioStreamOut对象及PlaybackThread线程过程。playbackthread就可以处理来自AudioTrack的音频数据流。
音频接口信息加载完成,playbackthread也创建好了,接下来就是等着音频播放的执行,但是等到音频数据来的时候怎么来使用这些设置进行音频链路的选择和播放呢?接下来就是第二部,如何获取播放策略。

2、如何获取播放策略

首先从测试程序开始分析,我们看到在播放音频前,需要获取buffer(关于buffer的获取,看AudioTrack分析的第二小节),构建AudioTrack(最终构建对应的track),在这个过程中会为此track获取播放策略、选择播放设备。

frameworks\base\media\tests\MediaFrameworkTest\src\com\android\mediaframeworktest\functional\audio\MediaAudioTrackTest.java
看一下音量测试程序的使用:

@LargeTestpublic void testSetStereoVolumeMax() throws Exception {// constants for testfinal String TEST_NAME = "testSetStereoVolumeMax";//设置音频格式、类型等信息final int TEST_SR = 22050;    final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;final int TEST_MODE = AudioTrack.MODE_STREAM;final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;//-------- initialization --------------int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);//获取缓冲区大小AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE);     //创建track,导致AudioTrack的构造函数被创建byte data[] = new byte[minBuffSize/2];//--------    test        --------------track.write(data, 0, data.length); //写数据track.write(data, 0, data.length);track.play(); //播放float maxVol = AudioTrack.getMaxVolume(); assertTrue(TEST_NAME, track.setStereoVolume(maxVol, maxVol) == AudioTrack.SUCCESS);//-------- tear down      --------------track.release(); //使用完成释放track}

AudioTrack(java)的构造函数:

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes, int mode, int sessionId)throws IllegalArgumentException {...//检查参数int initResult = native_setup(new WeakReference<AudioTrack>(this), //调用JNI层注册的函数,设置track信息mStreamType, mSampleRate, mChannels, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session);...

frameworks\base\core\jni\android_media_AudioTrack.cpp对应native_setup的函数:

static int
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,jint streamType, jint sampleRateInHertz, jint javaChannelMask,jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession){...//检查参数,分配内存,switch (memoryMode) { //根据数据的传输(stream)类型,设置track信息case MODE_STREAM:lpTrack->set(atStreamType,// stream typesampleRateInHertz,format,// word length, PCMnativeChannelMask,frameCount,AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack0,// shared mem  -- java层没有分配buffer,AudioFlinger动态分配true,// thread can call JavasessionId);// audio session IDbreak;case MODE_STATIC:...

继续看AudioTrack(cpp)set函数实现的功能:

...//获取和检查参数audio_io_handle_t output = AudioSystem::getOutput( //这里开始进入策略的获取和设备的选择streamType,sampleRate, format, channelMask,flags,offloadInfo);

//frameworks\av\media\libmedia\AudioSystem.cpp
audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream,uint32_t samplingRate,audio_format_t format,audio_channel_mask_t channelMask,audio_output_flags_t flags,const audio_offload_info_t *offloadInfo)
{const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); //通过binder获取AudioPolicyService的代理对象if (aps == 0) return 0;return aps->getOutput(stream, samplingRate, format, channelMask, flags, offloadInfo);//调用AudioPolicyService的getOutput
}//frameworks\av\services\audioflinger\AudioPolicyService.cpp
audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,uint32_t samplingRate,audio_format_t format,audio_channel_mask_t channelMask,audio_output_flags_t flags,const audio_offload_info_t *offloadInfo)
{if (mpAudioPolicy == NULL) {return 0;}ALOGV("getOutput()");Mutex::Autolock _l(mLock);return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, //mpAudioPolicy,是在拉起audiopolicyservice时由HAL层初始化的数据结构()format, channelMask, flags, offloadInfo);
}//hardware\libhardware_legacy\audio\audio_policy_hal.cpp
static int create_legacy_ap(const struct audio_policy_device *device,struct audio_policy_service_ops *aps_ops,void *service,struct audio_policy **ap) {//即mpAudioPolicystruct legacy_audio_policy *lap;...lap->policy.get_output = ap_get_output;//调用此函数...lap->apm = createAudioPolicyManager(lap->service_client);//此对象的创建最终实现audio\AudioPolicyManagerBase.cpp*ap = &lap->policy; //static audio_io_handle_t ap_get_output(struct audio_policy *pol,audio_stream_type_t stream,uint32_t sampling_rate,audio_format_t format,audio_channel_mask_t channelMask,audio_output_flags_t flags,const audio_offload_info_t *offloadInfo)
{struct legacy_audio_policy *lap = to_lap(pol); //由audio_policy 得到legacy_audio_policy ALOGV("%s: tid %d", __func__, gettid());return lap->apm->getOutput((AudioSystem::stream_type)stream, //由apm中的getOutput实现sampling_rate, (int) format, channelMask,(AudioSystem::output_flags)flags,offloadInfo);
}
//

从我们代码的分析来看,先是从java应用调用到JNI层,最后到HAL层,最终由音频策略管理者AudioPolicyManagerBase来实现音频链路和设备的选取。
AudioSystem::getOutput最终实现在hardware\libhardware_legacy\audio\AudioPolicyManagerBase.cpp

audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,uint32_t samplingRate,uint32_t format,uint32_t channelMask,AudioSystem::output_flags flags,const audio_offload_info_t *offloadInfo)
{audio_io_handle_t output = 0;uint32_t latency = 0;//重要!routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); //根据我们提供的音频类型(music)获取策略audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);//为策略获取对应的设备ALOGI("getOutput() device %d, stream %d, samplingRate %d, format %x, channelMask %x, flags %x",device, stream, samplingRate, format, channelMask, flags);//================打开direct output===================// open a direct output if required by specified parameters //force direct flag if offload flag is set: offloading implies a direct output stream// and all common behaviors are driven by checking only the direct flag// this should normally be set appropriately in the policy configuration fileif ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {flags = (AudioSystem::output_flags)(flags | AUDIO_OUTPUT_FLAG_DIRECT);}// Do not allow offloading if one non offloadable effect is enabled. This prevents from// creating an offloaded track and tearing it down immediately after start when audioflinger// detects there is an active non offloadable effect.   //注意此注释,重要--关于offlaod的创建时机// FIXME: We should check the audio session here but we do not have it in this context.// This may prevent offloading in rare situations where effects are left active by apps// in the background.IOProfile *profile = NULL;if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||!isNonOffloadableEffectEnabled()) {profile = getProfileForDirectOutput(device,samplingRate,format,channelMask,(audio_output_flags_t)flags);}if (profile != NULL) {AudioOutputDescriptor *outputDesc = NULL;for (size_t i = 0; i < mOutputs.size(); i++) {AudioOutputDescriptor *desc = mOutputs.valueAt(i);if (!desc->isDuplicated() && (profile == desc->mProfile)) {outputDesc = desc;// reuse direct output if currently open and configured with same parametersif ((samplingRate == outputDesc->mSamplingRate) &&(format == outputDesc->mFormat) &&(channelMask == outputDesc->mChannelMask)) {outputDesc->mDirectOpenCount++;ALOGI("getOutput() reusing direct output %d", mOutputs.keyAt(i));return mOutputs.keyAt(i);}}}// close direct output if currently open and configured with different parametersif (outputDesc != NULL) {closeOutput(outputDesc->mId);}outputDesc = new AudioOutputDescriptor(profile);outputDesc->mDevice = device;outputDesc->mSamplingRate = samplingRate;outputDesc->mFormat = (audio_format_t)format;outputDesc->mChannelMask = (audio_channel_mask_t)channelMask;outputDesc->mLatency = 0;outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);outputDesc->mRefCount[stream] = 0;outputDesc->mStopTime[stream] = 0;outputDesc->mDirectOpenCount = 1;output = mpClientInterface->openOutput(profile->mModule->mHandle, //用来打开direct和compress_offload接口,这两者都是使用的时候才会被打开&outputDesc->mDevice,&outputDesc->mSamplingRate,&outputDesc->mFormat,&outputDesc->mChannelMask,&outputDesc->mLatency,outputDesc->mFlags,offloadInfo);// only accept an output with the requested parametersif (output == 0 ||(samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||(format != 0 && format != outputDesc->mFormat) ||(channelMask != 0 && channelMask != outputDesc->mChannelMask)) {ALOGI("getOutput() failed opening direct output: output %d samplingRate %d %d,""format %d %d, channelMask %04x %04x", output, samplingRate,outputDesc->mSamplingRate, format, outputDesc->mFormat, channelMask,outputDesc->mChannelMask);if (output != 0) {mpClientInterface->closeOutput(output);}delete outputDesc;return 0;}audio_io_handle_t srcOutput = getOutputForEffect(); //为音效处理选取outputaddOutput(output, outputDesc);  //加入到mOutputs中,备选audio_io_handle_t dstOutput = getOutputForEffect();if (dstOutput == output) {mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput);}mPreviousOutputs = mOutputs;ALOGI("getOutput() returns new direct output %d", output);return output;}// ignoring channel mask due to downmix capability in mixer// open a non direct output ====== 为非direct output选择output// for non direct outputs, only PCM is supportedif (audio_is_linear_pcm((audio_format_t)format)) {// get which output is suitable for the specified stream. The actual// routing change will happen when startOutput() will be calledSortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs); //为设备选取Outputoutput = selectOutput(outputs, flags); //选取最合适的一路Output}ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,""format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);return output;
}

对于选取output的细节,我们j简要分析一下,其中getStrategy就是把音频类型分成大类,例如通知、多媒体、蓝牙、FM…这就是策略的选择,然后根据策略选择设备,从众多支持的输出设备(具体可以参考如上audio_policy.conf文件中的devices内容)中选择一个设备(如果没有合适的就选择默认的,一般是speaker)。
设备选好后就会选择对应的output,因为一个outputs中会有primary、fast、compress_offload等接口,这些接口可能会支持同一个设备,所以我们在为设备选择output时可能会有多个选项,但最终只能选择一个最合适的输出,选择策略如下:如果备选outputs没有合适的output,即没有合适输出,如果只有一个那就只能选这个,如果由多个,会按照flags和IOProfile中的devices flags进行位与,重合位数最多的就是最合适的,如果匹配的位数都为0,则选择支持primary的那路output。

总之,针对不同的平台和应用场景,选择输出策略和设备会有差异,其中方案商会参与到这些策略的制定。其遵照的规则就是,按照一定的使用场景把音频类型分类,决定优先级来,然后根据输入的音频类型并结合当前的工作模式(蓝牙、电话…)选择合适的一路output。

Android audio播放策略和设备的获取相关推荐

  1. Android Audio播放音频之数据传递

    AudioTrack之数据传递 简介 接上一篇AudioTrack播放音频之建立通道找到了通道的唯一句柄值output,这个output实质就是在AudioFlinger创建PlaybackThrea ...

  2. Android Audio播放流程详解

    本文涉及的所有代码都是AOSP源码 目录 1. AudioTrack 2.创建AudioTrack对象 1. AudioTrack AudioTrack用于播放PCM流格式的音频数据.播放器会在fra ...

  3. EasyPlayer Android RTSP播放器延迟再优化策略

    EasyPlayer延迟再优化策略 EasyPlayer是一款专门针对RTSP协议进行过优化的播放器.其中两个我们引以为傲的的优点就是起播快和低延迟.最近我们遇到一些需求,其对延迟要求非常苛刻,于是我 ...

  4. Android开发之Audio播放:竞争Audio之Audio Focus的应用

    Android是多任务系统,Audio系统是竞争资源.Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对Audio资源的竞争 ...

  5. 【Android 逆向】修改 Android 系统文件 ( Android 逆向中需要经常修改的文件和目录 | 在 root 后的设备中获取 / 目录的 rw 权限后注意事项 )

    文章目录 一.Android 逆向中需要经常修改的文件和目录 二.在 root 后的设备中获取 / 目录的 rw 权限后注意事项 1.不要随意执行 wipe 命令 2.不要随意执行 rm 命令 一.A ...

  6. takephoto 框架_GitHub - Smecking/TakePhoto: 一款用于在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库...

    TakePhoto是一款用于在Android设备上获取照片(拍照或从相册.文件中选择).裁剪图片.压缩图片的开源工具库,目前最新版本4.0.2. 3.0以下版本及API说明,详见TakePhoto2. ...

  7. Android audio切换设备通道流程(二十八)

    Android audio切换设备通道流程 1.frameworks/base/media/java/android/media/AudioManager.java public void setMo ...

  8. android html5播放器,用 HTML5 播放器在 iOS 或 Android 等移动设备上播放视频

    如果你想把自己网站上的视频弄得能在 iOS 或者 Android 设备上播放,你需要想两个事儿,一个是你网站上的视频播放器要支持 HTML5,还有你的视频得支持在这些设备上看. 网站的视频播放器我们可 ...

  9. Android 手机设备信息获取使用详解

    Android 手机是我们常用的工具之一,买手机之前,手机厂商会提供一些手机参数给我们,那么问题来了,我们该如何获取手机上的参数信息呢? 通过本文你讲了解到获取手机常用信息的基本方法. 获取手机基本信 ...

最新文章

  1. linux查询首字符不是T,linux – tload输出中的不同字符是什么意思?
  2. testing: mock object
  3. RabbitMQ工作队列
  4. openresty配置部署
  5. Vue.js(17)之 插槽
  6. 字符,字符串,int之间互相转换
  7. javascript中编码与解码的decodeURI()、decodeURIComponent()区别
  8. Python爬虫入门五URLError异常处理
  9. 使用JavaScript的图像识别游戏
  10. Differential Geometry之第九章常平均曲率曲面
  11. FIREDAC用于LINUX报头文件FireDAC.VCLUI.Wait找不到
  12. AbstractQueuedSynchronizer 源码分析(共享锁)
  13. 为什么软件预算经常会改变和膨胀
  14. 违章查询源码 php,PHP教程:php车辆违章查询数据示例
  15. 加密对冲基金究竟靠谱吗?全球第一份行业报告揭开秘密
  16. mybatis简明教程
  17. Markdown基础语法详细版
  18. 解析G652,G657A,G655和G654光缆之间的区别
  19. linux--Repo的介绍与安装
  20. Bilibili网站后台代码泄露,从注释看到的一些感想。

热门文章

  1. 新的十月(2022.10.15)
  2. excel vba 批量发送邮件邮件内容放入表格指定主题
  3. python实现树莓派监控_用树莓派实现室内温度监控
  4. 基于cocoStudio和BMfont的艺术字体制作
  5. 关于putty不能以root身份登录的解决方案
  6. 婴儿尿布更换用品F2388 – 18 标准(家用婴儿尿布更换用品标准消费者安全规范)安规检测
  7. ASP.NET入门随想六之大航海家(2)
  8. JsonIgnore导致不能接收参数
  9. 天纵智能软件快速开发一般统计分析插件
  10. iOS 微信右上角下拉菜单效果之CMPopTipView,各种角度各种位置