Android P的音频架构(二)

深入探讨AndroidP的音频策略,包括AudioPolicyService的工作原理,音频设备管理,路由策略及音量控制。分析audio_policy_configuration.xml配置文件的结构与解析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android P的音频策略分析

本文主要讲解AudioPolicy部分,对音频策略流程的分析,音频策略代码在frameworks\av\services\audiopolicy中。

相关定义:
frameworks/base/media/java/android/media/AudioSystem.java

AudioPolicyService

AudioPolicyService是Android音频系统的两大服务之一,路径为:frameworks\av\services\audiopolicy\service\AudioPolicyService.cpp

AudioPolicyService主要完成以下任务:

  • JAVA应用层通过JNI,经由IAudioPolicyService接口,访问AudioPolicyService提供的服务
  • 输入输出设备的连接状态
  • 系统的路由策略(strategy)的切换
  • 音量参数的设置

AudioPolicyService详细说明:

  • AudioPolicyService同时也继承了AudioPolicyClientInterface类,他有一个AudioPolicyInterface类的成员指针mAudioPolicyManager,实际上就是指向了AudioPolicyManager;
  • AudioPolicyManager类继承了AudioPolicyInterface类以便向AudioPolicyService提供服务,反过来同时还有一个AudioPolicyClientInterface指针,该指针在构造函数中被初始化,指向了AudioPolicyService,实际上,AudioPolicyService是通过成员指针mAudioPolicyManager访问AudioPolicyManager,而AudioPolicyManager则通过AudioPolicyClientInterface(mpClientInterface)访问AudioPolicyService;
  • AudioPolicyService有一个内部线程类AudioCommandThread,顾名思义,所有的命令(音量控制,输入、输出的切换等)最终都会在该线程中排队执行;

AudioPolicyService的很大一部分管理工作都是在AudioPolicyManager中完成的。包括音量管理,路由策略(strategy)管理,输入输出设备管理。下面我们重点看一下AudioPolicyManager.cpp文件

AudioPolicyManager

AudioPolicyManager主要完成了路由策略管理和输入输出设备的管理,其路径为:frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp

一、输入输出设备管理

Audio系统中定义了一些输入输出设备,定义在文件audio-base.h中,该文件路径为:system\media\audio\include\system\audio-base.h

定义如下:

enum {
    AUDIO_DEVICE_NONE                          = 0x0u,
    AUDIO_DEVICE_BIT_IN                        = 0x80000000u,
    AUDIO_DEVICE_BIT_DEFAULT                   = 0x40000000u,

    AUDIO_DEVICE_OUT_EARPIECE                  = 0x1u,		//听筒
    AUDIO_DEVICE_OUT_SPEAKER                   = 0x2u,		//扬声器
    AUDIO_DEVICE_OUT_WIRED_HEADSET             = 0x4u,		//带话筒的耳机
    AUDIO_DEVICE_OUT_WIRED_HEADPHONE           = 0x8u,		//不带话筒的耳机
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO             = 0x10u,		//蓝牙,面向连接(SCO)方式,主要用于语音传输
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET     = 0x20u,		//蓝牙耳机,带话筒
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT      = 0x40u,		//蓝牙车载设备
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP            = 0x80u,		//蓝牙立体声
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100u,	//蓝牙立体声音耳机
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER    = 0x200u,	//蓝牙扬声器
    AUDIO_DEVICE_OUT_AUX_DIGITAL               = 0x400u,	//Aux
    AUDIO_DEVICE_OUT_HDMI                      = 0x400u,      // OUT_AUX_DIGITAL
    AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET         = 0x800u,
    AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET         = 0x1000u,
    AUDIO_DEVICE_OUT_USB_ACCESSORY             = 0x2000u,
    AUDIO_DEVICE_OUT_USB_DEVICE                = 0x4000u,
    AUDIO_DEVICE_OUT_REMOTE_SUBMIX             = 0x8000u,
    AUDIO_DEVICE_OUT_TELEPHONY_TX              = 0x10000u,
    AUDIO_DEVICE_OUT_LINE                      = 0x20000u,
    AUDIO_DEVICE_OUT_HDMI_ARC                  = 0x40000u,
    AUDIO_DEVICE_OUT_SPDIF                     = 0x80000u,
    AUDIO_DEVICE_OUT_FM                        = 0x100000u,
    AUDIO_DEVICE_OUT_AUX_LINE                  = 0x200000u,
    AUDIO_DEVICE_OUT_SPEAKER_SAFE              = 0x400000u,
    AUDIO_DEVICE_OUT_IP                        = 0x800000u,
    AUDIO_DEVICE_OUT_BUS                       = 0x1000000u,
    AUDIO_DEVICE_OUT_PROXY                     = 0x2000000u,
    AUDIO_DEVICE_OUT_USB_HEADSET               = 0x4000000u,
    AUDIO_DEVICE_OUT_HEARING_AID               = 0x8000000u,
    AUDIO_DEVICE_OUT_ECHO_CANCELLER            = 0x10000000u,
    AUDIO_DEVICE_OUT_DEFAULT                   = 0x40000000u, // BIT_DEFAULT

