前言:
最近做安卓白板的项目,最主要是研究ARC/CEC方面相关的问题,这个过程中也遇到了一些ARC/CEC方面相关的问题,一方面是关于ARC/CEC连接以及遥控器同步的问题,一方面是ARC功放设备音量调节的问题,现在就2个比较经典的问题拿出来分析一下,后续会继续更新关于ARC/CEC相关的问题解析。
问题描述:
1、安卓设备本身speaker音量值会影响通过ARC连接的功放设备音量大小问题,测试发现调节安卓设备本身音量到很小的时候,接入ARC功放设备,这时候功放设备的声音也会很小,断开ARC设备,调节安卓设备本身音量到最大,再接入ARC,这时候功放设备的声音也会很大。
分析思路:
1、分析安卓上层,frameworks设置设备音量的流程,分析是不是把SPEAKER设备或者其他设备的音量值赋给了ARC功放设备。
首先看一下安卓设备本身通过音量条设置设备音量的流程
音量设置总流程
音量设置总流程如下:
![[音量设置流程.png]]
App在设置音量时,通常使用AudioManager的以下两个接口:
setStreamVolume(int streamType, int index, int flags)
index:音量等级,通常在0~100这个设置范围,音量可以突变设置,如上次音量为1,下次设置音量为5
adjustStreamVolume(int streamType, int direction, int flags)
direction:音量调整方向ADJUST_LOWER,ADJUST_RAISE,ADJUST_SAME,类似于每次只加/减一,匀速调整
通过菜单栏音量条调节音量一般用的是setStreamVolume()这个接口,而通过遥控器调节音量一般使用adjustStreamVolume()这个接口,接下来我们分析一下setStreamVolume()这个接口从应用层到framework层设置音量的流程
base/services/core/java/com/android/server/audio/AudioService.java
`private void setStreamVolume(int streamType, int index, int flags,`
`@Nullable AudioDeviceAttributes ada,`
`String callingPackage, String caller, String attributionTag, int uid,`
`boolean hasModifyAudioSettings) {`
......
//`streamTypeAlias`: 获取音频流的别名,用于内部处理
int streamTypeAlias = mStreamVolumeAlias[streamType];
//VolumeStreamState是AudioService内部的音量状态管理类,以streamType为单位,每一种音频流,都会创建一个VolumeStreamState内部对象,App应用层设置某个streamType的音量时,就会调用它对应的VolumeStreamState对象来处理音量
//`streamState`: 获取对应音频流的状态对象,用于后续音量调整
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
//获取音频流对应的设备
int device = (ada == null) ? getDeviceForStream(streamType) : ada.getInternalType();
......
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
......
//执行音量调整
onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
......
//发送音量更新通知,通知层音量更新
> [!代码1]
> sendVolumeUpdate(streamType, oldIndex, index, flags, device);
`}`
base/services/core/java/com/android/server/audio/AudioService.java
//代码1
`private void onSetStreamVolume(int streamType, int index, int flags, int device,`
`String caller, boolean hasModifyAudioSettings) {`
`final int stream = mStreamVolumeAlias[streamType];`
> [!代码2]
> `setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);`
.........
`}`
base/services/core/java/com/android/server/audio/AudioService.java
代码2
`private void setStreamVolumeInt(int streamType,`
`int index,`
`int device,`
`boolean force,`
`String caller, boolean hasModifyAudioSettings) {`
`/*if (isFullVolumeDevice(device)) {`
`return; }*/ VolumeStreamState streamState = mStreamStates[streamType];`
if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
// Post message to set system volume (it in turn will post a message
// to persist).
> [!代码3]
> sendMsg(mAudioHandler,
> MSG_SET_DEVICE_VOLUME,
> SENDMSG_QUEUE,
> device,
> 0,
> streamState,
> 0);
}
`}`
base/services/core/java/com/android/server/audio/AudioService.java
代码3
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
> [!代码4]
> setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
break;
}
base/services/core/java/com/android/server/audio/AudioService.java
代码4
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
// Apply volume
//应用音量到指定设备
> [!代码5]
> streamState.applyDeviceVolume_syncVSS(device);
>
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != streamState.mStreamType &&
mStreamVolumeAlias[streamType] == streamState.mStreamType) {
// Make sure volume is also maxed out on A2DP device for aliased stream
// that may have a different device selected int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice)
&& (isAbsoluteVolumeDevice(device)
|| isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device))) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
}
}
}
// Post a persist volume msg
//保存声音的状态到系统属性当中,安卓设备重启时通过属性获取各音频流的状态
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
PERSIST_DELAY);
}
base/services/core/java/com/android/server/audio/AudioService.java
代码5
/*package*/ void applyDeviceVolume_syncVSS(int device) {
int index;
if (isFullyMuted()) {
index = 0;
} else if (isAbsoluteVolumeDevice(device)
|| isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device)) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
//获取设备音量值,当设备为ARC设备时音量值总是最大值。
index = (mIndexMax + 5)/10;
} else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
index = (mIndexMax + 5)/10;
} else {
index = (getIndex(device) + 5)/10;
}
if (isTablet() && isSyncAjustVolumeDevice(device) && mStreamType == AudioSystem.STREAM_MUSIC) {
for (Integer item : mSyncAjustVolumeDevices) {
index = (getIndex(item) + 5)/10;
setStreamVolumeIndex(index, item);
}
} else {
> [!代码6]
> setStreamVolumeIndex(index, device);
}
}
base/services/core/java/com/android/server/audio/AudioService.java
代码6
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when // index is just set to 0 to repect BT requirements if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
&& !isFullyMuted()) {
index = 1;
}
Log.d(TAG,"zfp#setStreamVolumeIndex#index = " + index + "device = " + device + "mStreamType = " + mStreamType);
//java层真正设置音量的地方,最后调用 setStreamVolumeIndexAS向下设置音量
//重点!重点!重点!
> [!代码7/核心代码]
> mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
AudioService章节总结
1)利用设置的音频流类型streamType找到对应的音量状态管理类VolumeStreamState,此类集合的音量管理的核心方法,内部有一个map集合保存了所有输出设备及其对应的音量等级index,最终也是调整这个index
2)同时使用streamType根据strategy找到对应的输出设备,为此设备device设置index
3)一旦某个VolumeStreamState音量做出了调整,则需要把别名相同的另一个VolumeStreamState也要进行音量调整
4)其他就是要处理RingMode、fix固定音量和safe安全音量的问题
5)音量等级设置好后,就是向下调用setStreamVolumeIndex方法,进行音量等级到分贝的处理
接下来我们追一下setStreamVolumeIndex后的流程
//在VolumeStreamState 的setStreamVolumeIndex调用AudioSystem的setStreamVolumeIndexAS,开始真正地向下设置音量
上面AudioService最后向下调用setStreamVolumeIndex方法传递的重要参数是:
1)输出设备device; 2)音量等级index;3) 音频流StreamType
//frameworks/base/service/java/com/android/server/audio/AudioSystem.java
代码7
public class AudioSystem{
......
/** Wrapper for native methods called from AudioService */
public static int setStreamVolumeIndexAS(int stream, int index, int device) {
if (DEBUG_VOLUME) {
Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
+ " dev=" + Integer.toHexString(device) + " idx=" + index);
}
> [!代码8]
> return setStreamVolumeIndex(stream, index, device);
}
......
}
//在AudioSystem的setStreamVolumeIndexAS中调用setStreamVolumeIndex方法,setStreamVolumeIndex方法是一个native方法,通过JNI调用AudioSystem native的setStreamVolumeIndex函数:
// frameworks/base/core/jni/android_media_AudioSystem.cpp
代码8
static jint
android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
jobject thiz,
jint stream,
jint index,
jint device)
{
return (jint) check_AudioSystem_Command(
> [!代码9]
> AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
> index,
> (audio_devices_t)device));
}
//在JNI代码中调用AudioSystem的setStreamVolumeIndex函数:
//frameworks/av/media/libaudioclient/AudioSystem.cpp
代码9
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
> [!代码10]
> return aps->setStreamVolumeIndex(stream, index, device);
}
//此处创建了一个AudioPolicyService对象,并调用了该对象的setStreamVolume方法。
AudioPolicyService调用了BnAudioPolicyService::onTransact后回去调用自己的setStreamVolumeIndex方法。而setStreamVolumeIndex的实现在AudioPolicyInterfaceImpl.cpp中。
//frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
代码10
status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
> [!代码11]
> return mAudioPolicyManager->setStreamVolumeIndex(stream,
> index,
> device);
}
//这里调用mAudioPolicyManager->setStreamVolumeIndex(stream, index,device);,AudioPolicyManager下的setStreamVolumeIndex定义在AudioPolicyManager.cpp中:
//这块代码主要是将streamType转换到他对应的属性attribute,然后调用setVolumeIndexForAttributes函数继续往下设置
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
代码11
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
auto attributes = mEngine->getAttributesForStreamType(stream);
ALOGV("%s: stream %s attributes=%s", __func__,
toString(stream).c_str(), toString(attributes).c_str());
> [!代码12]
> return setVolumeIndexForAttributes(attributes, index, device);
}
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
代码12
status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attributes,
int index,
audio_devices_t device)
{
// Get Volume group matching the Audio Attributes
auto group = mEngine->getVolumeGroupForAttributes(attributes);
if (group == VOLUME_GROUP_NONE) {
ALOGD("%s: no group matching with %s", __FUNCTION__, toString(attributes).c_str());
return BAD_VALUE;
}
ALOGV("%s: group %d matching with %s", __FUNCTION__, group, toString(attributes).c_str());
status_t status = NO_ERROR;
//从attribute获取与之对应的volumeGroup,在从volumeGroup中获取到VolumeCurves
IVolumeCurves &curves = getVolumeCurves(attributes);
VolumeSource vs = toVolumeSource(group);
product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes);
//将当前的音量等级设置到volumeCurves的mIndexCur<device,index>成员中去
status = setVolumeCurveIndex(index, device, curves);
if (status != NO_ERROR) {
ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device);
return status;
}
DeviceTypeSet curSrcDevices;
auto curCurvAttrs = curves.getAttributes();
//取attrs的首节点,并且根据attribute获取当前系统最新的一个输出设备curSrcDevice
if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) {
auto attr = curCurvAttrs.front();
//获取attr适合的device
curSrcDevices = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types();
} else if (!curves.getStreamTypes().empty()) {
auto stream = curves.getStreamTypes().front();
curSrcDevices = mEngine->getOutputDevicesForStream(stream, false).types();
} else {
ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, vs);
return BAD_VALUE;
}
//多个设备的情况下,选择一个
audio_devices_t curSrcDevice = Volume::getDeviceForVolume(curSrcDevices);
resetDeviceTypes(curSrcDevices, curSrcDevice);
// update volume on all outputs and streams matching the following:
// - The requested stream (or a stream matching for volume control) is active on the output
// - The device (or devices) selected by the engine for this stream includes
// the requested device // - For non default requested device, currently selected device on the output is either the
// requested device or one of the devices selected by the engine for this stream
// - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
// no specific device volume value exists for currently selected device.
//遍历每一个输出通道
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
//打开的设备
DeviceTypeSet curDevices = desc->devices().types();
if (curDevices.erase(AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
curDevices.insert(AUDIO_DEVICE_OUT_SPEAKER);
}
if (!(desc->isActive(vs) || isInCall())) {
continue;
}
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME &&
curDevices.find(device) == curDevices.end()) {
continue;
}
bool applyVolume = false;
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curSrcDevices.insert(device);
/*[Amlogic start]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Change-Id: Ie0564b020e0918c084936d72b34fa20a168f8fa5 */
if (curSrcDevices.find(Volume::getDeviceForVolume(curDevices)) == curSrcDevices.end()) {
applyVolume = false;
} else {
// When multiple output devices on Outputs, We only allow high priority device volume to be set to stream.
if (device != Volume::getDeviceForVolume(curDevices) && curSrcDevices.size() > 1) {
applyVolume = false;
} else {
applyVolume = true;
}
}
/*[Amlogic end]-----------------------------------------------------------*/
} else {
applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
}
if (!applyVolume) {
continue; // next output
}
// Inter / intra volume group priority management: Loop on strategies arranged by priority
// If a higher priority strategy is active, and the output is routed to a device with a
// HW Gain management, do not change the volume
//此设备能进行gain增益控制,检查desc的openDevice的device,查看device的配置标签gain对应的AudioGains是否支持音量调节
if (desc->useHwGain()) {
applyVolume = false;
// If the volume source is active with higher priority source, ensure at least Sw Muted
desc->setSwMute((index == 0), vs, curves.getStreamTypes(), curDevices, 0 /*delayMs*/);
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy,
false /*preferredDevice*/);
//无存活客户端,不做音量调整
if (activeClients.empty()) {
continue;
}
bool isPreempted = false;
bool isHigherPriority = productStrategy < strategy;
for (const auto &client : activeClients) {
if (isHigherPriority && (client->volumeSource() != vs)) {
ALOGV("%s: Strategy=%d (\nrequester:\n"
" group %d, volumeGroup=%d attributes=%s)\n"
" higher priority source active:\n"
" volumeGroup=%d attributes=%s) \n"
" on output %zu, bailing out", __func__, productStrategy,
group, group, toString(attributes).c_str(),
client->volumeSource(), toString(client->attributes()).c_str(), i);
applyVolume = false;
isPreempted = true;
break;
}
// However, continue for loop to ensure no higher prio clients running on output
//client有一个符合音量曲线的设备
if (client->volumeSource() == vs) {
applyVolume = true;
}
}
if (isPreempted || applyVolume) {
break;
}
}
if (!applyVolume) {
continue; // next output
}
}
//FIXME: workaround for truncated touch sounds
// delayed volume change for system stream to be removed when the problem is
// handled by system UI
//最终会把index由音量等级转化为分贝db,把分贝值设置到AudioutputDescriptor的VolumeActivities成员变量中去
> [!代码13]
> status_t volStatus = checkAndSetVolume(
> curves, vs, index, desc, curDevices,
> ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM, false))?
> TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/);
return status;
}
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
代码13
status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index,
const sp<AudioOutputDescriptor>& outputDesc,
DeviceTypeSet deviceTypes,
int delayMs,
bool force)
{
// do not change actual attributes volume if the attributes is muted
if (outputDesc->isMuted(volumeSource)) {
ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource,
outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
return NO_ERROR;
}
VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL, false);
VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO, false);
bool isVoiceVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource);
bool isBtScoVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (btScoVolSrc == volumeSource);
bool isScoRequested = isScoRequestedForComm();
bool isHAUsed = isHearingAidUsedForComm();
// do not change in call volume if bluetooth is connected and vice versa
// if sco and call follow same curves, bypass forceUseForComm
if ((callVolSrc != btScoVolSrc) &&
((isVoiceVolSrc && isScoRequested) ||
(isBtScoVolSrc && !(isScoRequested || isHAUsed)))) {
ALOGV("%s cannot set volume group %d volume when is%srequested for comm", __func__,
volumeSource, isScoRequested ? " " : " not ");
// Do not return an error here as AudioService will always set both voice call
// and bluetooth SCO volumes due to stream aliasing.
return NO_ERROR;
}
if (deviceTypes.empty()) {
deviceTypes = outputDesc->devices().types();
}
/*[Amlogic start]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Change-Id: I833bfe491dbe47d617e2e2670d1ae928429705bd */
/* 1. The initStreamVolume function of AudioService has not been called,
* which will cause the volume setting failure and silent playback.
* 2. So we don't need to check the validity of the volume when bootvideo playing.
*/
//AudioService的initStreamVolume函数尚未调用,这将导致音量设置失败和静音播放。因此,在播放引导视频时,我们不需要检查音量的有效性
bool bootVideoPlaying = property_get_int32("service.bootvideo.exit", 0) == 1;
if ((curves.getVolumeIndexMin() < 0 || curves.getVolumeIndexMax() < 0) && !bootVideoPlaying) {
ALOGE("invalid volume index range");
return BAD_VALUE;
}
/*[Amlogic end]-----------------------------------------------------------*/
//计算音量值dB,device转化为device_category,根据它在找到curve,传入index获取其分贝值
float volumeDb = computeVolume(curves, volumeSource, index, deviceTypes);
if (outputDesc->isFixedVolume(deviceTypes) ||
// Force VoIP volume to max for bluetooth SCO device except if muted
(index != 0 && (isVoiceVolSrc || isBtScoVolSrc) &&
isSingleDeviceType(deviceTypes, audio_is_bluetooth_out_sco_device))) {
volumeDb = 0.0f;
}
/*[Amlogic start]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Change-Id: Ia4120848f02c700d9b03a48e0b7122415eb63799 */
/* Need adjust audio hal volume when television platform. */
//当ro.vendor.platform.has.tvuimode属性为true也就是平台为TV时,会引用Amlogic写的方法来调整音量
if (property_get_bool("ro.vendor.platform.has.tvuimode", false /* default_value */)) {
DeviceTypeSet curSrcDevicesVector = mEngine->getOutputDevicesForStream(AUDIO_STREAM_MUSIC, false).types();
audio_devices_t curDevice = Volume::getDeviceForVolume(curSrcDevicesVector);
DeviceTypeSet curDeviceVector {curDevice};
audio_devices_t outputDescDevices = deviceTypesToBitMask(outputDesc->devices().types());
bool bootVideoRunning = property_get_int32("service.bootvideo.exit", 0) == 1;
if ((curDevice & outputDescDevices) != 0 && (
curDevice == AUDIO_DEVICE_OUT_SPEAKER ||
curDevice == AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
curDevice == AUDIO_DEVICE_OUT_SPDIF)) {
//ignoring the "index" passed as argument and always use MUSIC stream index
//for all stream types works on TV because all stream types are aliases of MUSIC.
device_category devCategory = Volume::getDeviceCategory(curDeviceVector);
auto &volCurves = getVolumeCurves(AUDIO_STREAM_MUSIC);
int volumeIndex = volCurves.getVolumeIndex(curDeviceVector);
int volumeMaxIndex = volCurves.getVolumeIndexMax();
int volumeMinIndex = volCurves.getVolumeIndexMin();
float musicVolumeDb = volCurves.volIndexToDb(devCategory, volumeIndex);
float maxMusicVolumeDb = volCurves.volIndexToDb(devCategory, volumeMaxIndex);
float minMusicVolumeDb = volCurves.volIndexToDb(devCategory, volumeMinIndex);
ALOGV("[%s:%d] volume:%d, min:%d, max:%d, curDevice:%#x, devCategory:%d, outputDescDevices:%#x",
__func__, __LINE__, volumeIndex, volumeMinIndex, volumeMaxIndex, curDevice, devCategory, outputDescDevices);
ALOGV("[%s:%d] musicVolumeDb:%f, minMusicVolumeDb:%f, maxMusicVolumeDb:%f, bootVideoRunning:%d",
__func__, __LINE__, musicVolumeDb, minMusicVolumeDb, maxMusicVolumeDb, bootVideoRunning);
if (bootVideoRunning) {
maxMusicVolumeDb = 0.0f;
minMusicVolumeDb = -10000.0f;
musicVolumeDb = -1837.0f;
}
//当前输出设备为SPEAKER或者HEADPHONE或者SPDIF设备时会使用SwAudioOutputDescriptor的updateGain方法来调整设备音量
// volumeDb = 0.0f,也就是volumeDb的值一直设置为最大值,使用SwAudioOutputDescriptor的setVolume方法设置的音量保持最大不变
outputDesc->updateGain(curDevice, musicVolumeDb, minMusicVolumeDb, maxMusicVolumeDb);
volumeDb = 0.0f;
} else if (curDevice == AUDIO_DEVICE_OUT_HDMI_ARC) {
//当前设备为ARC设备时分贝值保持最大不变
volumeDb = 0.0f; //100
}
} else {
VolumeSource musicVolSrc = toVolumeSource(AUDIO_STREAM_MUSIC, false);
bool bootVideoRunning = property_get_int32("service.bootvideo.exit", 0) == 1;
ALOGV("[%s:%d] bootVideoRunning:%d, music:%d", __func__, __LINE__, bootVideoRunning, (musicVolSrc == volumeSource));
if (bootVideoRunning && musicVolSrc == volumeSource) {
volumeDb = -18.37f;
}
}
/*[Amlogic end]-----------------------------------------------------------*/
const bool muted = (index == 0) && (volumeDb != 0.0f);
//AudioOutputDescriptor 在 android 中有两个实现,分别是 SwAudioOutputDescriptor 和 HwAudioOutputDescriptor,音量设置的执行来到 SwAudioOutputDescriptor::setVolume():
//添加打印获取设备类型与其对应的分贝值,volumeDb为0.0f时对应的设备音量值为100
ALOGV("volumeDb = %f,muted = %d,volumeSource = %s,deviceTypes = %s",volumeDb, muted, volumeSource, dumpDeviceTypes(deviceTypes).c_str());
> [!代码14]
> outputDesc->setVolume(
> volumeDb, muted, volumeSource, curves.getStreamTypes(), deviceTypes, delayMs, force);
if (outputDesc == mPrimaryOutput && (isVoiceVolSrc || isBtScoVolSrc)) {
float voiceVolume;
// Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
if (isVoiceVolSrc) {
voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
} else {
voiceVolume = index == 0 ? 0.0 : 1.0;
}
if (voiceVolume != mLastVoiceVolume) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
}
return NO_ERROR;
}
以上代码主要完成两个任务:
- 将音量等级index转换为分贝db,computeVolume函数
- 将分贝值写入到输出设备outputDesc中去,如果是通话音量的话voice话,则调用setVoiceVolume直通hal层去调整音量
AudioPolicyManager小结
此部分作用主要是:
1、根据device、streamType找到attribute、strategy、volumeCurves音量曲线等
2、寻找哪些通道需要进行音量调整,主要是根据attribute、客户端client对应的volumeGroup是否一致,以及AudioOutputDescriptor的device和上层传入下来的device是否相同
3、找到AudioOutputDescriptor需要调整音量时,然后利用attribute找到的VolumeCurves、volumePoints等,利用查表和等比例方法查到其对应的分贝值
//frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
代码14
bool SwAudioOutputDescriptor::setVolume(float volumeDb,
VolumeSource vs, const StreamTypeVector &streamTypes,
audio_devices_t device,
uint32_t delayMs,
bool force)
{
StreamTypeVector streams = streamTypes;
if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, device, delayMs, force)) {
return false;
}
if (streams.empty()) {
streams.push_back(AUDIO_STREAM_MUSIC);
}
for (const auto& devicePort : devices()) {
// APM loops on all group, so filter on active group to set the port gain,
// let the other groups set the stream volume as per legacy
// TODO: Pass in the device address and check against it.
// 设备相等,且支持gain硬件调整音量的去设置
if (device == devicePort->type() &&
devicePort->hasGainController(true) && isActive(vs)) {
ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str());
// @todo: here we might be in trouble if the SwOutput has several active clients with
// different Volume Source (or if we allow several curves within same volume group)
//
// @todo: default stream volume to max (0) when using HW Port gain?
//将0dB转换为功率值
float volumeAmpl = Volume::DbToAmpl(0);
//为此类型的软件音量值设置0就是不发声,
for (const auto &stream : streams) {
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
//硬件音量更新
AudioGains gains = devicePort->getGains();
int gainMinValueInMb = gains[0]->getMinValueInMb();
int gainMaxValueInMb = gains[0]->getMaxValueInMb();
int gainStepValueInMb = gains[0]->getStepValueInMb();
int gainValueMb = ((volumeDb * 100)/ gainStepValueInMb) * gainStepValueInMb;
gainValueMb = std::max(gainMinValueInMb, std::min(gainValueMb, gainMaxValueInMb));
audio_port_config config = {};
devicePort->toAudioPortConfig(&config);
config.config_mask = AUDIO_PORT_CONFIG_GAIN;
config.gain.values[0] = gainValueMb;
//硬件音量设置
return mClientInterface->setAudioPortConfig(&config, 0) == NO_ERROR;
}
}
//上述走过硬件音量后,下面的都是软件音量,获取当前音量并转换为功率值ampl
// Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled
float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs));
if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) {
mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs);
}
//设置功率值
for (const auto &stream : streams) {
ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__,
mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str());
> [!代码15]
> mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
return true;
}
1、首先将音量值分贝Db设置到volumeActivities成员中,然后遍历当前打开的device
2、如果这个device支持gain硬件方式设置音量,就使用硬件音量调整setAudioPortConfig,此方法会调用到hal的set_audio_port_config指针函数;否则就是软件音量调整设置setStreamVolume
3、在进行第2步时,需要先把Db分贝转换为功率值ampl
//SwAudioOutputDescriptor 又通过 AudioPolicyClientInterface 设置音量;AudioPolicyClientInterface 的实现为 AudioPolicyService::AudioPolicyClient,设置音量的动作经 AudioPolicyService::AudioPolicyClient 转回 AudioPolicyService,只是这次调用的是 AudioPolicyService::setStreamVolume();
//frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
代码15
int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,float volume,
audio_io_handle_t output,int delayMs)
{
return (int)mAudioCommandThread->volumeCommand(stream, volume,output, delayMs);
}
status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
float volume,audio_io_handle_t output,int delayMs)
{
...(封装了一下data跟command)
return sendCommand(command, delayMs);
}
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs) {...(一些命令队列的操作)}
// 处理函数
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
...
while (!exitPending())
{
...
switch (command->mCommand) {
...
case SET_VOLUME:
ALOGV("AudioCommandThread() processing set volume stream %d, \
volume %f, output %d", data->mStream, data->mVolume, data->mIO);
...(Lock)
VolumeData *data = (VolumeData *)command->mParam.get();
> [!代码16]
> command->mStatus = AudioSystem::setStreamVolume(data->mStream,
> data->mVolume,data->mIO);
break;
...
}
}
//AudioPolicyService异步执行这个操作,最后会转到AudioSystem的setStreamVolume:
//frameworks/av/media/libaudioclient/AudioSystem.cpp
代码16
status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
> [!代码17]
> af->setStreamVolume(stream, value, output);
return NO_ERROR;
}
//在AudioSystem的setStreamVolume中会调用AudioFlinger的setStreamVolume:
//frameworks/base/media/java/android/media/audioflinger/AudioFlinger.cpp
代码17
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
status_t status = checkStreamType(stream);
if (status != NO_ERROR) {
return status;
}
if (output == AUDIO_IO_HANDLE_NONE) {
return BAD_VALUE;
}
LOG_ALWAYS_FATAL_IF(stream == AUDIO_STREAM_PATCH && value != 1.0f,
"AUDIO_STREAM_PATCH must have full scale volume");
AutoMutex lock(mLock);
//从mPlaybackThreads集合中拿到一个回播线程实例
VolumeInterface *volumeInterface = getVolumeInterface_l(output);
if (volumeInterface == NULL) {
return BAD_VALUE;
}
//设置音量对应功率值到playbackthread中的stream对应的音量值去
> [!代码18]
> volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
//AudioFlinger会去获取output对应的PlaybackThread并设置PlaybackThread的音量,如果output == AUDIOIOHANDLE_NONE,则设置所有PlaybackThread的音量。
PlaybackThread设置mStreamTypes的volume。并唤醒PlaybackThread线程
//frameworks/base/media/java/android/media/audioflinger/Threads.cpp
代码18
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].volume = value;
broadcast_l();
}
回播线程按照streamType类型集合保存了音量值,准确说应该是音量值对应的功率值,在播放音频或混音时,回播线程会将音频数据与音量数据相乘,最后将结果传送到Hal去播放,至此,软件音量调节过程就完结了!
现在回到开头的问题,ARC功放设备的音量为什么会受speaker设备的影响,首先排查一下java层ARC设备的音量设置是否正常。
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when // index is just set to 0 to repect BT requirements if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
&& !isFullyMuted()) {
index = 1;
}
Log.d(TAG,"zfp#setStreamVolumeIndex#index = " + index + "device = " + device + "mStreamType = " + mStreamType);
//java层真正设置音量的地方,最后调用 setStreamVolumeIndexAS向下设置音量
mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
java层真正设置音量的地方就是这里,这时候我们只需要添加打印,查看ARC设备对应的stream_music音频流对应的音量值为多少。ARC设备的值定义在AudioSystem.java中
public static final int DEVICE_OUT_HDMI_ARC = 0x40000; (转为十进制:262144)
public static final int DEVICE_OUT_HDMI_EARC = 0x40001; (转为十进制:262145)
public static final int DEVICE_OUT_SPEAKER = 0x2;
查看打印,如果java层ARC设备的音量值没有问题,我们需要再去追一下native层,音量设置是否有问题,添加打印查看ARC设备的音量,native层最主要排查的是「代码13」AudioPolicyManager.cpp的checkAndSetVolume接口。
如果这时候排查出ARC设备的音量值没什么问题,基本上可以确定是底层的策略导致的,这时候要找供应商反馈该问题。
https://devops.ktc.cn/project/ELMO_W71B_A311D2_Android13_IFPD/517 【中性问题】修复保持arc设备的音量不变,接入前大屏的音量会影响接入后的音量问题
A311D2安卓13这个问题就是供应商的策略导致的,最后是供应商通过修改hardware/amlogic/audio/audio_hal/audio_hw.c才恢复正常
第二个问题:通过菜单栏音量条无法调节ARC功放设备音量问题
首先还是查看,Java层音量设置的核心方法
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when // index is just set to 0 to repect BT requirements if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
&& !isFullyMuted()) {
index = 1;
}
、、100
Log.d(TAG,"zfp#setStreamVolumeIndex#index = " + index + "device = " + device + "mStreamType = " + mStreamType);
//java层真正设置音量的地方,最后调用 setStreamVolumeIndexAS向下设置音量
mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
查看ARC设备对应的stream_music音频流对应的音量值发现,通过菜单栏调节设备音量,ARC设备的音量值一直都是100,这时候往上追一下。
1)首先「代码2」那里,if (isFullVolumeDevice(device)) {
return; }当设备为满音量设备时直接return了,不会再去设置音量,而ARC设备正在满音量设备集合中
// Devices for which the volume is always max, no volume panel
Set mFullVolumeDevices = new HashSet<>(Arrays.asList(
AudioSystem.DEVICE_OUT_HDMI_ARC,
AudioSystem.DEVICE_OUT_HDMI_EARC
));
2)在「代码5」中,当设备为满音量设备时,音量值一直赋予最大值,所以我们打印得到的index值一直为100
解决完java层的影响后,这时java层传人native层的音量值正是我们菜单栏调节的音量值。如果这时候ARC功放设备的声音大小还是没有变化,这时候只能往下排查了,所以setStreamVolumeIndexAS方法是java层真正设置音量的地方,也是排查问题是java层引起还是native层引起的分界线。
native层,最主要排查的还是「代码13」AudioPolicyManager.cpp的checkAndSetVolume接口,查看打印,查看ARC设备对应的volumeDb也就是分贝值是否会随菜单栏音量的调节而改变。查看打印发现ARC设备对应的volumeDb一直为0.0f也就是最大值,这时候往上排查。
发现是Amlogic添加了对策,当设备为ARC功放设备为,volumeDb一直为0.0f
排除了以上的影响,如果菜单栏调节ARC功放设备的声音还是没有作用,这时候可能是底层或者供应商的问题,需要找他们排查一下。
接下来看一下,ARC设备静音和遥控器调节ARC设备音量的问题
1、https://devops.ktc.cn/project/SKG_W82B_RK3588_AN13_K81A_IFPD/24 【ARC】大屏控制静音/取消静音,音量条和功放声音受控不同步
2、https://devops.ktc.cn/project/SKG_W82B_RK3588_AN13_K81A_IFPD/22 【ARC】遥控调节大屏音量,ARC功放声音不受控