有的项目客户要求speaker播放的时候四个喇叭播放的声音呈现在不同的方位,有的算法有这个能力,但Android输出限制最多只有两声道,遇到类似需求的应该知道我想表达的是什么。一般的做法是在hal层或者kernel某个地方write的时候直接将音频流双声道copy成四声道,这种做法简单,但达不到四个角度的喇叭环绕效果,对于某些高端产品,需要宣传卖点,所以需要另一套方案,在audioflinger做些修改解除Android最多只有两声道输出限制,并让音效按四声道参数做算法。

写这些东西的目的是因为后面这一套方案比较复杂,当时我在做的时候网上没有任何可供参考的东西,当时也是花了一个半月时间才做了初步方案,希望帮助到有类似需求的人。

首先需要配置音频策略,不懂的可以看看Android官方文档,配置音频政策,访问需要有一点技巧。简单说下,里面包含三类标签,mixports,devicePort,route,分别代表虚拟设备,物理设备和路由。我们修改devicePort,使其具备四声道输出能力,这里需要写的是最大输出能力,mixports随便写,也可不写。

diff --git a/audio_policy_config/audio_policy_configuration.xml b/audio_policy_config/audio_policy_configuration.xml
--- a/audio_policy_config/audio_policy_configuration.xml
+++ b/audio_policy_config/audio_policy_configuration.xml
@@ -136,9 +132,7 @@<!-- Output devices declaration  i.e. Sink DEVICE PORT --><devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER"><profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
-                             samplingRates="44100 48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_QUAD"/></devicePort><devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink"><profile name="" format="AUDIO_FORMAT_PCM_32_BIT"

需要注意一点,当时我们做这个的时候把mixports里的fast和deep_buffer删掉了,这么做一是项目需要,二是可以少解决一些问题。上面说的mixports不写,就需要在下面这个位置做些修改,这里做可以灵活一点。

--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -534,8 +534,11 @@ status_t SwAudioOutputDescriptor::open(const audio_config_t *config,mFlags = (audio_output_flags_t)(mFlags | flags);-    ALOGV("opening output for device %s profile %p name %s",
-          mDevices.toString().c_str(), mProfile.get(), mProfile->getName().c_str());
+    if (device->type() == AUDIO_DEVICE_OUT_SPEAKER && mFlags == AUDIO_OUTPUT_FLAG_PRIMARY) {+        lConfig.channel_mask = AUDIO_CHANNEL_OUT_QUAD;
+    }
+    ALOGD("opening output for device %s profile %p name %s channel mask %#x flags %#x",
+          mDevices.toString().c_str(), mProfile.get(), mProfile->getName().c_str(), lConfig.channel_mask, mFlags);status_t status = mClientInterface->openOutput(mProfile->getModuleHandle(),output,

补充一点知识,lConfig.channel_mask参数最后会通过AudioFlinger传到HAL层的open函数,并保存。每个物理设备都会对应一个Thread类,这个类在framework/av/services/audioflinger/Threads.cpp,每个Thread类创建时都会调用PlaybackThread::readOutputParameters_l函数,函数开头获取的几个音频参数就是从HAL层获取的,后面的修改都围绕channel mask值

@@ -2998,6 +3019,9 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()mChannelCount = audio_channel_count_from_out_mask(mChannelMask);mBalance.setChannelMask(mChannelMask);+    mMaxChannelCount = mChannelCount < audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_QUAD)
+        ? mChannelCount : audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_7POINT1);
+// Get actual HAL format.status_t result = mOutput->stream->getAudioProperties(nullptr, nullptr, &mHALFormat);LOG_ALWAYS_FATAL_IF(result != OK, "Error when retrieving output stream format: %d", result);@@ -3121,7 +3145,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()mMixerBuffer = NULL;if (mMixerBufferEnabled) {mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // no longer valid: AUDIO_FORMAT_PCM_16_BIT.
-        mMixerBufferSize = mNormalFrameCount * mChannelCount
+        mMixerBufferSize = mNormalFrameCount * mMaxChannelCount* audio_bytes_per_sample(mMixerBufferFormat);(void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);}@@ -3129,7 +3153,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()mEffectBuffer = NULL;if (mEffectBufferEnabled) {mEffectBufferFormat = EFFECT_BUFFER_FORMAT;
-        mEffectBufferSize = mNormalFrameCount * mChannelCount
+        mEffectBufferSize = mNormalFrameCount * mMaxChannelCount* audio_bytes_per_sample(mEffectBufferFormat);(void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize);}

