当前位置: 首页 > article >正文

android音效算法集成框架(3)

同一个音效,它绑定到不同的seesionId时,会被重新创建一个新的EffectModule对象。比如BassBoost低音增强音效,它被分别绑定到QQ音乐和酷狗音乐的sessionId时,就会创建两个不同的BassBoost对象。这样两个音效处理就会各不干扰。

前面讲了多个EffectChain之间的执行顺序,那么在EffectChain中的多个EffectModule,它们的执行顺序是什么呢?AudioFlinger是根据effect_descriptor_t结构体(音效算法描述类)中的flags来控制的。在你实现的音效算法指定的flags中可以指定下面flag的任意一种:

EFFECT_FLAG_INSERT_FIRST:表示将自己这个音效算法插入到EffectChain最前面。即:最先执行。如果有多个音效算法指定了这个flag,就按照创建EffectModule的时间先后排序,后创建的放在后面执行。

EFFECT_FLAG_INSERT_LAST:表示将这个音效算法插入到EffectChain的最后面执行。如果有多个音效算法声明此flag,后创建的EffectModule会插入到上次那个的前面。

EFFECT_FLAG_INSERT_ANY:表示将这个音效算法插入到INSERT_FIRST和INSERT_LAST之间。有多个时,按照加入的时间依次向后插入。

EFFECT_FLAG_INSERT_EXCLUSIVE:表示排它。意思是插入的EffectChain中,只能包含这一个音效算法。

以下就是EffectChain中多个EffectModule的执行顺序:

INSERT_FIRST1--》INSERT_FIRST2--》INSERT_ANY1--》INSERT_ANY2--》INSERT_LAST2--》INSERT_LAST1

当AudioFlinger中的PlaybackThread在执行时,会创建三个本地内存块:mMixerBuffer、mEffectBuffer、mSinkBuffer。

mMixerBuffer内存块中保存的是所有没有绑定任何音效算法的Track的音频数据。

mEffectBuffer内存块中保存的是所有经过音效算法处理后的Track的音频数据。

mSinkBuffer内存块中保存的是最终需要输出给AudioHAL进程中StreamOut的音频数据。

所有的Track数据,都会先传递给AudioMixer混音器进行多声道转换、采样精度调整、重采样后,才会传递给绑定的音效算法处理或传递给mMixerBuffer内存块。

当此次PlaybackThread播放时所有的Track都没有绑定音效算法,PlaybackThread会将mMixerBuffer中的数据直接拷贝到mSinkBuffer中。

当此次PlaybackTread播放时所有的Track都绑定了音效算法,mMixerBuffer内存块中就没有数据,就不会将其拷贝搬运到任何地方。

当此次PlaybackTread播放时即有绑定音效算法的Track,又有没有绑定的Track,PlaybackThread就会先将mMixerBuffer中的数据拷贝到mEffectBuffer中,再将mEffectBuffer的数据拷贝到mSinkBuffer中

注意这里如果有多个数据流向同一块buffer,则会进行混音。

Device音效的绑定过程(和sink关联)

Android中支持两种方式,将一个音效算法绑定到Device上处理。一种是动态创建一个AudioEffect对象,并给该对象指定的seesionId为AUDIO_SESSION_DEVICE。另一种是在audio_effects.xml文件的节点中申明,然后由AudioPolicyService解析后,通知AudioFlinger进行绑定。下面将说明一下通过audio_effects.xml配置文件的绑定方式。

AudioPolicyService在它的初始化函数onFirstRef()中,会创建AudioPolicyEffects对象,AudioPolicyEffects在其构造函数中就会进行audio_effects.xml文件的解析,然后通知AudioFlinger绑定音效。以下是代码调用流程

AudioPolicyService.cpp->onFirstRef()

    |->new AudioPolicyEffects()

        |->AudioPolicyEffects.cpp->loadAudioEffectXmlConfig()//mInputSources集合中保存的是<preprocess>节点中的配置。mOutputStreams集合中保存的是<postprocess>节点中的配置。mDeviceEffects集合中保存的是<deviceEffects>节点中的配置。

            |->EffectsConfig.cpp->parse()//libeffectsconfig

        |->AudioPolicyEffects.cpp->initDefaultDeviceEffects()

