Android7.1 音频声音控制策略

我们知道在AUDIO_STREAM_MUSIC的stream类型下,声音是从Speaker或则耳机输出的,为了加深对AudioPolicy的认识,希望通过修改代码实现在AUDIO_STREAM_MUSIC的stream类型下,声音可以从Speaker和耳机同时输出。其实办法有2个,一个是改Framwork层,一个方法是改Hal层,原理其实是一致的。

Track::start
	AudioPolicyManager::startOutput
		startSource
			setOutputDevice
				createAudioPatch
HAL:				out_set_parameters       (高通不支持AudioPatch,转换为设置parameters)
						修改stream输出设备的指向:out->devices

如上流程就是修改音频输出设备的地方,因此如果修改Hal的方式,那么只需要如下改动即可:

hardware\qcom\audio\hal\Audio_hw.c

int start_output_stream(struct stream_out *out)
{
	...
	// Jon Add Begin
	out->devices = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES
	// End
	...
	uc_info->devices = out->devices;
	...
	//完成声卡和路由的切换和打开
	select_devices(adev, out->usecase);
	...
}

关于第二种方式有点曲折,我是这么修改的:

hardware\qcom\audio\policy_hal\AudioPolicyManager.cpp

status_t AudioPolicyManagerCustom::startSource(sp<AudioOutputDescriptor> outputDesc,
                                             audio_stream_type_t stream,
                                             audio_devices_t device,
                                             const char *address,
                                             uint32_t *delayMs)
{
	...
	//Begin Jon Add
	if(stream == AUDIO_STREAM_MUSIC)
		device = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET;
	//End
	
	muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address);
	...
}

这种方式修改后发现无论是Speaker还是耳机都没有声音,同时adb去查看Speaker和耳机的控件,发现device和dai都是正确打开了的,百思不得其解,最后再一思索,音频路径虽然都打通了,如果音量被设置为0了,不也没有声音吗。果然:

adb shell dumpsys media.audio_policy > D:\policy.txt

- Output 29 dump:
 profile name: deep_buffer
 Latency: 80
 Flags 00000008
 ID: 6
 Sampling rate: 48000
 Format: 00000001
 Channels: 00000003
 Devices 00000006
 Stream volume refCount muteCount
 00     0.000     00       01
 01     -764.000     00       01
 02     -14.700     00       00
 03     -758.00     03       01                   //对应这条stream
 04     -14.450     00       00
 05     -764.000     00       01
 06     -1.000     00       01
 07     -764.000     00       01
 08     -758.000     00       01
 09     0.000     00       01
 10     -758.000     00       01
 11     0.000     00       01
 12     -1.000     00       00

我们的stream是music类型,因此序号为3,我们发现volume为 -758,果然是被静音了。

我们借此为契机,分析下为什么这种情况下music的stream会被静音

hardware\qcom\audio\policy_hal\AudioPolicyManager.cpp

uint32_t AudioPolicyManager::setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                             audio_devices_t device,
                                             bool force,
                                             int delayMs,
                                             audio_patch_handle_t *patchHandle,
                                             const char* address)
{
	...
	//特别留意这条,会将音频路径的默认设备输出改变
	if (device != AUDIO_DEVICE_NONE) {
        outputDesc->mDevice = device;
    }
    
    muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);
    ...
}

静音路径如下:

checkDeviceMuteStrategies
	setStrategyMute
		setStreamMute
			checkAndSetVolume
				float volumeDb = computeVolume(stream, index, device);
				outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

我们重点分析下checkDeviceMuteStrategies这个函数就好:

uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc,
                                                       audio_devices_t prevDevice,
                                                       uint32_t delayMs)
{

    uint32_t muteWaitMs = 0;
    //获取当前音频路径的默认输出设备,此刻已经被改变为Speaker+耳机
    audio_devices_t device = outputDesc->device();
    //是否静音的判断原则,当前音频路径处于活跃状态,而且新设置的输出设备终端多余2个,
    //很不幸,我们当前全都满足,shouldMute为 ture
    bool shouldMute = outputDesc->isActive() && (popcount(device) >= 2);

    for (size_t i = 0; i < NUM_STRATEGIES; i++) {
    	//对于STRATEGY_MEDIA策略,返回curDevice 为0x04(耳机),针对这个函数后面有分析
        audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
        //主输出的路径上肯定是有耳机的,因此curDevice还是为0x04
        curDevice = curDevice & outputDesc->supportedDevices();
        //curDevice为0x04,但是device是0x06,所有mute为true
        bool mute = shouldMute && (curDevice & device) && (curDevice != device);
        bool doMute = false;
		//由于设备描述符在构造的时候,对于所有的策略mStrategyMutedByDevice全部赋值为false,因此doMute被赋值为true了
        if (mute && !outputDesc->mStrategyMutedByDevice[i]) {
            doMute = true;
            outputDesc->mStrategyMutedByDevice[i] = true;
        } else if (!mute && outputDesc->mStrategyMutedByDevice[i]){
            doMute = true;
            outputDesc->mStrategyMutedByDevice[i] = false;
        }
        if (doMute) {
        	//我机子总共4条音频路径,取出所有音频路径逐个和当前调用的音频路径的比较是否支持的设备集合有交集,但凡有交集,那么对应的音频路径在这个策略上都会被mute
            for (size_t j = 0; j < mOutputs.size(); j++) {
                sp<AudioOutputDescriptor> desc = mOutputs.valueAt(j);
                // skip output if it does not share any device with current output
                if ((desc->supportedDevices() & outputDesc->supportedDevices())
                        == AUDIO_DEVICE_NONE) {
                    continue;
                }
                
				
                setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs);
                ...
           }
	}
	...
    return 0;
}