这个位置是为了增大buffer,整个播放流程设计到mixer buffer, effect buffer,sink buffer,用作输入输出,每个buffer代表音频处理某个阶段,每个阶段处理完了拷贝到下一个buffer。前两个buffer搞这么大是因为输入的声道不确定,但我们限制最大只有8声道,sink buffer是最终写到HAL的buffer,effect buffer输入可能很大,输出不会超过四声道,所以sink buffer不用特别增大,但他会按照mChannelCount参数申请大小。

@@ -5106,6 +5163,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac// counts only _active_ fast trackssize_t fastTracks = 0;uint32_t resetMask = 0; // bit mask of fast tracks that need to be reset
+    uint32_t trackChannelMask = AUDIO_CHANNEL_NONE;float masterVolume = mMasterVolume;bool masterMute = mMasterMute;
@@ -5113,6 +5171,35 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracif (masterMute) {masterVolume = 0;}
+
+    auto getChannelMask = [this](audio_channel_mask_t channelMask)->audio_channel_mask_t {+        if (channelMask == AUDIO_CHANNEL_OUT_MONO || audio_channel_count_from_out_mask(channelMask) > mMaxChannelCount) {+            return AUDIO_CHANNEL_OUT_STEREO;
+        } else if (channelMask == AUDIO_CHANNEL_OUT_QUAD) {+            return AUDIO_CHANNEL_OUT_7POINT1;
+        } else {+            return channelMask;
+        }
+    };
+
+    if (count && (mChannelMask == AUDIO_CHANNEL_OUT_STEREO || !(mIsDapEnabled = checkDapEnabled(true)))) {+        trackChannelMask = AUDIO_CHANNEL_OUT_STEREO;
+    } else {+        for (auto track: mActiveTracks) {+            audio_channel_mask_t channelMask = getChannelMask(track->channelMask());
+            trackChannelMask |= channelMask;
+
+            //ALOGD("%s, mActiveTrackCount %zu, activeTrack->channelMask %#x, trackChannelMask %#x, mChannelMask %#x, deviceType %#x, id %d, this %p, sessionid %d",
+            //        __func__, count, track->channelMask(), trackChannelMask, mChannelMask, mOutputDevice, mId, this, track->sessionId());
+        }
+    }
+
+    if(trackChannelMask != AUDIO_CHANNEL_NONE){+         for(size_t i = 0; i < mEffectChains.size(); i++){+            mEffectChains[i]->updateChannelMask(static_cast<audio_channel_mask_t>(trackChannelMask));
+         }
+     }// Delegate master volume control to effect in output mix effect chain if neededsp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);if (chain != 0) {@@ -5585,7 +5672,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTractrackId,AudioMixer::TRACK,AudioMixer::MIXER_CHANNEL_MASK,
-                (void *)(uintptr_t)(mChannelMask | mHapticChannelMask));
+                (void *)(uintptr_t)(trackChannelMask | mHapticChannelMask));// limit track sample rate to 2 x output sample rate, which changes at re-configurationuint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;uint32_t reqSampleRate = proxy->getSampleRate();

这里修改的目的是为了得到一个正确的trackChannelMask,然后分别传到EffectModule和AudioMixer。

未完待续

