1

Android的共享输入

安卓官方文档有提到该功能,

共享音频输入:

https://developer.android.goo...

也就是在Android10的时候就支持共享音频输入了,也就是多应用可以共用录音。

介绍文字一大堆,把人搞晕了,不过其中提到:

在大多数情况下,如果新应用获取音频输入,则之前的捕获应用将继续运行,但会受到静默处理。在某些情况下,系统可以继续向这两个应用传送音频

从这句话来看,所谓的静默处理更多的是出于隐私安全的考虑,多应用是可以同时收到正常的音频的。

我们知道录音各个应用可能采样率(48K,16K...),格式(16bit,24bit,32bit...),通道数(1ch,2ch...)等都不一样,要想共享,是否需要各应用的参数保持一致?

或者可以不一样,像播放一样不同的参数的应用在AudioFlinger里有重采样?带着这个问题,我们可以研究下是咋实现的(从理论上来说,肯定是得AF重采样的,要是各参数一致就太弱鸡了,显示不出安卓的实力)。

可以先看下这篇文档:
Android Q共享音频输入:
https://blog.csdn.net/u013490...

总之,

  1. 现在多应用同时录音不会报错,会继续,只是因为隐私策略,有的应用会拿到静音数据, 可通过AudioPolicyService::updateUidStates_l() 定制你的策略;
  2. 应用可通过AudioManager.AudioRecordingCallback()回调获得属性更改,是否静音,设备,源等更改信息。

接下来我们看下AF RecordThread::threadLoop()对数据转换的处理。

注:录音一般是RecordThread也有flags有AUDIO_INPUT_FLAG_MMAP_NOIRQ用MmapCaptureThread的情况,我们以RecordThread为例

AF里录音处理流程

录音的threadLoop()和播放的都是很长的一个函数,大体的结构差不多,主要为:

  • 处理事件
  • 非Active Track的处理
  • 音效链处理
  • 从HAL读数据
  • 数据转换
