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

Android14音频子系统-Framework分析

文章目录

    • 1、概述
      • 1)应用怎么播放音频?
      • 2)Android Framework层音频框架
    • 2、AudioService (AS) -java
    • 3、Audioserver(as)-native
    • 4、AudioPolicyService(aps)-native
      • 1、术语概念
      • 2、AudioPolicyService的作用及形式
      • 3、audio policy策略配置文件
        • 1)配置文件类型
        • 2)audio_policy.conf
        • 3)audio_policy_configuration.xml
      • 4、APS启动代码时序图
      • 5、关键点代码分析
    • 5、AudioFlinger(af) - native启动过程分析
      • 1、术语概念
      • 2、AudioFlingerService的作用
      • 3、数据结构及代码时序图
        • 1)数据结构 - HAL层的类图
        • 2)数据结构 - AudioFlinger层 与 hal层的对接关系类图
        • 4)AudioFlinger初始化代码流程图
    • 6、AudioTrack创建过程
      • 1、AudioTrack创建过程
      • 2、选择一个output
      • 3、创建Track代码流程图
      • 4、共享内存机制
      • 5、PlaybackThread
        • 1)概述
        • 2)数据结构 - playbackThread与Track类图
        • 3)关键代码分析
      • 6、AudioMix处理流程
        • 1)概述
        • 2)关键代码分析
        • 3)如何实现混音?
      • 7、Playback音频数据的传递
    • 7、音量调节 - 综合应用
      • 1)基本概念
      • 2)讨论两种线程对音量的设置
      • 3)数据结构与重点方法
      • 4)代码时序图

1、概述

1)应用怎么播放音频?

先来看看一个APP播放多媒体音频示例程序
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.media.AudioAttributes;
import android.media.AudioManager;public class AudioPlayer {private AudioTrack audioTrack;public void initAndPlay() {int sampleRateInHz = 44100; // 音频采样率int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; // 音频通道配置int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 音频格式int minBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);// 创建 AudioTrack 对象AudioAttributes attributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build();audioTrack = new AudioTrack(attributes,sampleRateInHz,channelConfig,audioFormat,minBufferSize,AudioTrack.MODE_STREAM);// 生成示例音频数据(1秒 440Hz 的正弦波)int duration = 1; // 秒int numSamples = sampleRateInHz * duration;short[] audioData = new short[numSamples];double frequency = 440.0; // Hzdouble amplitude = 32767.0; // 最大振幅(16位PCM)for (int i = 0; i < numSamples; i++) {double angle = 2.0 * Math.PI * i * frequency / sampleRateInHz;audioData[i] = (short) (Math.sin(angle) * amplitude);}// 开始播放audioTrack.play();// 将音频数据写入 AudioTrackaudioTrack.write(audioData, 0, audioData.length);// 停止播放并释放资源audioTrack.stop();audioTrack.release();}
}

APP播放音频流只需要简单的几个步骤
new AudioTrack()
AudioTrack.play()
AudioTrack.write()

2)Android Framework层音频框架

在这里插入图片描述

1、组件和术语

1、AudioTrack : 音轨
2、AudioRecord: 录音
3、AudioFlinger : native层server
4、stream type : 声音类型
5、policy :罗列各类条件 - 优先级、选择设备
6、headset/headphone(不带麦克风)
7、APM:audio policy management 音频策略管理

2、重点分析

1)AudioTrack / AuioRecord是Framework层给APP调用的最上层接口,接下来以AudioTrack作为线索往下研究分析
2)绝大部分核心实现都在native层,java层的Service只是对native层的进一步封装,接下来重点分析native层的AudioPolicyService/AudioFlinger
3)注意音频子系统中的数据结构庞大,更不好的是 各个层级的命名不统一(估计是Google不同工程师编写),令人混乱!

2、AudioService (AS) -java

1、AudioService
android\frameworks\base\services\core\java\com\android\server\audio\AudioService.java
AudioService用于管理音频设备路由策略,即java层对AudioPolicyService封装

3、Audioserver(as)-native

