Android AudioFlinger(五)—— 揭开AudioMixer面纱

前言:

在 Android 音频系统中,AudioMixer 是音频框架中一个关键的组件,用于处理多路音频流的混音操作。它主要存在于音频回放路径中,是 AudioFlinger 服务的一部分。

上一节我们讲threadloop的时候,提到了一个函数prepareTracks_l,在这个函数的最后就调用了 mAudioMixer->create、mAudioMixer->setParameter去设置参数,channel、format、volume等等。

AudioMixer继承自 AudioMixerBase,当我们去看AudioMixer的构造函数的时候发现并没有做任何操作
在这里插入图片描述

那他的初始化代码在哪里呢?

走进AudioMixer:

我们看prepareTracks_l内关于mAudioMixer的调用流程就可以发现,他首先调用了create函数,然而Audiomixer内部却没有实现create接口,我们追溯到它的父类,发现在AudioMixerBase对象种定义了create接口并且实现了。

我们粗略的看下create里主要做了什么,代码多我做了删减。

status_t AudioMixerBase::create(int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
{LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);if (!isValidChannelMask(channelMask)) {ALOGE("%s invalid channelMask: %#x", __func__, channelMask);return BAD_VALUE;}if (!isValidFormat(format)) {ALOGE("%s invalid format: %#x", __func__, format);return BAD_VALUE;}auto t = preCreateTrack();{t->needs = 0;t->volume[0] = 0;...t->channelCount = audio_channel_count_from_out_mask(channelMask);t->enabled = false;t->channelMask = channelMask;t->sessionId = sessionId;t->hook = NULL;...// setBufferProvider(name, AudioBufferProvider *) is required before enable(name)t->sampleRate = mSampleRate;t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;t->mFormat = format;t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);t->mInputFrameSize = audio_bytes_per_frame(t->channelCount, t->mFormat);status_t status = postCreateTrack(t.get());if (status != OK) return status;mTracks[name] = t;return OK;}
}

可以看到除了一开始做了channel和format的判断,后面基本上就是对track的初始化,像volume、channel、format、sampleRate还有Hook的初始化。

初始化完成后就开始调用AudioMixer内部的接口了,我们依次往下看发现还有getUnreleasedFrames、setParameter、setBufferProvider、process等。
我们先看下setParameter,当属性变化的时候就会调用到这里。


void AudioMixer::setParameter(int name, int target, int param, void *value)
{LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);const std::shared_ptr<Track> &track = getTrack(name);int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));int32_t *valueBuf = reinterpret_cast<int32_t*>(value);switch (target) {case TRACK:switch (param) {case CHANNEL_MASK: {const audio_channel_mask_t trackChannelMask =static_cast<audio_channel_mask_t>(valueInt);if (setChannelMasks(name, trackChannelMask,static_cast<audio_channel_mask_t>(track->mMixerChannelMask | track->mMixerHapticChannelMask))) {ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);invalidate();}} break;case MAIN_BUFFER:if (track->mainBuffer != valueBuf) {track->mainBuffer = valueBuf;ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);if (track->mKeepContractedChannels) {track->prepareForAdjustChannels(mFrameCount);}invalidate();}break;case AUX_BUFFER:AudioMixerBase::setParameter(name, target, param, value);break;case FORMAT: {audio_format_t format = static_cast<audio_format_t>(valueInt);if (track->mFormat != format) {ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);track->mFormat = format;ALOGV("setParameter(TRACK, FORMAT, %#x)", format);track->prepareForReformat();invalidate();}} break;case MIXER_FORMAT: {audio_format_t format = static_cast<audio_format_t>(valueInt);if (track->mMixerFormat != format) {track->mMixerFormat = format;ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);if (track->mKeepContractedChannels) {track->prepareForAdjustChannels(mFrameCount);}}} break;case MIXER_CHANNEL_MASK: {const audio_channel_mask_t mixerChannelMask =static_cast<audio_channel_mask_t>(valueInt);if (setChannelMasks(name, static_cast<audio_channel_mask_t>(track->channelMask | track->mHapticChannelMask),mixerChannelMask)) {ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);invalidate();}} break;
...default:LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);}break;case RESAMPLE:case RAMP_VOLUME:case VOLUME:AudioMixerBase::setParameter(name, target, param, value);break;case TIMESTRETCH:switch (param) {case PLAYBACK_RATE: {const AudioPlaybackRate *playbackRate =reinterpret_cast<AudioPlaybackRate*>(value);
...} break;default:LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);}break;default:LOG_ALWAYS_FATAL("setParameter: bad target %d", target);}
}