    AUDIO_DEVICE_IN_COMMUNICATION              = 0x80000001u, // BIT_IN | 0x1
    AUDIO_DEVICE_IN_AMBIENT                    = 0x80000002u, // BIT_IN | 0x2
    AUDIO_DEVICE_IN_BUILTIN_MIC                = 0x80000004u, // BIT_IN | 0x4
    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET      = 0x80000008u, // BIT_IN | 0x8
    AUDIO_DEVICE_IN_WIRED_HEADSET              = 0x80000010u, // BIT_IN | 0x10
    AUDIO_DEVICE_IN_AUX_DIGITAL                = 0x80000020u, // BIT_IN | 0x20
    AUDIO_DEVICE_IN_HDMI                       = 0x80000020u, // IN_AUX_DIGITAL
    AUDIO_DEVICE_IN_VOICE_CALL                 = 0x80000040u, // BIT_IN | 0x40
    AUDIO_DEVICE_IN_TELEPHONY_RX               = 0x80000040u, // IN_VOICE_CALL
    AUDIO_DEVICE_IN_BACK_MIC                   = 0x80000080u, // BIT_IN | 0x80
    AUDIO_DEVICE_IN_REMOTE_SUBMIX              = 0x80000100u, // BIT_IN | 0x100
    AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET          = 0x80000200u, // BIT_IN | 0x200
    AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET          = 0x80000400u, // BIT_IN | 0x400
    AUDIO_DEVICE_IN_USB_ACCESSORY              = 0x80000800u, // BIT_IN | 0x800
    AUDIO_DEVICE_IN_USB_DEVICE                 = 0x80001000u, // BIT_IN | 0x1000
    AUDIO_DEVICE_IN_FM_TUNER                   = 0x80002000u, // BIT_IN | 0x2000
    AUDIO_DEVICE_IN_TV_TUNER                   = 0x80004000u, // BIT_IN | 0x4000
    AUDIO_DEVICE_IN_LINE                       = 0x80008000u, // BIT_IN | 0x8000
    AUDIO_DEVICE_IN_SPDIF                      = 0x80010000u, // BIT_IN | 0x10000
    AUDIO_DEVICE_IN_BLUETOOTH_A2DP             = 0x80020000u, // BIT_IN | 0x20000
    AUDIO_DEVICE_IN_LOOPBACK                   = 0x80040000u, // BIT_IN | 0x40000
    AUDIO_DEVICE_IN_IP                         = 0x80080000u, // BIT_IN | 0x80000
    AUDIO_DEVICE_IN_BUS                        = 0x80100000u, // BIT_IN | 0x100000
    AUDIO_DEVICE_IN_PROXY                      = 0x81000000u, // BIT_IN | 0x1000000
    AUDIO_DEVICE_IN_USB_HEADSET                = 0x82000000u, // BIT_IN | 0x2000000
    AUDIO_DEVICE_IN_BLUETOOTH_BLE              = 0x84000000u, // BIT_IN | 0x4000000
    AUDIO_DEVICE_IN_DEFAULT                    = 0xC0000000u, // BIT_IN | BIT_DEFAULT
};

1.关于audio_policy_configuration.xml文件的解析

Android P 引入了一种新的音频政策配置文件格式 (XML),用于描述音频拓扑。