音视频开发之基于某三方音效的Android native层四声道音频输出相关推荐

  1. JavaCV音视频开发宝典:使用JavaCV采集windows系统声音并录制成mp3音频文件(FFmpeg采集windows系统声音)

    <JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 之前已经写过很多采集windows.macos和linux本机麦克风声音的文章, ...

  2. 音视频开发(三十九):Android视频缓存之AndroidVideoCache

    目录 背景 AndroidVideoCache简单使用 实现原理 源码分析 AndroidVideoCache的不足 一.背景 播放音视频时,播放器数据的请求是由播放器内部发起的,我们只是提供了一个u ...

  3. 2022全网最详细的音视频开发学习路线,零基础到项目实战,从小白到音视频专家

    前言 音视频的历史与前景在这里就不介绍了,小编之前的文章里面都有讲到. 行业现状分析 核心竞争力:定义音视频是程序届的皇冠,掌握音视频意味着拿到通往未来的船票,不用担心会被其他人替代.音视频是有门槛的 ...

  4. Android 音视频开发相关知识

    音视频开发技能 要在Android上进行音视频开发,需要掌握以下技能: Android开发:首先,您需要掌握Android应用程序的基本开发技能,包括Java或Kotlin编程语言,Android S ...

  5. 23最新《Android音视频开发进阶指南》,音视频开发者速领

    作为Android开发程序员,我们时刻站在互联网的前端,而音视频作为现在乃至未来几年一个强劲的风口,吸引了许多程序员的关注. 那么音视频开发的行业现状究竟如何呢?我们又该怎样入门呢?请看下文: 音视频 ...

  6. JavaCV音视频开发宝典:JavaCV混合屏幕录屏和系统声音录制mp4视频文件(windows桌面屏幕和系统声音混合录制)

    <JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 之前已经写过dshow方式采集摄像头画面.麦克风.系统声音和桌面屏幕画面. 之前 ...

  7. 《Android 音视频开发》《 Android 进阶解密》 书籍赠送

    今年听了一年的 "Android不行了..." 之类的谣言,这不都年底了嘛,也没看见哪个公司停止了安卓开发需求. 最近又出现了很多关于"互联网寒冬..."之类的 ...

  8. 音视频开发之旅(32)-音视频学习资料

    目录 为什么要学习音视频? 如何学习系统性音视频? 音视频相关的资料 学习实践的输出文章分类聚合 收获 最近有朋友问想学习音视频,应该怎么学,有什么资料吗? 这个问题也困扰我很久,几年前就想开始音视频 ...

  9. 音视频人才的需求从小众变成了大众?一名合格的音视频开发人员,少则30万起,多则年薪可达百万以上......

    随着5G时代的到来,音视频逐渐成为人们日常生活中的必需品. 所以,现在有大量的公司开始寻找音视频人才,一个稍好点的音视频人才现在可能会有3-4家公司抢着要.对音视频人员也是从小众的需求转向了大众. 这 ...

  10. Android 音视频开发学习思路

    Android 音视频开发这块目前的确没有比较系统的教程或者书籍,网上的博客文章也都是比较零散的.只能通过一点点的学习和积累把这块的知识串联积累起来. 初级入门篇: Android 音视频开发(一) ...

最新文章

  1. redis完整笔记总结-数据类型-事务与锁-集群-分布式锁-常见问题(缓存穿透、击穿、雪崩)
  2. 【DIY】实用节能声控温湿度计完整设计方案-详细图文视频教程
  3. 短视频Gif快手-有点意思 | 手摸手产品研究院
  4. 二级菜单--竖排---HTML
  5. 处理Xcode8输出无用的Log信息
  6. 用t430搭建虚拟服务器教程,T430完美使用VmWare
  7. JavaScript总结摘要
  8. ubuntu 下codeblocks的相关配置
  9. 【图像识别】人脸识别原理及CNN讲解
  10. html5读取加速度传感器,一文读懂加速度传感器
  11. LONG RAW转换BLOB
  12. 阿里中间件_Diamond
  13. Vue+H5 活动内容调用微信分享
  14. ArcGIS图层标注显示(将图层属性名字显示出来)
  15. 小知识系列(3):Hanoi塔(汉诺塔,河内塔)
  16. 多款新品亮相!一文看懂nova 11系列及全场景新品发布会
  17. TikTok在国内运营一个月,20万粉丝蛮简单的,绝对避免抖音国际版零播放问题
  18. 低代码,虽然有点毒瘤,但管用就好
  19. 对拉格朗日乘数法和KKT条件的简单理解(来自PRML的附录)
  20. arch安装包请求404错误的问题

热门文章

  1. [js]调用google,51ditu和mapbar的地图API
  2. windows子系统激活root权限
  3. 安全漏洞防御(8) DDOS 攻击的防范教程
  4. container 和initContainers使用
  5. 计算机 调剂 学校,考研调剂应该怎样联系学校?这三点一定要注意
  6. 看 SICP 不如先看 The Little Schemer
  7. XL4001 典型应用电路
  8. 硬件编码相关知识(H264,H265),IPB分别压缩率/H265/H264压缩率
  9. idb的安装log及解决办法
  10. 灵活无成本的ITSM系统|ServiceHot ITSOM