函数的主要结构就是一个switch,首先通过trackId找到对应的track对象,然后去设置对应track的parameter参数,例如 CHANNEL_MASK、FORMAT、MAIN_BUFFER等。

这只是设置参数,那混音在哪里呢?我们继续往下看process

void process() {preProcess();(this->*mHook)();postProcess();
}

这里主要就是调用mHook,mHook是一个函数指针,他会根据不同的场景分别调用不同的函数。

  • process__nop:初始值
  • process__genericResampling:对两路以上的track进行重采样操作
  • process__genericNoResampling:对两路以上的track不进行重采样操作
  • process__validate:这个函数就是根据当前的不同情况将mHook指向不同的函数
  • process__oneTrack16BitsStereoNoResampling:只有一路track,16bit,立体声的时候不进行重采样
process_hook_t mHook = &AudioMixerBase::process__nop;

mHook初始化的时候指向的是process__nop

void invalidate() {mHook = &AudioMixerBase::process__validate;}

process__validate是在invalidate函数里幅值给了mHook 指针。

void AudioMixerBase::process__validate()
{// select the processing hooksmHook = &AudioMixerBase::process__nop;if (mEnabled.size() > 0) {if (resampling) {if (mOutputTemp.get() == nullptr) {mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);}if (mResampleTemp.get() == nullptr) {mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);}mHook = &AudioMixerBase::process__genericResampling;} else {// we keep temp arrays around.mHook = &AudioMixerBase::process__genericNoResampling;if (all16BitsStereoNoResample && !volumeRamp) {if (mEnabled.size() == 1) {const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];if ((t->needs & NEEDS_MUTE) == 0) {// The check prevents a muted track from acquiring a process hook.//// This is dangerous if the track is MONO as that requires// special case handling due to implicit channel duplication.// Stereo or Multichannel should actually be fine here.mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,t->useStereoVolume());}}}}}
}

这个函数首先使用while循环来遍历每一个track,然后通过 NEEDS_RESAMPLE、NEEDS_AUX、NEEDS_CHANNEL_1、NEEDS_MUTE等判断,最终得到resampling、all16BitsStereoNoResample、volumeRamp的值,然后基于这几个值来决定调用,mHook来指向哪一个函数。

至于音频流数据是如何混到一起的,我们后面章节再来进一步分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go的”ambiguous import in multiple modules”

执行“go mod tidy”报如下错误&#xff1a; go mod tidy -compat1.17 go: finding module for package github.com/gomooon/goredis go: found github.com/gomooon/goredis in github.com/gomooon/goredis v0.3.5 go: github.com/gomooon/core importsgithub.com/gomooon/gor…

从0开始的操作系统手搓教程27:下一步,实现我们的用户进程

目录 第一步&#xff1a;添加用户进程虚拟空间 准备冲向我们的特权级3&#xff08;用户特权级&#xff09; 讨论下我们创建用户线程的基本步骤 更加详细的分析代码 用户进程的视图 说一说BSS段 继续看process.c中的函数 添加用户线程激活 现在&#xff0c;我们做好了TSS…

Java线程池深度解析,从源码到面试热点

Java线程池深度解析&#xff0c;从源码到面试热点 一、线程池的核心价值与设计哲学 在开始讨论多线程编程之前&#xff0c;可以先思考一个问题&#xff1f;多线程编程的原理是什么&#xff1f; 我们知道&#xff0c;现在的CUP是多核CPU&#xff0c;假设你的机器是4核的&#x…

大数据技术在土地利用规划中的应用分析

大数据技术在土地利用规划中的应用分析 一、引言 土地利用规划是对一定区域内的土地开发、利用、整治和保护所作出的统筹安排与战略部署,对于实现土地资源的优化配置、保障社会经济的可持续发展具有关键意义。在当今数字化时代,大数据技术凭借其海量数据处理、高效信息挖掘等…

Node 使用 SSE 结合redis 推送数据(echarts 图表实时更新)

1、实时通信有哪些实现方式&#xff1f; 特性轮询&#xff08;Polling&#xff09;WebSocketSSE (Server-Sent Events)通信方向单向&#xff08;客户端 → 服务端&#xff09;双向&#xff08;客户端 ↔ 服务端&#xff09;单向&#xff08;服务端 → 客户端&#xff09;连接方…

Android Native 之 文件系统挂载

一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知&#xff0c;init进程为android系统的第一个进程&#xff0c;也是native世界的开端&#xff0c;要想让整个android世界能够稳定的运行&#xff0c;文件系统的创建和初始化是必不可少的&#xff…

Redis--Set类型