android\frameworks\av\media\audioserver\main_audioserver.cpp //注意audio/media是分开的
cc_binary {name: "audioserver",shared_libs: ["packagemanager_aidl-cpp","libaaudioservice","libaudioclient","libaudioflinger","libaudiopolicyservice","libaudioprocessing",
}

4、AudioPolicyService(aps)-native

1、术语概念

深度分析:https://blog.csdn.net/yangwen123/article/details/39497375术语
1、spatializer:空间音响
2、关键点:
1、android\frameworks\av\services\audiopolicy\service\AudioPolicyService.cpp
void AudioPolicyService::onFirstRef()  //指南指针的强引用机制
{}AudioPolicyService继承RefBase(Android引用计数基类),当new sp<AudioPolicyService>时,引用计数从0变1,触发onFirstRef();C++中,构造函数和onFirstRef,有什么不同,执行优先级?
1)构造函数在创建对象时触发;
2)OnFirstRef在引用对象时触发(传参也算引用),用于延迟初始化,这样可以节省资源;
相当于将构造函数分开两个地方放,看代码时都要看!2、CPP11以后引入的显示底层类型枚举
enum enum_name1 : underlying_type {
}enum_name23、Vector / Collection机制
class HwAudioOutputCollection :public DefaultKeyedVector< audio_io_handle_t, sp<HwAudioOutputDescriptor> >
{}
DefaultKeyedVector是一个容器类,结合Vector和HashMap的特性typedef Vector<sp<IOProfile> > OutputProfileCollection;

2、AudioPolicyService的作用及形式

1、路由作用
在这里插入图片描述

1)基本作用就是将上层抛下来的数据传给哪种设备播放,及路由作用;

2)为什么单独拎出来,用Switch不就完事了?原因是Android音频播放设计得比较复杂,有较多拓扑关系,单看Stream Type的类型就够多够复杂,这样可以实现多种表现满足用户要求

在这里插入图片描述

举个例子:

stream type为VOICE CALL时,没有接耳机时,从喇叭输出;接耳机时,从耳机和喇叭同时输出;

用户使用过程中有更复杂的组合场景,当然不同设备(手机/TV…)的复杂度也是不同;

2、AudioTrack、AudioPolicyService和AudioFlinger的层级关系
在这里插入图片描述

1)AudioPolicyService充当“switch”角色,将各种STREAM_TYPE 的Track数据分发给AudioFlinger层的PlaybackThread处理;

2)一种类型STREAM_TYPE对应一个PlaybackThread线程;

3、audio policy策略配置文件

1)配置文件类型
一般放置在device目录下,比如/android/device/mediatek/mt5862/common/audio_policy/
Android下的文件策略配置文件
1)低版本(<Android 7.0)的audio_policy.conf
对应的加载函数:loadAudioPolicyConfig
2)高版本的(>Android 7.0)audio_policy_configuration.xml
对应的加载函数:AudioPolicyConfig::loadFromXml
Android 7.0后推出,能描述复杂的音频拓扑,例如电视和汽车等行业audio_policy.conf还有必要了解? 代码中还有保留这部分逻辑,需要了解识别!
2)audio_policy.conf
audio_policy.conf
一个简单的例子如下
a2dp {  //称为Module 或 HwModule 或interfaceoutputs {a2dp {sampling_rates 44100|48000channel_masks AUDIO_CHANNEL_OUT_STEREOformats AUDIO_FORMAT_PCM_16_BITdevices AUDIO_DEVICE_OUT_ALL_A2DP}}# a2dp sinkinputs {a2dp {sampling_rates 44100channel_masks AUDIO_CHANNEL_IN_STEREOformats AUDIO_FORMAT_PCM_16_BITdevices AUDIO_DEVICE_IN_BLUETOOTH_A2DP}}
}1、module 一般指一个厂商提供的一个HAL库(可以理解为一个音频系统下的子系统!),AudiopolicyService会根据这个字段找到对应的库(格式audio."module".default.so,比如audio.a2dp.default.so,那还不如直接写库的名称更为直接!),module一般包含一个output和input,也就是这个库所驱动的外设,output/input设备就比较多种多样了。
2、一个简单的例子
1)output:包含一个speaker(device)和earphone(device),他们具备同样的profile,所以放在一起,从上层来看,他们没有区别,音频数据往这个output塞,因为上层知道他们都可以处理,至于最终走哪个device,由其它条件决定;
2)input:包含一个headset和bluetooth,同理
3、文件路径
1)在线路径(板卡):/vendor/etc/audio_policy.conf
2)离线路径(跟厂商):android/device/mediatek/audio_policy

为何这样分类设计?根据产品的设计和管理来进行理解
在这里插入图片描述

3)audio_policy_configuration.xml