AudioPolicyEffects与EffectHAL一样,也是调用EffectsConfig.cpp->parse()来进行audio_effects.xml文件的解析,它会将节点中的配置保存到mDeviceEffects集合中,将节点中的配置保存到mInputSources集合,将节点中的配置保存到mOutputStreams集合中。

然后会调用initDefaultDeviceEffects()函数,通知AudioFlinger进行Device音效绑定。在该函数中,会为每个待绑定的音效算法,创建一个AudioEffect.cpp对象,并将sessionID设置为AUDIO_SESSION_DEVICE,将待绑定的DeviceType设置到AudioEffect对象中。然后调用AudioEffect->setEnabled(true)函数。这样做之后,AudioEffect对象在它的初始化时就会通知AudioFlinger绑定Device音效。以下是代码调用流程:

AudioPolicyEffects.cpp->initDefaultDeviceEffects()//绑定Device音效。为每个Device对应的所有Effect,都分别创建一个包名为android的客户端AudioEffect对象。并初始化它。

   |->AudioEffect.cpp->set(AUDIO_SESSION_DEVICE)

        |->AudioFlinger.cpp->createEffect()//Binder跨进程调用

            |->DeviceEffectManager.cpp->AudioFlinger::DeviceEffectManager::createEffect_l()

                |->new DeviceEffectProxy()//从mDeviceEffects列表中找不到此音效时,新创建一个。

                |->new EffectHandle()//mEffect引用的是DeviceEffectProxy对象

                |->Effects.cpp->AudioFlinger::DeviceEffectProxy::init()

                    |->Effects.cpp->AudioFlinger::DeviceEffectProxy::onCreatePatch()

                        |->Effects.cpp->AudioFlinger::DeviceEffectProxy::checkPort()//为当前Device绑定音效



    |->AudioEffect.cpp->setEnabled(true)       

从上面的代码流程可以看出,AudioFlinger通过自己的成员变量DeviceEffectManager创建了一个DeviceEffectProxy对象。而实现Device音效绑定的地方,是在DeviceEffectProxy::checkPort()函数中。这个函数为Device音效实现了两种绑定方式:

一种是让AudioHAL进程中的DeviceHAL直接处理音效数据。通过调用audio.h->audio_hw_device_t.add_device_effect(audio_port_handle_t device, effect_handle_t effect)接口实现。这种方式在音效算法处理过程中,就不会出现跨进程调用和共享内存创建的情况了。也就是说会由AudioHAL进程处理音效算法生成的数据,而不是AudioFlinger处理。AudioFlinger只负责控制音效算法的参数。

另一种方法是找到这个Device对应的PlaybackThread,由PlaybackThread去创建EffectModule对象,并插入到AUDIO_SESSION_DEVICE这个sessionId对应的EffectChain中,最终由PlaybackThread在传输音频数据时调用EffectChain->process_l()函数去处理音效算法生成的数据。也就是说由AudioServer进程负责处理音效数据。这种方式会出现跨进程调用和额外的共享内存创建。

DeviceHAL处理音效数据的方式

采用这种方式,加载的音效算法必须声明为前处理音效类型(EFFECT_FLAG_TYPE_PRE_PROC)或后处理音效类型(EFFECT_FLAG_TYPE_POST_PROC)。并且声明EFFECT_FLAG_HW_ACC_TUNNEL这个flag。它们都是通过在音效算法的实现代码中,设置effect_descriptor_t->flags来声明的。

在DeviceEffectProxy::checkPort()函数中,判断待绑定的音效算法如果包含EFFECT_FLAG_HW_ACC_TUNNEL flag,就会新创建一个EffectModule对象,将其保存到自己的DeviceEffectProxy::mHalEffect变量中。

下面我们看看AudioEffect.cpp->setEnabled(true)函数做了什么?