目录 一、引言 二、介绍 三、命令 1.sadd,smembers,sismember 2.spop&#xff0c;srandmember 3.smove&#xff0c;srem 4.sinter&#xff0c;sinterstore 5.sunion,sunionstore,sdiff,sdiffstore 四、内部编码 1.intset 2.hashtable 五、应用场景 1.使用Set保存用…

for...of的用法与介绍

一、定义 for...of 是 ES6&#xff08;ECMAScript 2015&#xff09;引入的一种用于 遍历可迭代对象&#xff08;Iterable&#xff09;的循环语句 二、语法 for (const item of iterable) {// 代码块 }参数&#xff1a; iterable&#xff1a;一个可迭代对象&#xff08;如数组…

Faster R-CNN原理详解以及Pytorch实现模型训练与推理

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

使用dockerfile创建镜像

1.什么是Dockerfile Dockerfile 是一个用于指导 Docker 镜像构建过程的脚本文件。它通过一系列指令来详细描述了构建镜像所需的步骤和配置细节。利用 Dockerfile&#xff0c;我们可以精确地设定容器的运行环境&#xff0c;安装必要的软件&#xff0c;复制项目文件&#xff0c;…

在CentOS系统上安装Conda的详细指南

前言 Conda 是一个开源的包管理系统和环境管理系统&#xff0c;广泛应用于数据科学和机器学习领域。本文将详细介绍如何在 CentOS 系统上安装 Conda&#xff0c;帮助您快速搭建开发环境。 准备工作 在开始安装之前&#xff0c;请确保您的 CentOS 系统已经满足以下条件&#x…

大脑宏观结构中的富集俱乐部:图论分析视角

摘要 大脑是一个高度复杂的网络。越来越多的证据支持大脑网络中一组重要脑区的关键作用&#xff0c;这些脑区通常被称为大脑的“核心”或“枢纽”区域。这些区域不仅能量消耗较高&#xff0c;而且在神经信息传递方面的效率也极高&#xff0c;因此被称为“富集俱乐部”。富集俱乐…

Redis7——进阶篇(五)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 基础篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09;Redis&#x…

Reflect.get和target[key]有何不同?

主要区别在this指向不同&#xff0c;下面输出张三还是李四?&#xff1a; const person{name:张三,get FullName(){return this.name;},};let personProxynew Proxy(person,{get(target,key){return Reflect.get(target,key)//或者return target[key]}});const p1{__proto__:pe…

rust语言match模式匹配涉及转移所有权Error Case

struct S{data:String, }//注意&#xff1a;因为String默认是移动语义&#xff0c;从而决定结构体S也是移动语义&#xff0c;可采用(1)或(2)两种方法解决编译错误&#xff1b;关键思路&#xff1a;放弃获取结构体S的字段data的所有权&#xff0c;改为借用。fn process(s_ref:&a…

光谱相机检测肉类新鲜度的原理

光谱相机通过分析肉类样本在特定波长范围内的光谱反射特性&#xff0c;结合化学与生物指标的变化规律&#xff0c;实现对其新鲜度的无损检测。其核心原理可概括为以下方面&#xff1a; 一、光谱特征与物质成分的关联性 ‌物质特异性吸收/反射‌ 不同化学成分&#xff08;如水分…

c#面试题整理9

1.遍历xml文档 2.解释一下这段 String s new String("xyz"); 这段在C#平台中&#xff0c;编译失败 3.说明一下抽象类 抽象类可以有构造函数 抽象类不能是静态和密封的类&#xff0c;密封的类表示无法继承&#xff0c;抽象类本身就不可实例化&#xff0c;加不好…

《React 属性与状态江湖:从验证到表单受控的实战探险》

属性初识 属性能解决两个大问题&#xff1a;通信和复用 props.js: import React, { Component } from react import Navbar from ./Navbarexport default class App extends Component {state {a:100}render() {return (<div><div><h2>首页</h2>&l…

Qwen/QwQ-32B 基础模型上构建agent实现ppt自动生成

关心Qwen/QwQ-32B 性能测试结果可以参考下 https://zhuanlan.zhihu.com/p/28600079208https://zhuanlan.zhihu.com/p/28600079208 官方宣传上是该模型性能比肩满血版 DeepSeek-R1&#xff08;671B&#xff09;&#xff01; 我们实现一个 使用Qwen/QwQ-32B 自动生成 PowerPoi…

Javascript基础语法详解

面向对象的语言.脚本语言,不需要编译,浏览器解释即可运行 .用于控制网页的行为.浏览器的source可以打断点调试, console输入代码可以执行 use strict指令: 在“严格模式”下运行js代码, 防止意外创建全局变量等, 提高代码安全性和执行效率. 使用: 全局严格模式&#xff1a;…