1、拓扑关系
在这里插入图片描述

(1)理解并区分两个概念:policy (策略/路由) 、profile(配置/属性)

2、xml对应的类图

相当复杂,了解即可,工程应用上只需配置xml

1)基础类型
在这里插入图片描述

2)HwModule/AudioPolicyConfig
在这里插入图片描述

3)AudioOutputDescripter/AudioInputDescripter
在这里插入图片描述

4、APS启动代码时序图

1)重点数据结构和方法

Policy相关的类图
在这里插入图片描述

1、AudioPolicyService统一向上提供服务接口(binder接口)

2、AudioPolicyManger负责厂家策略服务,AudioPolicyClient则负责调用AudioFlinger接口

3、MAudioPolicyManager 来自厂家提供的libaudiopolicymanagercustom.so,主要作用是调用

4、AudioSystem封装AudioFlinger接口 提供给AudioPolicyClient

7、Engine - 负责路由的选择,比如决出最适合的output

1)Engine.cpp(在高Android版本才有此模块,抽离出来 让厂商来制定路由)
2)低版本函数getDeviceForStrategy() 对应Engine模块的getOutputDevicesForAttributes()

3)几个重要函数

mEngine->getOutputDevicesForAttributes()
mEngine->getAllAttributesForProductStrategy()
mEngine->getOrderedProductStrategies()

8、小结几个重点类的关系:AudioPolicyService<–>AudioPolicyManager<–>Engine

2)初始化逻辑在这里插入图片描述

5、关键点代码分析

1、
AudioPolicyClient 是 AudioFliger 的客户端2、AudioPolicyService访问AudioFlinger需要跨进程? >> 需要,属于两个服务(但属于Binder同进程通讯,注意区分),在main_audioserver.cpp中启动
sp<IServiceManager> sm = defaultServiceManager();
sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME), afAdapter,
false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
sm->addService(String16(AudioPolicyService::getServiceName()), aps,
false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);3、audio_policy_configuration.xml 对应的数据结构在哪里解析?
android\frameworks\av\services\audiopolicy\common\managerdefinitions\src  //每一项都有对应的类
xml是什么?audio_policy_configuration.xml
/vendor/etc/audio_policy_configuration.xml4、AudioSystem.cpp均是静态方法,不需要new 即可调用
status_t AudioPolicyService::AudioPolicyClient::getAudioPolicyConfig(media::AudioPolicyConfig *config)
{sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();return af->getAudioPolicyConfig(config);
}5、AudioPolicyService中的Command
Status AudioPolicyService::onNewAudioModulesAvailable()
{mOutputCommandThread->audioModulesUpdateCommand();return Status::ok();
}6、MAudioPolicyManager 厂商部分代码的嵌入设计
1)厂家部分 封装成一个库 
1、一部分是class内容(继承AudioPolicyManager);
2、另一部分是: extern "c" function (dlopen,主要负责初始化);>> 这样设计的好处?

5、AudioFlinger(af) - native启动过程分析

1、delegate : 授权
2、如何打开devices?
AUDIO_DEVICE_OUT_SPEAKER //枚举变量为什么用线程来接收track?1、CPP中的命名空间和前向声明
namespace android {
class StreamInHalInterface;
class StreamOutHalInterface;class DeviceHalInterface : public virtual RefBase
{}
//前向声明(Forward Declaration),仅使用此类的指针或引用,无需知道类的具体结构,前向声明即可满足;解决头文件依赖(会触发对应源文件的编译)和循环依赖;2、android\frameworks\av\services\audioflinger\AudioFlinger.cpp
mDevicesFactoryHal = DevicesFactoryHalInterface::create(); //打开hal库,获取hal接口
mEffectsFactoryHal = audioflinger::EffectConfiguration::getEffectsFactoryHal(); //音频效果3、AudioFlingerService 优先于 AudioPolicyService启动;后者需要调用前者4、AudioFlingerClientAdapter?
//用于封装处理Binder IPC(aidl) 通信异常情况- AudioFlinger
static inline ::android::status_t statusTFromBinderStatus(const ::android::binder::Status &status) {return status.isOk() ? ::android::OK // check ::android::OK,: status.serviceSpecificErrorCode() // service-side error, not standard Java exception// (fromServiceSpecificError)?: status.transactionError() // a native binder transaction error (fromStatusT)?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a// standard Java exception (fromExceptionCode)
}5、Delagate代理模式-与继承功能相反
1)适合基类会变化,而子类不会变化的情况
class AudioFlingerServerAdapter : public media::BnAudioFLingerService {
public:using Status = binder::Status;class Delegate : public IAudioFling {friend class AudioFlingerServerAdapter;}explicit AudioFlingerServerAdapter(const sp<AudioFlingerServerAdapter::Delegate>& delegate)
private:sp<AudioFlingerServerAdapter::Delegate> mDelegate;
}6、通过aidl访问hal层
android\frameworks\av\media\libaudiohal\impl\DevicesFactoryHalAidl.cpp
if (std::find(deviceNames.begin(), deviceNames.end(), name) != deviceNames.end()) {if (strcmp(name, "primary") == 0) name = "default";auto serviceName = std::string(IModule::descriptor) + "/" + name;service = IModule::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str()))); //获取aidl服务ALOGE_IF(service == nullptr, "%s fromBinder %s failed", __func__, serviceName.c_str());
}7、hal
1)android\frameworks\av\media\libaudiohal\impl
2)android\frameworks\av\media\libaudiohal\include\media\audiohal
充斥着大量的跨进程封装(parcel/错误处理/返回值处理等等),使用factory模式 兼容hidl和aidl