新的 XML 文件支持定义输出输入流配置文件、可用于播放和捕获的设备以及音频属性的数量和类型。此外,XML 格式还提供以下增强功能:

  • 音频配置文件目前的结构类似于 HDMI 简单音频描述符,支持每种音频格式使用一组不同的采样率/声道掩码。
  • 设备和流之间所有可能连接的显式定义。以前,借助隐式规则,可以使连接到同一 HAL 模块的所有设备互连,从而阻止音频政策控制使用音频补丁程序 API 请求的连接。现在,在 XML 格式中,拓扑描述定义了连接限制。
  • 对“包含”的支持可避免出现重复的标准 A2DP、USB 或重新导向提交定义。
  • 可自定义的音量曲线。以前,音量表采用硬编码格式。在 XML 格式中,音量表通过描述来定义,并且可自定义。

关于音频输入输出的xml配置文件的默认路径为:frameworks\av\services\audiopolicy\config\audio_policy_configuration.xml;
但是,实际的开发平台不同,音频设备不同,配置文件的路径一般根据开发平台放在device目录下,例如:device\fsl\imx8q\mek_8q\audio_policy_configuration.xml,该目录的文件会覆盖默认路径下的文件,成为有效的文件。

//audio_policy_configuration.xml文件
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
    <globalConfiguration speaker_drc_enabled="true"/>
    <modules>
    	//对应着一个HwModule对象
        <module name="primary" halVersion="2.0">
        	//定义需要的设备名称,可以自己定义,但需要跟<devicePorts>和<routes>节点的配置对应起来,保持一致
            <attachedDevices>
                <item>Speaker</item>
                <item>Built-In Mic</item>
            </attachedDevices>
            //配置默认的音频输出设备,默认为扬声器输出
            <defaultOutputDevice>Speaker</defaultOutputDevice>
            <mixPorts>
            	//定义输出流设备:primary ouput 
            	//包含由音频 HAL 提供的所有输出流和输入流的列表。每个 mixPort 都可被视为传输到 Android AudioService 的物理音频流
            	//role="source" 表示音频为输出源,是需要输出给输出设备
            	//flags="AUDIO_OUTPUT_FLAG_PRIMARY" 表示音频的输出标识
            	//<profile>分别配置音频的位宽,采样率和通道参数
                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
                //定义输出流设备:esai output
                <mixPort name="esai output" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
                </mixPort>
                //定义输入流设备:primary input
                <mixPort name="primary input" role="sink">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="8000,11025,16000,22050,24000,32000,44100,48000"
                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
                </mixPort>
            </mixPorts>
            <devicePorts>
            	//定义设备名称,但需要跟<attachedDevices>保持一致
            	//包含可从此模块访问的所有输入和输出设备(包括永久连接的设备和可移除设备)的设备描述符列表
            	//role="sink",表示该设备为输出目的地,音频数据往这个设备上输出
            	//type,对应着响应的设备类型
                <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink" >
                </devicePort>
                <devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
                </devicePort>
                <devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
                </devicePort>
            	//role="sink",表示该设备为输入源,音频数据往这个设备上输入
                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
                </devicePort>
                <devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
                </devicePort>
                <devicePort tagName="Spdif-In" type="AUDIO_DEVICE_IN_AUX_DIGITAL" role="source">
                </devicePort>
            </devicePorts>
            <routes>
            	//路由配置
            	//定义输入和输出设备之间或音频流和设备之间可能存在的连接的列表
            	//sink需要配置role="sink"的<devicePort>,表示输出到哪个设备上
            	//sources表示哪些输出流可以输出到sink的上去,一个sink可以对应多个sources
                <route type="mix" sink="Speaker"
                       sources="esai output,primary output"/>
                <route type="mix" sink="Wired Headset"
                       sources="primary output"/>
                <route type="mix" sink="Wired Headphones"
                       sources="primary output"/>
                <route type="mix" sink="primary input"
                       sources="Built-In Mic,Wired Headset Mic,Spdif-In"/>
            </routes>
        </module>
        //加载a2dp的HwModule
        <!-- A2dp Audio HAL -->
        <xi:include href="a2dp_audio_policy_configuration.xml"/>
        
        //加载usb的HwModule
        <!-- Usb Audio HAL -->
        <xi:include href="usb_audio_policy_configuration.xml"/>
        
        //加载r_submix的HwModule
        <!-- Remote Submix Audio HAL -->
        <xi:include href="r_submix_audio_policy_configuration.xml"/>
    </modules>
    
    //音量管理
    <!-- Volume section -->
    <xi:include href="audio_policy_volumes.xml"/>
    <xi:include href="default_volume_tables.xml"/>
