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.pswp.cn/news/897580.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897580.shtml
英文地址,請注明出處:http://en.pswp.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;…