1、术语概念

2、AudioFlingerService的作用

3、数据结构及代码时序图

1)数据结构 - HAL层的类图

在这里插入图片描述

1、为什么如此复杂?为了适应不同系统版本共存的HAL/HIDL/AIDL方式

2、以AIDL为例:DevicesFactoryHalAidl包含DeviceHalAidl (hal接口)

2)数据结构 - AudioFlinger层 与 hal层的对接关系类图

在这里插入图片描述

1)AudioHwDevice对应的是audio policy策略文件中的module,在AudioPolicyService中又被称为HwModule!

4)AudioFlinger初始化代码流程图

在这里插入图片描述

1、PlaybackTread是AudioFlinger模块中的组成,在初始化时创建,循环地将音频从共享内存中稍加处理后送到硬件中;

2、创建MixerThread(由audio_policy_configuration.xml决定创建哪种类型的Thread)后,上层即可从mPlaybackTrreads选择对应的Thread丢数据;

3、应用给底层丢数据的过程如下:
在这里插入图片描述

一种类型STREAM_TYPE对应一个PlaybackThread线程;

4、audio_io_handle_t

audio_io_handle_t是一个全局唯一的整形值,作为键值对的索引值key,在output中对应PlaybackThread
/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */
typedef int audio_io_handle_t;audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1,audio_io_handle_t output2)
{
...mPlaybackThreads.add(id, thread);// notify client processes of the new output creationthread->ioConfigChanged(AUDIO_OUTPUT_OPENED);return id;
}

6、AudioTrack创建过程

1、playback: 重放
2、offload : 音频数据不解码,即软件不解码,硬件负责解码
3、time-stretch : 时间拉伸,对应音频的加速
4、fallback : 回放
5、MSD :multi-Stream Decoder
6、underrun : 生产速度 赶不上 消费速度10、一个音轨Track对应一个共享内存,一个APP只能对应一个Track;
AudioFlinger会轮训多个Track,送进mixer,混合后给到output->device;1、
android\frameworks\av\media\libaudioclient\AudioSystem.cpp2、
android\frameworks\av\media\libaudioclient\AudioTrack.cpp
const sp<media::IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); //获取AudioFliger接口

1、AudioTrack创建过程

先来看从java层如何使用AudioTrack

1、播放音频流几个步骤
new AudioTrack
AudioTrack.set()
AudioTrack.start()2、调用native方法创建Track
android\frameworks\base\media\java\android\media\AudioTrack.java
int initResult = native_setup();3、写共享内存
public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {return write(audioData, offsetInBytes, sizeInBytes, WRITE_BLOCKING);
}final int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,writeMode == WRITE_BLOCKING);

2、选择一个output

1、基础数据结构
在这里插入图片描述

