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

有的项目客户要求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);
     }
@@ -3970,7 +4023,7 @@ bool AudioFlinger::PlaybackThread::threadLoop()
                 }
 
                 memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
-                        mNormalFrameCount * (mChannelCount + mHapticChannelCount));
+                        mNormalFrameCount * (mMaxChannelCount + mHapticChannelCount));
 
                 // If we're going directly to the sink and there are haptic channels,
                 // we should adjust channels as the sample data is partially interleaved
@@ -3538,7 +3591,7 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c
         // Only one effect chain can be present in direct output thread and it uses
         // the sink buffer as input
         if (mType != DIRECT) {
    
    
-            size_t numSamples = mNormalFrameCount * (mChannelCount + mHapticChannelCount);
+            size_t numSamples = mNormalFrameCount * (mMaxChannelCount + mHapticChannelCount);
             status_t result = mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
                     numSamples * sizeof(effect_buffer_t),
                     &halInBuffer);

这个位置是为了增大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 tracks
     size_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::prepareTrac
     if (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 needed
     sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
     if (chain != 0) {
    
    
@@ -5585,7 +5672,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                 trackId,
                 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-configuration
             uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;
             uint32_t reqSampleRate = proxy->getSampleRate();

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

diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -879,6 +879,16 @@ void AudioFlinger::EffectModule::reset_l()
     mEffectInterface->command(EFFECT_CMD_RESET, 0, NULL, 0, NULL);
 }
 
+void AudioFlinger::EffectChain::updateChannelMask(audio_channel_mask_t channelMask) {
    
    
+    if (mTrackChannelMask == channelMask) return;
+
+    mTrackChannelMask = channelMask;
+    for (auto effect: mEffects) {
    
    
+        ALOGD("%s update success! sessionId %d", __func__, effect->sessionId());
+        effect->configure();
+    }
+}
+
 status_t AudioFlinger::EffectModule::configure()
 {
    
    
     ALOGVV("configure() started");
@@ -901,6 +911,11 @@ status_t AudioFlinger::EffectModule::configure()
     mConfig.inputCfg.channels = channelMask;
     mConfig.outputCfg.channels = channelMask;
 
+    mConfig.inputCfg.channels = callback->chain().promote()->mTrackChannelMask;
+    if (!audio_is_global_session(mSessionId)) {
    
    
+        mConfig.outputCfg.channels = callback->chain().promote()->mTrackChannelMask;
+    }
+
     if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
    
    
         if (mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_MONO) {
    
    
             mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
@@ -2088,6 +2103,8 @@ AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& thread,
       mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX),
       mEffectCallback(new EffectCallback(wp<EffectChain>(this), thread))
 {
    
    
+    mTrackChannelMask = AUDIO_CHANNEL_OUT_STEREO;
+
     mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
     sp<ThreadBase> p = thread.promote();
     if (p == nullptr) {
    
    
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -434,6 +434,9 @@ public:
     audio_session_t sessionId() const {
    
     return mSessionId; }
     void setSessionId(audio_session_t sessionId) {
    
     mSessionId = sessionId; }
 
+    audio_channel_mask_t mTrackChannelMask;
+    void updateChannelMask(audio_channel_mask_t channelMask);
+
     sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
     sp<EffectModule> getEffectFromId_l(int id);
     sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);

下面是对音效开关的处理,音效关的时候需要手动将双声道转成四声道

diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -137,6 +137,23 @@
 // TODO: Move these macro/inlines to a header file.
 #define max(a, b) ((a) > (b) ? (a) : (b))
 
+#define EFFECT_UUID_DAP {
    
     0x9d4921da, 0x8225, 0x4f29, 0xaefa, {
    
     0x39, 0x53, 0x7a, 0x04, 0xbc, 0xaa } };
+#define copy_frame_by_channel(dst_buffer, dst_type, src_buffer, src_type, num_frames, clamp) \
+{
    
     \
+    dst_type *dst = (dst_type *)dst_buffer; \
+    src_type *src = (src_type *)src_buffer; \
+    int32_t dst_idx = (num_frames - 1) * 4; \
+    int32_t src_idx = (num_frames - 1) * 2; \
+    for (int32_t i = num_frames - 1; i >= 0; i--) {
    
     \
+        dst[dst_idx + 3] = clamp(src[src_idx + 1]); \
+        dst[dst_idx + 2] = clamp(src[src_idx + 0]); \
+        dst[dst_idx + 1] = dst[dst_idx + 3]; \
+        dst[dst_idx + 0] = dst[dst_idx + 2]; \
+        dst_idx -= 4; \
+        src_idx -= 2; \
+    } \
+}

+bool AudioFlinger::PlaybackThread::checkDapEnabled(bool needLock)
+{
    
    
+    Vector< sp<EffectChain> > effectChains;
+    bool enabled = false;
+
+    effect_descriptor_t desc;
+    desc.uuid = EFFECT_UUID_DAP;
+
+    if (needLock) {
    
    
+        lockEffectChains_l(effectChains);
+    }
+    for (auto chain: mEffectChains) {
    
    
+        auto effect = chain->getEffectFromDesc_l(&desc);
+        if(effect != nullptr && effect->isProcessEnabled()) {
    
    
+            enabled = true;
+            break;
+        }
+    }
+    if (needLock) {
    
    
+        unlockEffectChains(effectChains);
+    }
+    return enabled;
+}
@@ -4044,8 +4097,12 @@ bool AudioFlinger::PlaybackThread::threadLoop()
                 mBalance.process((float *)mEffectBuffer, mNormalFrameCount);
             }
 
-            memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
-                    mNormalFrameCount * (mChannelCount + mHapticChannelCount));
+            if (mType != OFFLOAD && mType != DIRECT && mChannelCount == 4 && (!mIsDapEnabled /* || !checkDapEnabled(false) */)) {
    
    
+                copy_frame_by_channel(mSinkBuffer, int32_t, mEffectBuffer, float, mNormalFrameCount, clamp32_from_float);
+            } else {
    
    
+                memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
+                        mNormalFrameCount * (mChannelCount + mHapticChannelCount));
+            }
             // The sample data is partially interleaved when haptic channels exist,
             // we need to adjust channels here.
             if (mHapticChannelCount > 0) {
    
    

还有一些HAL的修改,不同平台实现不一样。需要在HAL层对除了扬声器以外的其它设备比如蓝牙耳机,有线耳机,DuplicatingThread播放,REMOTE_SUBMIX模式等等做一个统一操作,因为虽然除扬声器以外的设备都是双声道播放,但是因为修改了前面提到的AudioOutputDescriptor.cpp文件,参数不会跟着变化,所以要在HAL层write函数位置进行四转二操作。

代码修改基本就这些,真正困难的是调试,还需要大家对Audio有一定熟悉,当然遇到这类需要的人应该非常少。

未完待续

猜你喜欢

转载自blog.csdn.net/daixiang789/article/details/128025180