// frameworks/av/services/audioflinger/Threads.cpp
AudioFlinger::RecordThread::threadLoop()
| // 处理事件
+ processConfigEvents_l();
| // 根据active pause等状态,是否要移除
+ mActiveTracks.remove(activeTrack); 
| // 音效链处理 咦,咋不是读数据后处理?
+ effectChains[i]->process_l();
| // 从HAL读数据到 mRsmpInBuffer 里
+ mSource->read(
|      (uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);
|
+ for (size_t i = 0; i < size; i++) {
+    activeTrack = activeTracks[i];
+    activeTrack->getNextBuffer(&activeTrack->mSink);
+    activeTrack->mResamplerBufferProvider->sync(&framesIn, &hasOverrun);
|    // 如果有flag AUDIO_INPUT_FLAG_DIRECT,将数据拷到 mSink.raw
+    if (activeTrack->isDirect()) { 
|      activeTrack->mResamplerBufferProvider->getNextBuffer(&buffer);
|      memcpy(activeTrack->mSink.raw, buffer.raw, buffer.frameCount * mFrameSize);
|      activeTrack->mResamplerBufferProvider->releaseBuffer(&buffer);
+    } else {
|      // 如果需要转换,将mResamplerBufferProvider的数据处理后给到 mSink.raw
+      activeTrack->mRecordBufferConverter->convert(
|        activeTrack->mSink.raw,
|        activeTrack->mResamplerBufferProvider,
|        framesOut);
+    }

对于数据转换,如果是direct就直接把数据给到activeTrack->mSink.raw了,否则就要转换下。
这里activeTrack又是mResamplerBufferProvider,又是mRecordBufferConverter的很容易让人晕,
简单说从字面上看就知道Provider就是数据的提供着,Converter就将提供者的数据按要求进行转换的。

在看convert()函数之前,我们还是看下Converter的创建。

Converter的创建

// frameworks/av/services/audioflinger/Tracks.cpp
AudioFlinger::RecordThread::RecordTrack::RecordTrack()
+ if (!isDirect())
|   // 创建 Converter
+   mRecordBufferConverter = new RecordBufferConverter(
|        thread->mChannelMask, thread->mFormat, thread->mSampleRate,
|        channelMask, format, sampleRate);
|
+ mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
|    mFrameSize, !isExternalTrack());
|
| // Buffer Provider
+ mResamplerBufferProvider = new ResamplerBufferProvider(this);

Converter最主要的就是创建Resampler,他会根据质量不同创建不同的Resampler,比如linear的, cubic的,sic的或者dynamic等类型,

默认的会创建动态中等质量的Resampler,当然厂商也可以在这实现自己的。

// frameworks/av/media/libaudioprocessing/RecordBufferConverter.cpp
RecordBufferConverter::RecordBufferConverter()
+ updateParameters()
  | // 如果采样率不一样,创建Resampler
  + if (mSrcSampleRate != mDstSampleRate) {
  +   mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_FLOAT,
  |                mSrcChannelCount, mDstSampleRate);

// 根据不同质量创建AudioResampler
// frameworks/av/media/libaudioprocessing/AudioResampler.cpp
AudioResampler* AudioResampler::create(...) {...
    // 默认的创建动态中等质量的
    if (quality == DEFAULT_QUALITY) {
        quality = DYN_MED_QUALITY;
    }
    ...
    switch (quality) {
    default:
    case LOW_QUALITY:
        ALOGV("Create linear Resampler");...
        resampler = new AudioResamplerOrder1(inChannelCount, sampleRate);
        break;
    case MED_QUALITY:
        ALOGV("Create cubic Resampler");...
        resampler = new AudioResamplerCubic(inChannelCount, sampleRate);
        break;
    case HIGH_QUALITY:
        ALOGV("Create HIGH_QUALITY sinc Resampler");...
        resampler = new AudioResamplerSinc(inChannelCount, sampleRate);
        break;
    case VERY_HIGH_QUALITY:
        ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);...
        resampler = new AudioResamplerSinc(inChannelCount, sampleRate, quality);
        break;
    case DYN_LOW_QUALITY:
    case DYN_MED_QUALITY:
    case DYN_HIGH_QUALITY:
        ALOGV("Create dynamic Resampler = %d", quality);
        if (format == AUDIO_FORMAT_PCM_FLOAT) {
            resampler = new AudioResamplerDyn<float, float, float>(inChannelCount,
                    sampleRate, quality);
        } else {
            LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
            if (quality == DYN_HIGH_QUALITY) {
                resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(inChannelCount,
                        sampleRate, quality);
            } else {
                resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(inChannelCount,
                        sampleRate, quality);
            }
        }

数据转换

回过头来看下convert()函数,也即RecordBufferConverter::convert()
分为不需要重采样的情况和需要重采样的情况。

AudioFlinger::RecordThread::threadLoop()
+ activeTrack->mRecordBufferConverter->convert()
| // frameworks/av/media/libaudioprocessing/RecordBufferConverter.cpp
| // RecordBufferConverter::convert()
| // 不需要重采样情况
+ if (mResampler == NULL) {
+   provider->getNextBuffer(&buffer);
|   // format convert to destination buffer
+   convertNoResampler(dst, buffer.raw, buffer.frameCount); // --> 见下
|
+ } else {
+   frames = mResampler->resample((int32_t*)mBuf, frames, provider); // 重采
|   // format convert to destination buffer
+   convertResampler(dst, mBuf, frames); // --> 见下
+ }

不需要重采样


对于不需要重采样,也就是采样率相同情况,就只需要根据channel和format(8bit, 16bit...)进行转换,
先进行的是ch的转换,
对2ch->1ch这种情况,会两声道相加再x0.5,也就是2声道取平均组成新的1ch
对于1ch->2ch的,赋值给左右声道就行,也就是这两声道的值都一样的。

RecordBufferConverter::convertNoResampler()
| // do we need to do legacy upmix and downmix?
+ if (mIsLegacyUpmix || mIsLegacyDownmix) { // 现在还是用的老式方法
|   if (mIsLegacyUpmix) {
|     // 上混,1ch -> 2ch,直接赋值
+     upmix_to_stereo_float_from_mono_float()
+     | // /primitives.c
|     + dst[0] = temp;
|     + dst[1] = temp;
|   } else /*mIsLegacyDownmix */ {
|     // 下混, 2ch->1ch, 取平均
+     downmix_to_mono_float_from_stereo_float()
|     + *dst++ = (src[0] + src[1]) * 0.5
|   }
|
+   memcpy_by_audio_format() // format转换
|   return;
+ }
| // 新的方法按index转换channel
+ if (mSrcChannelMask != mDstChannelMask) {...
|   memcpy_by_index_array(dstBuf, mDstChannelCount,
|                 src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames);
|
|  // format转换
+  memcpy_by_audio_format()

之后进行格式转换,其实也就是借助uint8_t, int16_t, int32_t 进行一些处理,有兴趣的可以仔细的研究下,特别是24bit的相互转换。

这里只贴下目标格式为16bit的代码。

// system/media/audio_utils/format.c
void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
        const void *src, audio_format_t src_format, size_t count)
{...
    switch (dst_format) {
    case AUDIO_FORMAT_PCM_16_BIT:
        switch (src_format) {
        case AUDIO_FORMAT_PCM_FLOAT:
            memcpy_to_i16_from_float((int16_t*)dst, (float*)src, count);
            return;
        case AUDIO_FORMAT_PCM_8_BIT:
            memcpy_to_i16_from_u8((int16_t*)dst, (uint8_t*)src, count);
            return;
        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
            memcpy_to_i16_from_p24((int16_t*)dst, (uint8_t*)src, count);
            return;
        case AUDIO_FORMAT_PCM_32_BIT:
            memcpy_to_i16_from_i32((int16_t*)dst, (int32_t*)src, count);
            return;
        case AUDIO_FORMAT_PCM_8_24_BIT:
            memcpy_to_i16_from_q8_23((int16_t*)dst, (int32_t*)src, count);
            return;
        default:
            break;
        }

需要重采样


需要重采样会先进行mResampler->resample(),这个就是前面提到的根据质量创建的不同的Resampler,好像还是挺复杂的,一时半会儿也看不明白,以后有机会再说。

然后就进行convertResampler()转换了,需要注意的是对于上混这种情况resampler会做处理,该函数就不再处理了,用到的函数和不需要重采的差不多,就不再过多的讲了。

RecordBufferConverter::convertResampler()
+ if (mIsLegacyUpmix) {
+    ; // mono to stereo already handled by resampler
| } else if (mIsLegacyDownmix
|           || (mSrcChannelMask == mDstChannelMask && mSrcChannelCount == 1)) {
+    downmix_to_mono_float_from_stereo_float((...);
| } else if (mSrcChannelMask != mDstChannelMask) { // ch mask不一样的情况
+    if (mSrcChannelCount == 1)
+       downmix_to_mono_float_from_stereo_float(...);
|
|    // 和不需要重采的有点小区别,先进行格式转换
|    // convert to destination format (in place, OK as float is larger than other types)
|    if (mDstFormat != AUDIO_FORMAT_PCM_FLOAT)
+        memcpy_by_audio_format() // 格式转换
|        // channel convert and save to dst
|        memcpy_by_index_array() // 根据index拷贝
|        return;
| }
| // ch相同只需要格式转换就行
+ memcpy_by_audio_format()

Atom
26 声望30 粉丝

带着问题看code