1、audio_flags_mask_t
typedef enum {AUDIO_FLAG_NONE                       = 0x0,AUDIO_FLAG_AUDIBILITY_ENFORCED        = 0x1,  //不能被静音,比如防止偷拍AUDIO_FLAG_SECURE                     = 0x2,  //音频加密类型,受数字版权管理(DRM)AUDIO_FLAG_SCO                        = 0x4,  //SCO Synchronous Connection-Oriented 蓝牙AUDIO_FLAG_BEACON                     = 0x8,
}2、
enum legacy_strategy {STRATEGY_NONE = -1,STRATEGY_MEDIA,  //媒体播放的音频策略STRATEGY_PHONE, //通话的音频策略STRATEGY_SONIFICATION,  //系统提示音STRATEGY_SONIFICATION_RESPECTFUL, //尊重用户环境的系统提示音STRATEGY_DTMF, //双音多频(DTML), 蓝牙设备STRATEGY_ENFORCED_AUDIBLE, //强制可听STRATEGY_TRANSMITTED_THROUGH_SPEAKER, //通过speakerSTRATEGY_ACCESSIBILITY, //辅助功能STRATEGY_REROUTING, //音频重路由STRATEGY_CALL_ASSISTANT, //通话辅助
};stream_type(music-细分) -> strategy(media-粗分)

audio_stream_type_t、audio_attributes_t、Strategy、Device的关联关系
在这里插入图片描述

2、决出一个output,选择大致过程简述:

1)stream type->attribute(属性)->stratege(根据属性分组/类别)->device(此类别所对应的设备)->output(支持此设备的output,有一定的优先级)2)AudioPolicyManager根据stream type获取合适的output(由Engine模块来负责决策)
audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream)
{DeviceVector devices = mEngine->getOutputDevicesForStream(stream, false /*fromCache*/);SortedVector<audio_io_handle_t> outputs = getOutputsForDevices(devices, mOutputs);const audio_io_handle_t output = selectOutput(outputs);ALOGV("getOutput() stream %d selected devices %s, output %d", stream,devices.toString().c_str(), output);return output;
}

3、创建Track代码流程图

在这里插入图片描述

4、共享内存机制

1)基本数据结构(无非就是将实体往上封装 加点管理接口)
在这里插入图片描述

2)共享内存(cbk : control block)示意图
在这里插入图片描述

3)重点部分

1、基本数据结构定义的头文件
android\frameworks\av\include\private\media\AudioTrackShared.h2、通过共享内存提供音频数据的两种方式
1)一次性提供方式(MODE_STATIC,也称为静态模式),适合音频数据很小的场合,比如系统铃声,警告声;流式提供(MODE_STREAM,也称流模式),比如音乐;
2)MODE_STATIC、MODE_STREAM对应的共享内存创建者不同
android\frameworks\base\core\jni\android_media_AudioTrack.cpp
static jint android_media_AudioTrack_setup(...,jint memoryMode, ...)
switch (memoryMode) {case MODE_STREAM:  //APP层不创建内容,由AudioFlingerService负责创建,需要复杂的管理机制支持(环形buffer),底层控制实现status = lpTrack->set();break;case MODE_STATIC: //APP层创建共享内存,因为是一次性任务,不需要管理,直接APP层创建好传给底层即可{// AudioTrack is using shared memoryconst auto iMem = allocSharedMem(buffSizeInBytes);if (iMem == nullptr) {ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");goto native_init_failure;}status = lpTrack->set(..,iMem,..);break;}
}
3)AudioFlingerService创建共享内存
1、共享内存相关的变量
android\frameworks\av\services\audioflinger\TrackBase.h
class TrackBase : public ExtendedAudioBufferProvider, public RefBase {sp<IMemory>         mCblkMemory;audio_track_cblk_t* mCblk;sp<IMemory>         mBufferMemory;  // currently non-0 for fast RecordTrack onlyvoid*               mBuffer;    // start of track buffer, typically in shared memory// except for OutputTrack when it is in local memorysize_t              mBufferSize; // size of mBuffer in bytes
}2、分配内存
android\frameworks\av\services\audioflinger\Tracks.cpp
AudioFlinger::ThreadBase::TrackBase::TrackBase(){if (client != 0) {mCblkMemory = client->allocator().allocate(mediautils::NamedAllocRequest{{size},std::string("Track ID: ").append(std::to_string(mId))});} else {mCblk = (audio_track_cblk_t *) malloc(size);}
}3、Tracks.cpp : AudioTrackServerProxy和StaticAudioTrackServerProxy都是负责管理共享内存的类
AudioFlinger::PlaybackThread::Track::Track(){if (sharedBuffer == 0) {mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize, !isExternalTrack(), sampleRate); //对应MODE_STATIC} else {mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize, sampleRate); //对应MODE_STATIC}
}4、
AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
}