</audioPolicyConfiguration>
audio_policy_configuration.xml层级结构:
  • xml中 包含和其他模块, 每个modules包含了多个 module 成员,每个 module都是一个 Audio HAL对象(对应代码中的HwModule类)。
  • 每个module中又包含了多个层级, 从PolicySerializer::deserialize函数可以看出,android先解析module, 再解析其他模块,我们着重分析 module 模块的解析过程。

audio_policy_configuration.xml文件的解析是在AudioPolicyManager的构造函数中进行的,主要逻辑如下:

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
        : AudioPolicyManager(clientInterface, false /*forTesting*/)
{
	//最终会调用\frameworks\av\services\audiopolicy\common\managerdefinitions\src\Serializer.cpp中的
	//PolicySerializer::deserialize()进行xml解析
    loadConfig();		//加载audio_policy_configuration.xml文件 ,加载到mConfig中
    
    initialize();		//根据xml文件信息,初始化输出输入流
}

//AudioPolicyManager.h文件中定义了mOutputs
SwAudioOutputCollection mOutputs;

status_t AudioPolicyManager::initialize() {
	//保存可用的输入输出设备到mOutputs中
}
PolicySerializer::deserialize()分析:

\frameworks\av\services\audiopolicy\common\managerdefinitions\src\Serializer.cpp文件:

status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig &config)
{
	//省略一部分代码
   	...
   	
   	//开始解析Modules子节点
    // Lets deserialize children
    // Modules
    ModuleTraits::Collection modules;
    deserializeCollection<ModuleTraits>(doc, cur, modules, &config);
    config.setHwModules(modules);	//解析完后的数据设置到config中

	//省略一部分代码
   	...
   	
	//...
	//解析Volumes子节点
	//解析全局子节点
   
}

static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur,
                                      typename Trait::Collection &collection,
                                      typename Trait::PtrSerializingCtx serializingContext)
{
	//省略一部分代码
   	...
   	
	//解析模块时,先调用一个模板函数 deserializeCollection
	//依次解析每个子元素<module>时,会依次调用对应 ModuleTraits::deserialize 函数,并add到集合collection中
	//最终返回时设置给AudioPolicyConfig &config
	while (child != NULL) {
		if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) {
			typename Trait::PtrElement element;
			//调用ModuleTraits::deserialize, 解析Modules节点下面的child
			status_t status = Trait::deserialize(doc, child, element, serializingContext);
			if (status != NO_ERROR) {
			return status;
		}
		if (collection.add(element) < 0) {
			ALOGE("%s: could not add element to %s collection", __FUNCTION__,
			     Trait::collectionTag);
			}
		}
		child = child->next;
	}
	
	//省略一部分代码
   	...
   	
}

