ARC1.6-Amlogic安卓设备本身speaker音量值会影响通过ARC连接的功放设备音量以及通过菜单栏音量条无法调节ARC功放设备音量问题分析

前言:

最近做安卓白板的项目,最主要是研究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;
}

以上代码主要完成两个任务:

  1. 将音量等级index转换为分贝db,computeVolume函数
  2. 将分贝值写入到输出设备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功放声音不受控

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值