4、共享内存采用的是Ashmem匿名共享机制
在这里插入图片描述

5、PlaybackThread

1)概述
疑问:
1、音频数据格式 谁来解析处理?编解码模块(Codec)负责处理
2、Mix 原理是什么?1、PlaybackThread的作用及整体的处理音频数据流程
1)PlaybackTread是AudioFlinger模块中的组成,在初始化时创建,循环地将音频从共享内存中稍加处理后送到硬件中;
2)整理流程:轮询共享内存 -> 取出处理 -> 送到HAL层去 -> 硬件播放
2)数据结构 - playbackThread与Track类图

在这里插入图片描述

1、audio_track_cblk_t 是共享内存 控制块,用于音频流的传送

2、PlaybackThread中对Track的管理
在这里插入图片描述

3)关键代码分析
1、Threads.cpp
AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,AudioStreamOut* output,audio_io_handle_t id,type_t type,bool systemReady,audio_config_base_t *mixerConfig):   ThreadBase(audioFlinger, id, type, systemReady, true /* isOut */),mNormalFrameCount(0), mSinkBuffer(NULL),
{ }2、整体流程
1)onFirstRef执行run
void AudioFlinger::PlaybackThread::onFirstRef()
{if (!isStreamInitialized()) {ALOGE("The stream is not open yet"); // This should not happen.}run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO);mThreadSnapshot.setTid(getTid());
}
2)进入子线程循环体
bool AudioFlinger::PlaybackThread::threadLoop()
{....for (int64_t loopCount=0; !exitPending(); ++loopCount) {// mMixerStatusIgnoringFastTracks is also updated internallymMixerStatus = prepareTracks_l(&tracksToRemove); //取出Track音频数据....if (mBytesRemaining == 0) {mCurrentWriteLength = 0;if (mMixerStatus == MIXER_TRACKS_READY) {// threadLoop_mix() sets mCurrentWriteLengththreadLoop_mix(); //对音频数据进行mix}}....if (!waitAsynCallback()) {ret = threadLoop_write(); //调用hal接口写入硬件}}....threadLoop_exit();
}3)threadLoop_write()
ssize_t AudioFlinger::MixerThread::threadLoop_write() {return PlaybackThread::threadLoop_write();
}ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{if(mNormalSink != 0) {//采用NBAIO(Non-blocking Audio I/O),即先将数据送进缓冲区,让软件进一步处理(比如mix混音),再通过非阻塞方式将数据传输到音频硬件ssize_t frameWritten = mNormalSink->write((char *)mSinkBuffer + offset, count); }else{//Direct output and offload, 即不进行音频处理,直接调用hal接口写入,比如HDMI设备bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining);}
}

6、AudioMix处理流程

1)概述

每一个MixerThread都有一个唯一对应的AudioMixer,接口如下
在这里插入图片描述

2)关键代码分析
1、调用处
android\frameworks\av\services\audioflinger\Threads.cpp
void AudioFlinger::MixerThread::threadLoop_mix()
{//mix buffers..mAudioMixer->process();
}2、Mixer的内部实现
android\frameworks\av\media\libaudioprocessing\include\media\AudioMixerBase.h
class AudioMixer : public AudioMixerBase
{//process的实现如下,调用mHook,mHooK会根据不同场景赋值,指向不同的函数void        process() {preProcess();(this->*mHook)();postProcess();}// process hook functionality - 函数指针,指向AudioMixerBase的函数using process_hook_t = void(AudioMixerBase::*)();process_hook_t mHook = &AudioMixerBase::process__nop;   // one of process__*, never nullptr//函数指针,指向TrackBase的函数using hook_t = void(TrackBase::*)(int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);hook_t      hook;void process__validate();void process__nop();void process__genericNoResampling(); //两路以上Track,不重采样void process__genericResampling(); //两路以上Track,重采样void process__oneTrack16BitsStereoNoResampling();//只有一路Track,16bit 立体声,不重采样
}注意AudioMixer和TrackBase是共用音频数据缓冲区的

mHook的变化图
在这里插入图片描述