status_t ModuleTraits::deserialize(xmlDocPtr doc, const xmlNode *root, PtrElement &module,
                                   PtrSerializingCtx ctx)
{
	//省略一部分代码
   	...
   	
	// Modules下面的子节点
	// Deserialize childrens: Audio Mix Port, Audio Device Ports (Source/Sink), Audio Routes
	
	//MixPorts
	//包含由音频 HAL 提供的所有输出流和输入流的列表。每个 mixPort 都可被视为传输到 Android AudioService 的物理音频流。
    MixPortTraits::Collection mixPorts;
    deserializeCollection<MixPortTraits>(doc, root, mixPorts, NULL);
    module->setProfiles(mixPorts);

	//DevicePorts
	//包含可从此模块访问的所有输入和输出设备(包括永久连接的设备和可移除设备)的设备描述符列表
    DevicePortTraits::Collection devicePorts;
    deserializeCollection<DevicePortTraits>(doc, root, devicePorts, NULL);
    module->setDeclaredDevices(devicePorts);

	//Routes:
	//定义输入和输出设备之间或音频流和设备之间可能存在的连接的列表。
    RouteTraits::Collection routes;
    deserializeCollection<RouteTraits>(doc, root, routes, module.get());
    module->setRoutes(routes);

	//MixPort,DevicePorts和Routes的详细解析就不在这里分析了
    //他们分别在MixPortTraits::deserialize、DevicePortTraits::deserialize、RouteTraits::deserialize中进行解析
    
    while (children != NULL) {
        if (!xmlStrcmp(children->name, (const xmlChar *)childAttachedDevicesTag)) {
            ALOGV("%s: %s %s found", __FUNCTION__, tag, childAttachedDevicesTag);
            const xmlNode *child = children->xmlChildrenNode;
            while (child != NULL) {
                if (!xmlStrcmp(child->name, (const xmlChar *)childAttachedDeviceTag)) {
                    xmlChar *attachedDevice = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
                    if (attachedDevice != NULL) {
                        ALOGV("%s: %s %s=%s", __FUNCTION__, tag, childAttachedDeviceTag,(const char*)attachedDevice);
                        sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)attachedDevice));

						//添加有效的设备到AudioPolicyConfig(mConfig)中
                        ctx->addAvailableDevice(device);
                        xmlFree(attachedDevice);
                    }
                }
                child = child->next;
            }
        }
        if (!xmlStrcmp(children->name, (const xmlChar *)childDefaultOutputDeviceTag)) {
            xmlChar *defaultOutputDevice = xmlNodeListGetString(doc, children->xmlChildrenNode, 1);;
            if (defaultOutputDevice != NULL) {
                ALOGV("%s: %s %s=%s", __FUNCTION__, tag, childDefaultOutputDeviceTag,
                      (const char*)defaultOutputDevice);
                sp<DeviceDescriptor> device =
                        module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)defaultOutputDevice));
                if (device != 0 && ctx->getDefaultOutputDevice() == 0) {
                    ctx->setDefaultOutputDevice(device);
                    ALOGV("%s: default is %08x", __FUNCTION__, ctx->getDefaultOutputDevice()->type());
                }
                xmlFree(defaultOutputDevice);
            }
        }
        children = children->next;
    }
    
	//省略一部分代码
   	...
   	
}

AudioPolicyManager.h中mConfig的定义为:AudioPolicyConfig mConfig;

2.AudioPolicyConfig文件分析

AudioPolicyConfig 文件包含了HwModule的集合和可用输入输出设备容器:

private:
    HwModuleCollection &mHwModules; 		//Module的集合,包含配置文件中设备,端口,路由等信息
    DeviceVector &mAvailableOutputDevices;	//可用输出设备的容器
    DeviceVector &mAvailableInputDevices;	//可用输入设备的容器
    sp<DeviceDescriptor> &mDefaultOutputDevices;	//默认输出设备,AudioPolicy会默认指定一个输出设备
    VolumeCurvesCollection *mVolumeCurves;			//音频音量的集合
    // TODO: remove when legacy conf file is removed. true on devices that use DRC on the
    // DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly.
    // Note: remove also speaker_drc_enabled from global configuration of XML config file.
    bool mIsSpeakerDrcEnabled;

3.HwModule文件分析

HwModule文件描述了一个Module包含了哪些属性:

private:
	const String8 mName; 			// 音频Module的基本名称 (primary, a2dp ...)
	audio_module_handle_t mHandle;	// 能够引用Module方法和结构的句柄
	OutputProfileCollection mOutputProfiles; // 此模块公开的输出配置文件
	InputProfileCollection mInputProfiles;   // 此模块公开的输入配置文件
	uint32_t mHalVersion; 					 // HAL层 API的版本
	DeviceVector mDeclaredDevices; 			 // 在audio_policy_configuration.xml配置文件中声明的设备
	AudioRouteVector mRoutes;				 // 在audio_policy_configuration.xml配置文件中声明的路由
	AudioPortVector mPorts;					 // 在audio_policy_configuration.xml配置文件中声明的设备端口

二、路由管理

在音频架构(一)讲到,AudioFlinger会调用到AudioPolicyManager::getOutputForAttr()的方法。