AudioPolicyEffects.cpp->initDefaultDeviceEffects()

    |->AudioEffect.cpp->setEnabled(true)

        |->Effects.cpp->AudioFlinger::EffectHandle::enable()//对应的是DeviceEffectProxy

            |->Effects.cpp->AudioFlinger::DeviceEffectProxy::setEnabled()

                |->Effects.cpp->AudioFlinger::EffectBase::setEnabled()//执行的是DeviceEffectProxy对象的方法

                    |->Effects.cpp->AudioFlinger::EffectBase::setEnabled_l()//mState = STARTING,设置的是DeviceEffectProxy对象状态

                    |->Effects.cpp->AudioFlinger::DeviceEffectProxy::ProxyCallback::onEffectEnable(sp<EffectBase>& effectBase)//传入的是DeviceEffectProxy对象,所以直接返回null

                |->Effects.cpp->AudioFlinger::EffectHandle::enable()//执行的DeviceEffectProxy的mEffectHandles集合中保存的EffectHandle对象,对应的是EffectModule对象

                    |->Effects.cpp->AudioFlinger::EffectBase::setEnabled()//执行的是EffectModule对象的方法         

                        |->Effects.cpp->AudioFlinger::EffectBase::setEnabled_l()//mState = STARTING,设置的是EffectModule对象状态

                        |->Effects.cpp->AudioFlinger::DeviceEffectProxy::ProxyCallback::onEffectEnable(sp<EffectBase>& effectBase)传入的是EffectModule对象

                            |->Effects.cpp->AudioFlinger::EffectModule::start()

                                |->Effects.cpp->AudioFlinger::EffectModule::start_l()

                                    |->EffectHalHidl.cpp->command(EFFECT_CMD_ENABLE)//通知EffectHAL层enable

                                    |->Effects.cpp->AudioFlinger::EffectModule::addEffectToHal_l()//只有EFFECT_FLAG_TYPE_PRE_PROC或EFFECT_FLAG_TYPE_POST_PROC才执行下面的代码

                                        |->Effects.cpp->AudioFlinger::DeviceEffectProxy::ProxyCallback::addEffectToHal()

                                            |->Effects.cpp->AudioFlinger::DeviceEffectProxy::addEffectToHal() 

                                                |->DeviceEffectManager.h->DeviceEffectManagerCallback::addEffectToHal()

                                                    |->DeviceEffectManager.h->DeviceEffectManager::addEffectToHal()

                                                        |->AudioFlinger.cpp->addEffectToHal()

                                                            |->DeviceHalHidl.cpp->addDeviceEffect()

                                                                |->Device.cpp->addDeviceEffect()//Binder跨进程调用

                                                                    |->EffectMap.cpp->EffectMap::getInstance().get(effectId)//源码在/hardware/interfaces/audio/common/all-versions/default/目录

                                                                    |->audio.h->audio_hw_device_t.add_device_effect()       

从上面这份代码调用流程可以看出,Effects.cpp文件中Effect和Callback之间的嵌套关系还是挺深的,最终通过层层调用,终于调用到了audio.h->audio_hw_device_t.add_device_effect(audio_port_handle_t device, effect_handle_t effect)接口。成功将此音效算法的effect_handle_t 句柄传递给DeviceHAL的audio_hw_device_t进行关联绑定。


http://www.kler.cn/a/614119.html

相关文章:

  • 极速全场景 MPP数据库starrocks介绍
  • scss基础用法
  • 国内GitHub镜像源全解析:加速访问与替代方案指南
  • 【6】VS Code 新建上位机项目---项目分层
  • CES Asia 2025:科技展会体验再进化
  • Opencv 图像读取与保存问题
  • # 动态规划解决单词拆分问题详解
  • Compose笔记(十三)--事件总线
  • 【企业网络安全防护】一体化管控平台:企业办公效率与安全兼得!
  • 【工具使用-编译器】VScode(Ubuntu)使用
  • 前端一些你不了解的知识点?
  • 【DeepSeek大语言模型】基于DeepSeek和Python的高光谱遥感从数据到智能决策全流程实现与城市、植被、水体、地质、土壤五维一体应用
  • Pyglet、Panda3D 和 Pygame对比
  • 基于vue框架的智能点餐系统5tjmh(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • WEB安全--SQL注入--SQL注入的危害
  • 【MySQL基础-14.1】MySQL方言语法解析:INSERT INTO 表名 SET 字段=值
  • C/C++后端开发面经
  • Vue-admin-template安装教程
  • 如何高效利用 Postman Mock Server? 模拟 API 响应,加速开发
  • LeetCode算法题(Go语言实现)_14