3)如何实现混音?
1、以process__genericResampling为例
void AudioMixerBase::process__genericResampling()
{ALOGVV("process__genericResampling\n");int32_t * const outTemp = mOutputTemp.get(); // 临时buffer存放Tracksize_t numFrames = mFrameCount;for (const auto &pair : mGroups) {const auto &group = pair.second;const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]]; //应用程序提供的Track数据// clear temp buffermemset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);for (const int name : group) {const std::shared_ptr<TrackBase> &t = mTracks[name];int32_t *aux = NULL;if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {aux = t->auxBuffer;}// this is a little goofy, on the resampling case we don't// acquire/release the buffers because it's done by// the resampler.if (t->needs & NEEDS_RESAMPLE) {(t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);} else {size_t outFrames = 0;while (outFrames < numFrames) {t->buffer.frameCount = numFrames - outFrames;t->bufferProvider->getNextBuffer(&t->buffer);t->mIn = t->buffer.raw;// t->mIn == nullptr can happen if the track was flushed just after having// been enabled for mixing.if (t->mIn == nullptr) break;(t.get()->*t->hook)(outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,mResampleTemp.get() /* naked ptr */,aux != nullptr ? aux + outFrames : nullptr);outFrames += t->buffer.frameCount;t->bufferProvider->releaseBuffer(&t->buffer);}}}convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount); //转换格式,最终数据存放在t1->mainBuffer中}
}
总的来说就是将各个Track音频数据叠加起来,实现混音

7、Playback音频数据的传递

AudioTrack或AudioFlingerService创建好共享内存,APP只需要将数据写进共享内存,并通知AudioFlingerService(主要是PLaybackThread和AudioMix)进行处理即可,我们从宏观上了解交互控制部分,其它细节比较晦涩(极端buffer大小等)不好记忆,这里不展开,需要了解时再去琢磨

1)Audio Playback数据流传递如下
在这里插入图片描述

2)AudioTrack(生产者-user) 与AudioFlinger(消费者-Server)的数据交互大致如下
在这里插入图片描述

3)环形缓冲区
在这里插入图片描述

4)AudioTrack与AudioFlinger都通过obtainBuffer和releaseBuffer来获取/释放同一块缓冲区,

如何实现互斥?这两个方法使用Mutex实现互斥功能

7、音量调节 - 综合应用

1)基本概念

master volume //Android框架中的主音量,决定全局的音量
stream volume //Android框架中的流类型音量,决定局部(比如铃声/电话/音乐)的音量
stream volume alias //Android框架中的流类型音量,即对stream volume二次分类
track volume //音频流中的音量( 附属在音频数据中 )
在这里插入图片描述

无论是AudioTrack volume、stream volume, 都是单独设置.
能否通过某个变量影响到所有stream、所有AudioTrack的音量?
有!这就是 master volume, 这个值是可以直接用来控制声卡的

音量处理公式:final volume = master volume * stream volume * stream volume alias * track volume

2)讨论两种线程对音量的设置

1)哪里决定使用哪种线程驱动声卡?

audio_policy_configuration.xml
flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" maxOpenCount="2" maxActiveCount="1">

2)数据流简图
在这里插入图片描述

1、对于MixerThread

APP对音量的设置不会影响到声卡的硬件音量,而只会影响APP的音频数据的幅值(变小或放大),
这些音频数据最终被混合后传给声卡,多个APP本身的音量设置互不影响对于混音的音量处理公式
app1: 
data1_mix = data1_in * master_volume * stream1_volume * AudioTrack1_volumeapp2: 
data2_mix = data2_in * master_volume * stream2_volume * AudioTrack2_volume混合在一起:
data_mix = data1_mix + data2_mix

2、对于DirectOutputThread
同一时间里只有一个APP、只有一个AudioTrack使用它,
所以该AudioTrack的音量可以被DirectOutputThread直接用来设置硬件音量

3)数据结构与重点方法

1、基本数据结构

1、android\frameworks\av\services\audioflinger\AudioFlinger.h
struct  stream_type_t {stream_type_t():   volume(1.0f),mute(false){}float       volume;bool        mute;};2、android\frameworks\av\services\audioflinger\AudioFlinger.h
class AudioFlinger : public AudioFlingerServerAdapter::Delegate
{// member variables below are protected by mLockfloat                               mMasterVolume;bool                                mMasterMute;float                               mMasterBalance = 0.f;// end of variables protected by mLock
}3、
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{Mutex::Autolock _l(mLock);mStreamTypes[stream].volume = value;broadcast_l();
}4、
status_t AudioFlinger::setMasterVolume(float value)
{Mutex::Autolock _l(mLock);mMasterVolume = value;//设置设备的主音量AudioHwDevice *dev = mAudioHwDevs.valueAt(i);dev->hwDevice()->setMasterVolume(value);//如果声卡不支持主音量设置,在每个PlaybackThreads中设置软件"主音量"mPlaybackThreads.valueAt(i)->setMasterVolume(value);return NO_ERROR;
}