status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
                                              audio_io_handle_t *output,
                                              audio_session_t session,
                                              audio_stream_type_t *stream,
                                              uid_t uid,
                                              const audio_config_t *config,
                                              audio_output_flags_t *flags,
                                              audio_port_handle_t *selectedDeviceId,
                                              audio_port_handle_t *portId)
{

	//省略一部分代码
   	...
   	
	//根据上层app设置的streamType来添加一个路由
    mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);

	//根据上层app设置attributes来选择一个策略
	//策略定义在一个枚举类型中frameworks\av\services\audiopolicy\common\include\RoutingStrategy.h
	//	enum routing_strategy {
    //		STRATEGY_MEDIA,
    //		STRATEGY_PHONE,
    //		STRATEGY_SONIFICATION,
    //		STRATEGY_SONIFICATION_RESPECTFUL,
    //		STRATEGY_DTMF,
    //		STRATEGY_ENFORCED_AUDIBLE,
    //		STRATEGY_TRANSMITTED_THROUGH_SPEAKER,
    //		STRATEGY_ACCESSIBILITY,
    //		STRATEGY_REROUTING,
    //		NUM_STRATEGIES
	//	};
    routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
    
    //根据策略返回相对应的输出设备
	audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);

	//
    *output = getOutputForDevice(device, session, *stream, config, flags);
    if (*output == AUDIO_IO_HANDLE_NONE) {
        mOutputRoutes.removeRoute(session);
        return INVALID_OPERATION;
    }

	//省略一部分代码
   	...
}
<think>我们首先需要理解Android音频系统中devicePort和mixPort的概念及其关系。 在Android音频框架中,音频流在软件层面的处理涉及多个抽象层。其中: - mixPort:代表一个混音端口,通常对应一个输出流(如播放)或输入流(如录音)。它属于音频模块(AudioModule/AudioHAL)的一部分,可以理解为音频数据处理的一个逻辑端点。 - devicePort:代表物理或逻辑的音频设备端口,如扬声器、耳机、蓝牙设备等。 在音频路由过程中,数据流向通常是: 应用 → 音频框架(AudioFlinger) → mixPort(进行混音等处理) → devicePort(连接到具体的物理设备) 但问题中描述“为devicePort找合适mixPort”,而数据流向是“从mixPort传导到devicePort”,这似乎与上述流向一致。然而,用户可能对这两个概念的关系有疑问。 实际上,在Android音频策略管理(AudioPolicyManager)中,需要为音频流(AudioTrack)选择合适的输出设备(devicePort),而该设备必须与某个mixPort关联。因为mixPort是设备在HAL层的代表,数据最终通过mixPort输出到devicePort对应的物理设备。 更准确的数据流是: App → AudioFlinger (创建Track) → 被路由到某个MixerThread(对应一个mixPort) → 通过该mixPort输出到关联的devicePort(物理设备) 因此,选择过程是:根据音频属性(usage, content type等)确定输出设备(devicePort),然后根据该devicePort找到其支持的mixPort(在音频策略配置文件中通过<route>标签关联)。 在音频策略配置文件(audio_policy_configuration.xml)中,会定义: - <mixPort>:定义混音端口(如primary output, deep buffer等) - <devicePort>:定义设备端口(如扬声器、耳机等) - <route>:定义路由,指明数据可以从某个mixPort流向某个devicePort,或者从某个devicePort流向某个mixPort(对于输入)。 所以,当我们为某个devicePort寻找合适的mixPort时,实际上是在寻找一个支持输出到该devicePort的mixPort。数据确实是从mixPort流向devicePort的。 回答重点: 1. 解释mixPort和devicePort的关系。 2. 说明数据流向:mixPort -> devicePort 是正确的。 3. 在策略管理中,为devicePort找mixPort是为了确定音频流应该通过哪个混音端口输出到该设备。 因此,我们可以这样组织回答: ### 音频路由中mixPort与devicePort的关联机制 #### 一、核心概念定义 ```mermaid graph LR A[应用音频流] --> B(AudioFlinger) B --> C[MixerThread] C --> D[mixPort] D --> E[devicePort] E --> F[物理设备] ``` #### 、配置关联关系 在`audio_policy_configuration.xml`中: ```xml <audioPolicyConfiguration> <modules> <module name="primary"> <!-- 定义混音端口 --> <mixPort name="primary output" role="source"/> <mixPort name="deep_buffer" role="source"/> <!-- 定义设备端口 --> <devicePort tagName="Speaker" type="SPEAKER" role="sink"/> <devicePort tagName="Headphones" type="HEADPHONES" role="sink"/> <!-- 定义路由:从mixPort到devicePort --> <route source="primary output" sink="Speaker"/> <route source="deep_buffer" sink="Speaker"/> <route source="primary output" sink="Headphones"/> </module> </modules> </audioPolicyConfiguration> ``` #### 三、策略选择流程 1. **确定目标设备(devicePort)** 根据音频属性(usage, content type等)和当前设备连接状态选择devicePort。 *示例:插入耳机时,媒体音频路由到耳机。* 2. **查找支持该设备的mixPort** 通过预定义的路由(route)关系,找到所有可以连接到目标devicePort的mixPort。 *约束条件:* - mixPort必须支持该音频流的格式和采样率 - mixPort必须满足延迟要求(如deep buffer用于高延迟播放,而low latency用于低延迟) 3. **激活路由** AudioPolicyManager将音频流引导至选定的mixPort,数据最终通过mixPort输出到devicePort。 #### 四、常见误区澄清 | **误解** | **实际原理** | |-----------------------------------|------------------------------------------------| | “先有devicePort再找mixPort”是反向 | 正是为输出到特定设备(devicePort)而选择mixPort | | 数据从devicePort流向mixPort | 数据只能从mixPort(源)流向devicePort(接收器) | §§相关问题§§ 1. 在音频策略配置中,如何为一个mixPort定义多个目标devicePort以实现多路同时输出? 2. 当插入USB音频设备时,AudioPolicyManager如何动态更新可用mixPort与devicePort的匹配关系? 3. 在Android的A2DP蓝牙音频传输中,mixPort与devicePort的具体路由路径是怎样的? 4. 如果多个应用同时播放音频到不同设备,AudioFlinger如何管理多个mixPort的并发访问? 5. 在音频输入场景(录音)中,devicePort到mixPort的路由策略与输出有何差异?</think>### Android音频路由中mixPort与devicePort的关联机制解析 #### 一、核心概念定义 ```mermaid graph TD A[应用层] -->|PCM数据| B(AudioFlinger) B --> C{mixPort} C -->|处理后的数据| D[devicePort] D --> E[物理设备] style C fill:#f9f,stroke:#333 style D fill:#bbf,stroke:#333 ``` | **对象** | **角色** | **数据流向** | **配置文件位置** | |---------------|--------------------------|-------------------|------------------------| | **mixPort** | 音频处理节点(混音/效果) | 数据生产者 → | audio_policy_configuration.xml | | **devicePort**| 物理设备抽象(扬声器/耳机)| → 数据消费者 | audio_policy_configuration.xml | #### 、路由匹配逻辑 1. **策略决策流程** ```cpp // AudioPolicyManager.cpp status_t getOutputForDevice(audio_io_handle_t *output, audio_devices_t device, audio_stream_type_t stream) { // 步骤1:根据设备类型筛选候选mixPort Vector<audio_io_handle_t> outputs = getOutputsForDevice(device); // 步骤2:基于流类型选择最优mixPort for (audio_io_handle_t out : outputs) { if (supportsStream(out, stream)) { *output = out; // 返回匹配的mixPort句柄 return NO_ERROR; } } return BAD_VALUE; } ``` 2. **配置映射示例** ```xml <!-- audio_policy_configuration.xml --> <route type="mix" sink="Speaker" sources="primary_output,deep_buffer_output"/> <!-- 定义Speaker设备可用的mixPort源 --> ``` #### 三、数据流向误区澄清 | **误解** | **实际原理** | |-------------------------------|------------------------------------------------| | "数据从devicePort流向mixPort" | 数据永远从mixPort → devicePort → 物理设备 | | "为devicePort找mixPort是反向" | 本质是为物理设备选择处理节点(电源管理/音效) | | "两者是同级关系" | mixPort是数据源,devicePort是数据终点 | #### 四、动态路由场景 ```mermaid sequenceDiagram 应用->>+AudioFlinger: 请求播放(设备=蓝牙耳机) AudioFlinger->>AudioPolicy: getOutputForDevice() AudioPolicy->>HAL: 查询可用mixPort HAL-->>AudioPolicy: 返回 a2dp_output mixPort AudioPolicy-->>应用: 分配a2dp_output 应用->>a2dp_output: 写入PCM数据 a2dp_output->>蓝牙devicePort: 传输处理后的数据 蓝牙devicePort->>物理耳机: 输出音频 ``` #### 五、关键设计价值 1. **硬件抽象**:使应用无需感知具体物理设备 2. **功耗优化**:通过匹配低功耗mixPort(如DEEP_BUFFER) 3. **动态切换**:耳机插入时自动重路由到HEADPHONES devicePort
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sunxiaolin2016

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值