音量调节相关的一些问题解答之Active StreamType原理

背景:

在学习了音量调节相关的课程后,大家对整个音量调节整个流程都应该很清楚了,但是在使用手机音量调节时候,对一些场景功能可能还是会有一些疑问,主要有以下几个部分:

1、为啥物理音量键可以准确识别当前正在播放的StreamType类型,从而调节对应的音量进度
在这里插入图片描述
2、物理音量按键也可以调节声音,设置界面的Seekbar也可以调节声音,请问二者是如何进行同步的呢?
在这里插入图片描述
物理音量调节后设置的画面的也seekbar会同步动起来。

那么本文主要来剖析一下疑问1

疑问1剖析:

为什么系统在按音量上下键时候,就可以准确识别出当前的StreamType,调节的都是当前正在播放的StreamType?

现象如下:
在设置中触发notification的声音播放时候,按下物理音量上下键,观察音量进度条是选中的notification这部分。
在这里插入图片描述铃声播放时候按音量上下键发现调节也是铃声的声音进度
在这里插入图片描述那么这块的具体的代码是如何实现的?具体应该如何剖析调试追踪呢?

调试追踪选中Active StreamType相关方法:

需要在AudioService中把DEBUG_VOL = true就行
在这里插入图片描述然后再使用如下方式过滤log:

 adb logcat | grep AudioService

按一下物理的音量上下键

在这里插入图片描述日志中其实也大概可以看出StreamType是如何变化的:
刚开始stream=-2147483648,然后通过getActiveStreamType变成的默认的StreamType为3,也就是StreamType为music,后续都一直以StreamType为3进行音量条件,那么接下来主要分析一下getActiveStreamType方法。

AudioService的getActiveStreamType获取活跃状态的StreamType

private int getActiveStreamType(int suggestedStreamType) {
      //省略
        switch (mPlatformType) {
        case AudioSystem.PLATFORM_VOICE:
              //判断是否处于语音电话等模式
            if (isInCommunication()
                    || mAudioSystem.isStreamActive(AudioManager.STREAM_VOICE_CALL, 0)) {
                if (!replaceStreamBtSco()
                        && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {  
                        //都会有log打印当前的StreamType情况
                    if (DEBUG_VOL) {
                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
                    }
                     //返回蓝牙相关StreamType
                    return AudioSystem.STREAM_BLUETOOTH_SCO;
                } else {
                    if (DEBUG_VOL) {
                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
                    }
                      //返回通话相关StreamType
                    return AudioSystem.STREAM_VOICE_CALL;
                }
            }
              //判断suggestedStreamType是不是值为默认,默认就是一个很小的负数
             else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
             //这里会判断是不是STREAM_RING处于活跃状态
                if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                    if (DEBUG_VOL)
                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
                    return AudioSystem.STREAM_RING;
                     //这里会判断是不是STREAM_NOTIFICATION处于活跃状态
                } else if (wasStreamActiveRecently(
                        AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                        if (DEBUG_VOL) {
                            Log.v(
                                    TAG,
                                    "getActiveStreamType: Forcing STREAM_NOTIFICATION stream"
                                            + " active");
                        }
                        return AudioSystem.STREAM_NOTIFICATION;
	                } else {
//如果上面两种StreamType都是不处于Active状态,那么就会返回默认的StreamType为music
                    if (DEBUG_VOL) {
                        Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
                                + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
                    }
                    //返回默认streamtype,也就是music
                    return DEFAULT_VOL_STREAM_NO_PLAYBACK;
                }
            } else if (
                    wasStreamActiveRecently(AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                if (DEBUG_VOL)
                    Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
                return AudioSystem.STREAM_NOTIFICATION;
            } else if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                if (DEBUG_VOL)
                    Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
                return AudioSystem.STREAM_RING;
            }
        default:
            if (isInCommunication()) {
                if (!replaceStreamBtSco()
                        && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
                    return AudioSystem.STREAM_BLUETOOTH_SCO;
                } else {
                    if (DEBUG_VOL)  Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
                    return AudioSystem.STREAM_VOICE_CALL;
                }
            } else if (mAudioSystem.isStreamActive(
                    AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                return AudioSystem.STREAM_NOTIFICATION;
            } else if (mAudioSystem.isStreamActive(
                    AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
                return AudioSystem.STREAM_RING;
            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
                if (mAudioSystem.isStreamActive(
                        AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                    return AudioSystem.STREAM_NOTIFICATION;
                }
                if (mAudioSystem.isStreamActive(
                        AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
                    return AudioSystem.STREAM_RING;
                }
                if (DEBUG_VOL) {
                    Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
                            + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
                }
                return DEFAULT_VOL_STREAM_NO_PLAYBACK;
            }
            break;
        }

//最后还会有一个蓝牙相关的替换
        suggestedStreamType = replaceBtScoStreamWithVoiceCall(suggestedStreamType,
                "getActiveStreamType");

        if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
                + suggestedStreamType);
        return suggestedStreamType;
    }

上面代码比较简单,本质上就是按照如下顺序
STREAM_BLUETOOTH_SCO --》 STREAM_VOICE_CALL --》 STREAM_NOTIFICATION --》STREAM_RING
进行判断是否有这些类型的StreamType处于活跃的,如果都没有最后就是返回DEFAULT_VOL_STREAM_NO_PLAYBACK也就是music状态。
那么看看是如何判断当前StreamType是否Active状态:

 public boolean isStreamActive(int stream, int inPastMs) {
        return AudioSystem.isStreamActive(stream, inPastMs);
    }

frameworks/av/media/libaudioclient/AudioSystem.cpp


status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, uint32_t inPastMs) {
    const sp<IAudioPolicyService> aps = get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    if (state == NULL) return BAD_VALUE;

    AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
            legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
            //最后调用到了AudioPolicyManager中
    int32_t inPastMsAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(inPastMs));
    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
            aps->isStreamActive(streamAidl, inPastMsAidl, state)));
    return OK;
}

下面来看看AudioPolicyManager的isStreamActive方法:

frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{
//转换成VolumeSource其实就是根据StreamType找到对应volume_group_t
    auto vs = toVolumeSource(stream, false);
    //然后在mOutputs集合中寻找这个VolumeSource是不是Active状态
    return vs != VOLUME_SOURCE_NONE ? mOutputs.isActive(vs, inPastMs) : false;
}

那么主要来看看mOutputs.isActive方法:
frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp

// SwAudioOutputCollection implementation
bool SwAudioOutputCollection::isActive(VolumeSource volumeSource, uint32_t inPastMs) const
{
    nsecs_t sysTime = systemTime();
    for (size_t i = 0; i < this->size(); i++) {
        const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
        //这里稍微要注意一下inPastMs代表在过去的多少ms内,但是系统一般设置为0ms
        if (outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
            return true;
        }
    }
    return false;
}

这里会去寻找各个AudioOutputDescriptor,也就OutputThread看看是否有这个类型的track处于Active状态,其实也可以直接通过dumpsys media.audio_policy查看:

如果没有任何track在播放,则所有的active count都为0

在这里插入图片描述比如这个时候如果正在播放铃声的StreamType为Ring的时候进行dump:
在这里插入图片描述可以看到这里volume activities的对应id为11,其实也就是VolumeSource,对应就是

在这里插入图片描述

原文地址参考:
https://mp.weixin.qq.com/s/JEZWQYysDMEndT3IqklWfw

更多framework实战开发干货,请关注“千里马学框架”

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值