2、Mixer中对音量的控制

android\frameworks\av\media\libaudioprocessing\include\media\AudioMixerBase.h
class AudioMixerBase
{struct TrackBase {// TODO: Eventually remove legacy integer volume settingsunion {int16_t     volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)int32_t     volumeRL;};int32_t     prevVolume[MAX_NUM_VOLUMES];int32_t     volumeInc[MAX_NUM_VOLUMES];int32_t     auxInc;int32_t     prevAuxLevel;int16_t     auxLevel;       // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performancefloat          mVolume[MAX_NUM_VOLUMES];     // floating point set volumefloat          mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volumefloat          mVolumeInc[MAX_NUM_VOLUMES];  // floating point volume incrementfloat          mAuxLevel;                     // floating point set aux levelfloat          mPrevAuxLevel;                 // floating point prev aux levelfloat          mAuxInc;                       // floating point aux increment}
}
aux:指的是单声道设备

prevolume/inc/volume的关系,目的是为了平缓音量的变化,避免对用户产生不适
在这里插入图片描述

3、音量最终在哪里生效?

android\frameworks\av\services\audioflinger\Threads.cpp
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(Vector< sp<Track> > *tracksToRemove)
{
// cache the combined master volume and stream type volume for fast mixer; this// lacks any synchronization or barrier so VolumeProvider may read a stale valueconst float vh = track->getVolumeHandler()->getVolume(proxy->framesReleased()).first;volume *= vh;track->mCachedVolume = volume;gain_minifloat_packed_t vlr = proxy->getVolumeLR();float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));vlf *= volume;  //volume left finalvrf *= volume;  //volume right finaltrack->setFinalVolume(vlf, vrf);++fastTracks;
}void AudioFlinger::PlaybackThread::Track::setFinalVolume(float volumeLeft, float volumeRight)
{mFinalVolumeLeft = volumeLeft;mFinalVolumeRight = volumeRight;
}

4)代码时序图

1、

在这里插入图片描述

2、用户按下音量键先进入输入子系统,再转到音频子系统处理

1、输入子系统入口
android\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,int policyFlags) {}2、音频子系统入口
android\frameworks\base\services\core\java\com\android\server\audio\AudioService.java
public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv,@NonNull String callingPackage, @NonNull String caller) {}3、音量相关的keyevent
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
http://www.lqws.cn/news/528319.html

相关文章:

  • Python 常用正则表达式大全
  • Spark 之 QueryStage
  • [Java实战]springboot3使用JDK21虚拟线程(四十)
  • 第十三章---软件工程过程管理
  • 【LLM】位置编码
  • vscode 回退代码版本
  • SQL变量声明与赋值 分支 循环
  • 信创国产化替代中的开发语言选择分析
  • 4.2_1朴素模式匹配算法
  • 6月份最新代发考试战报:思科华为HCIP HCSE 考试通过
  • Java四种拷贝方式总结!一文扫清所有拷贝问题
  • npm run dev报错
  • 软件安装——下载安装ollama
  • leetcode 65
  • Autosar方法论
  • 力扣2311:小于等于K的最长二进制子序列
  • 【TIDB】了解,MySQL和TiDB的取舍,差异
  • postman设置接口关联,实现参数化
  • kotlin中::class.java的意义
  • Redis 为什么选用跳跃表,而不是红黑树
  • PHP基础2(流程控制,函数)
  • 【机器学习深度学习】交互式线性回归 demo
  • C语言再出发:2025年AI时代的关键语言
  • notepad++ 怎么快速给 python (nginx、shell) 文件加 # 注释
  • VUE3入门很简单(3)--- watch
  • MR30分布式 IO在物流堆垛机的应用
  • 解锁AI无限潜能!景联文科技数据产品矩阵再升级:多语言题库、海量语料、垂域代码库,全面赋能大模型训练
  • 力扣第45题-跳跃游戏2
  • 【智能记录系统Blinko】从0到1搭建个人云端笔记本:Blinko+Docker环境配置
  • JVM OutOfMemoryError原因及排查解决方案