我们先分析getDeviceForStrategy,根据策略获取当前的输出设备

audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                         bool fromCache)
{
    // Routing
    // see if we have an explicit route
    // scan the whole RouteMap, for each entry, convert the stream type to a strategy
    // (getStrategy(stream)).
    // if the strategy from the stream type in the RouteMap is the same as the argument above,
    // and activity count is non-zero
    // the device = the device from the descriptor in the RouteMap, and exit.
	/*
		下面的逻辑是从当前所有的音频路由中选择一条策略相同的,返回其路由指向的输出设备即可。原则有2条
		1. 音频路由中策略选择,是依据路由中的stream类型来判断
		2. 选中的音频路由,必须是active的(设备描述符不为空,与此同时这条音频路由发生了改变或者引用计数大于0)
		遗憾的是由于我当前的更改并不是track->setOutputDevice的方式,因此路由不会发生改变,当然会话的引用计数也为0,从而导致条件不满足,调用后面的mEngine->getDeviceForStrategy
	*/
    for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
        sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
        routing_strategy routeStrategy = getStrategy(route->mStreamType);
        if ((routeStrategy == strategy) && route->isActive()) {
            return route->mDeviceDescriptor->type();
        }
    }
	//这里fromCache为false
    if (fromCache) {
        ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
              strategy, mDeviceForStrategy[strategy]);
        return mDeviceForStrategy[strategy];
    }
	
	//直接根据音频策略获取输出设备,当前我插着耳机,因此必然返回0x04(耳机)
    return mEngine->getDeviceForStrategy(strategy);
}

checkDeviceMuteStrategies最后会调用到setStrategyMute,如下

void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
                                             bool on,
                                             const sp<AudioOutputDescriptor>& outputDesc,
                                             int delayMs,
                                             audio_devices_t device)
{
    ALOGVV("setStrategyMute() strategy %d, mute %d, output ID %d",
           strategy, on, outputDesc->getId());
    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
        if (getStrategy((audio_stream_type_t)stream) == strategy) {
        	//将system和music(都是STRATEGY_MEDIA策略)的stream在当前音频路径上静音
            setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device);
        }
    }
}

而我的测试代码正好是music的stream,因此被静音了。原因就在于这么暴力的修改并没有触发音频路由的改变,导致stream被静音,然后track->setOutputDevice虽然能够触发音频路由的改变,但是它并不能设置多个snd_device的输出,原因在前文分析过,它是比较的id(会自然增长)而不是匹配的device type。

有的小伙伴在这儿可能会有疑问,那为啥铃声类型的stream却可以从Speaker和耳机同时输出呢。
原因在于我们new track的时候会Call到getOutputForAttr

frameworks\av\services\audiopolicy\managerdefault\audioPolicyManager.cpp

status_t AudioPolicyManager::getOutputForAttr(...)
{
	audio_attributes_t attributes;
	if (attr != NULL) {
		attributes = *attr;
	} else {
		stream_type_to_audio_attributes(*stream, &attributes);
	}
	...
	//根据当前音频的属性,获取当前track的音频策略,这里的音频策略返回STRATEGY_PHONE
    routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
    //根据当前track的音频策略,获取当前音频的输出终端,这里会根据STRATEGY_PHONE的策略返回输出设备为0x06(Speaker和耳机)
    audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
    //根据音频设备,音频输出标识,format等选择输出路径,这个函数的详细分析请查看本文最后
    *output = getOutputForDevice(device, session, *stream,
                                 samplingRate, format, channelMask,
                                 flags, offloadInfo);
	...
}

如上

因此回到最初的话题,如果我非得在Framework中改下,使得music可以在耳机和Speaker同时输出,很显然我们只需要将getDeviceForStrategy返回耳机+speaker即可
如下:

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

audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                         bool fromCache)
{
	...
	
	/*Begin Add Jon*/
	if(strategy == STRATEGY_MEDIA)
		return AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET;
	/*End*/
	
    return mEngine->getDeviceForStrategy(strategy);
}

你可能感兴趣的:(Android-Audio)