zoukankan      html  css  js  c++  java
  • (一)Audio子系统之AudioRecord.getMinBufferSize

    在文章《基于Allwinner的Audio子系统分析(Android-5.1)》中已经介绍了Audio的系统架构以及应用层调用的流程,接下来,继续分析AudioRecorder方法中的getMinBufferSize的实现

      

      函数原型:

        public static int getMinBufferSize (int sampleRateInHz, int channelConfig, int audioFormat)

      作用:

        返回成功创建AudioRecord对象所需要的最小缓冲区大小

      参数:

        sampleRateInHz:默认采样率,单位Hz,这里设置为44100,44100Hz是当前唯一能保证在所有设备上工作的采样率

        channelConfig: 描述音频声道设置,这里设置为AudioFormat.CHANNEL_CONFIGURATION_MONO,CHANNEL_CONFIGURATION_MONO保证能在所有设备上工作;

        audioFormat:音频数据的采样精度,这里设置为AudioFormat.ENCODING_16BIT;

      返回值:

        返回成功创建AudioRecord对象所需要的最小缓冲区大小。 注意:这个大小并不保证在负荷下的流畅录制,应根据预期的频率来选择更高的值,AudioRecord实例在推送新数据时使用此值

        如果硬件不支持录制参数,或输入了一个无效的参数,则返回ERROR_BAD_VALUE(-2),如果硬件查询到输出属性没有实现,或最小缓冲区用byte表示,则返回ERROR(-1)

    接下来进入系统分析具体实现

      frameworks/base/media/java/android/media/AudioRecord.java

     static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
            int channelCount = 0;
            switch (channelConfig) {
            case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT //1
            case AudioFormat.CHANNEL_IN_MONO: //16
            case AudioFormat.CHANNEL_CONFIGURATION_MONO://2
                channelCount = 1;
                break;
            case AudioFormat.CHANNEL_IN_STEREO: //12
            case AudioFormat.CHANNEL_CONFIGURATION_STEREO://3
            case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK): // 16||32
                channelCount = 2;
                break;
            case AudioFormat.CHANNEL_INVALID://0
            default:
                loge("getMinBufferSize(): Invalid channel configuration.");
                return ERROR_BAD_VALUE;
            }
    
            // PCM_8BIT is not supported at the moment
            if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) {
                loge("getMinBufferSize(): Invalid audio format.");
                return ERROR_BAD_VALUE;
            }
    		
            int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
    
            if (size == 0) {
                return ERROR_BAD_VALUE;
            }
            else if (size == -1) {
                return ERROR;
            }
            else {
                return size;
            }
        }

         对音频通道与音频采样精度进行判断,单声道(MONO)时channelCount为1,立体声(STEREO)时channelCount为2,且A64上仅支持PCM_16BIT采样,其值为2,然后继续调用native函数

            frameworks/base/core/jni/android_media_AudioRecord.cpp

    static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
        jint sampleRateInHertz, jint channelCount, jint audioFormat) {
    
        ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
              sampleRateInHertz, channelCount, audioFormat);
    
        size_t frameCount = 0;
    	//从java转成jni的format类型
        audio_format_t format = audioFormatToNative(audioFormat);//AUDIO_FORMAT_PCM_16_BIT=0x1
    
    	//获取frameCount,并判断硬件是否支持
        status_t result = AudioRecord::getMinFrameCount(&frameCount,
                sampleRateInHertz,
                format,
                audio_channel_in_mask_from_count(channelCount));
    
        if (result == BAD_VALUE) {
            return 0;
        }
        if (result != NO_ERROR) {
            return -1;
        }
        return frameCount * channelCount * audio_bytes_per_sample(format);
    }

        调用服务端的函数,获取frameCount大小,最后返回了frameCount*声道数*采样精度,其中frameCount表示最小采样帧数,继续分析frameCount的计算方法

            frameworks/av/media/libmedia/AudioRecord.cpp

    status_t AudioRecord::getMinFrameCount(
            size_t* frameCount,
            uint32_t sampleRate,
            audio_format_t format,
            audio_channel_mask_t channelMask)
    {
        if (frameCount == NULL) {
            return BAD_VALUE;
        }
    	
        size_t size;
        status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
        if (status != NO_ERROR) {
            ALOGE("AudioSystem could not query the input buffer size for sampleRate %u, format %#x, "
                  "channelMask %#x; status %d", sampleRate, format, channelMask, status);
            return status;
        }
        //计算出最小的frame
        // We double the size of input buffer for ping pong use of record buffer.
        // Assumes audio_is_linear_pcm(format)
        if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *
                audio_bytes_per_sample(format))) == 0) {
            ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",
                sampleRate, format, channelMask);
            return BAD_VALUE;
        }
    
        return NO_ERROR;
    }

        此时frameCount= size*2/(声道数*采样精度),注意这里需要double一下,而size是由hal层得到的,AudioSystem::getInputBufferSize()函数最终会调用到HAL层

            frameworks/av/media/libmedia/AudioSystem.cpp

    status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
            audio_channel_mask_t channelMask, size_t* buffSize)
    {
        const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
        if (af == 0) {
            return PERMISSION_DENIED;
        }
        Mutex::Autolock _l(gLockCache);
        // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
        size_t inBuffSize = gInBuffSize;
        if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
            || (channelMask != gPrevInChannelMask)) {
            gLockCache.unlock();
            inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
            gLockCache.lock();
            if (inBuffSize == 0) {
                ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
                        sampleRate, format, channelMask);
                return BAD_VALUE;
            }
            // A benign race is possible here: we could overwrite a fresher cache entry
            // save the request params
            gPrevInSamplingRate = sampleRate;
            gPrevInFormat = format;
            gPrevInChannelMask = channelMask;
    
            gInBuffSize = inBuffSize;
        }
        *buffSize = inBuffSize;
    
        return NO_ERROR;
    }

    这里通过get_audio_flinger获取到了一个AudioFlinger对象

    const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
    {
        sp<IAudioFlinger> af;
        sp<AudioFlingerClient> afc;
        {
            Mutex::Autolock _l(gLock);
            if (gAudioFlinger == 0) {
                sp<IServiceManager> sm = defaultServiceManager();
                sp<IBinder> binder;
                do {
                    binder = sm->getService(String16("media.audio_flinger"));
                    if (binder != 0)
                        break;
                    ALOGW("AudioFlinger not published, waiting...");
                    usleep(500000); // 0.5 s
                } while (true);
                if (gAudioFlingerClient == NULL) {
                    gAudioFlingerClient = new AudioFlingerClient();
                } else {
                    if (gAudioErrorCallback) {
                        gAudioErrorCallback(NO_ERROR);
                    }
                }
                binder->linkToDeath(gAudioFlingerClient);
                gAudioFlinger = interface_cast<IAudioFlinger>(binder);
                LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);
                afc = gAudioFlingerClient;
            }
            af = gAudioFlinger;
        }
        if (afc != 0) {
            af->registerClient(afc);
        }
        return af;
    }

    然后判断是否参数是之前配置过的参数,这样做是为了防止重复多次调用getMinBufferSize导致占用硬件资源,所以当第一次调用或更新参数调用后,则调用AF中的getInputBufferSize方法获取BuffSize,而af是IAudioFlinger类型的智能指针,所以实际上会通过binder到达AudioFlinger中

    frameworksavservicesaudioflingerAudioFlinger.cpp

    size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
            audio_channel_mask_t channelMask) const
    {
        status_t ret = initCheck();
        if (ret != NO_ERROR) {
            return 0;
        }
    
        AutoMutex lock(mHardwareLock);
        mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
        audio_config_t config;
        memset(&config, 0, sizeof(config));
        config.sample_rate = sampleRate;
        config.channel_mask = channelMask;
        config.format = format;
    
        audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
        size_t size = dev->get_input_buffer_size(dev, &config);
        mHardwareStatus = AUDIO_HW_IDLE;
        return size;
    }

    把参数传递给hal层,获取buffer大小

    hardwareawaudio ulipaudio_hw.c

    static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
                                             const struct audio_config *config)
    {
        size_t size;
        int channel_count = popcount(config->channel_mask);
        if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0)
            return 0;
        return get_input_buffer_size(config->sample_rate, config->format, channel_count);
    }

    再次检查一次参数是否正确,为什么在很多函数里面都做一次检查参数呢?可能在其他的地方也调用到了这个函数,所以最好做一次检查,确保万无一失

    static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count)
    {
        size_t size;
        size_t device_rate;
    
        if (check_input_parameters(sample_rate, format, channel_count) != 0)
            return 0;
    
        /* take resampling into account and return the closest majoring
        multiple of 16 frames, as audioflinger expects audio buffers to
        be a multiple of 16 frames */
        size = (pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate;
        size = ((size + 15) / 16) * 16;
    
        return size * channel_count * sizeof(short);
    }

    这里包含一个结构体struct pcm_config,定义了一个周期包含了多少采样帧,并根据结构体的rate数据进行重采样计算,这里的rate是以MM_SAMPLING_RATE为标准,即44100,一个采样周期有1024个采样帧,然后计算出重采样之后的size

    同时audioflinger的音频buffer是16的整数倍,所以再次计算得出一个最接近16倍的整数,最后返回size*声道数*1帧数据所占字节数

    struct pcm_config pcm_config_mm_in = {
        .channels = 2,
        .rate = MM_SAMPLING_RATE,
        .period_size = 1024,
        .period_count = CAPTURE_PERIOD_COUNT,
        .format = PCM_FORMAT_S16_LE,
    };

    总结:

    minBuffSize = ((((((((pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate) + 15) / 16) * 16) * channel_count * sizeof(short)) * 2) / (audio_channel_count_from_in_mask(channelMask) * audio_bytes_per_sample(format))) * channelCount * audio_bytes_per_sample(format);

          =(((((((pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate) + 15) / 16) * 16) * channel_count * sizeof(short)) * 2)

      其中:pcm_config_mm_in.period_size=1024;pcm_config_mm_in.rate=44100;这里我们可以看到他除掉(channelCount*format),后面又乘回来了,这个是因为在AudioRecord.cpp对frameCount进行了一次校验,判断是否支持该参数的设置。

    以getMinBufferSize(44100, MONO, 16BIT);为例,即sample_rate=44100,channel_count=1, format=2,那么

    BufferSize = (((1024*sample_rate/44100)+15)/16)*16*channel_count*sizeof(short)*2 = 4096

    即最小缓冲区大小为:周期大小 *  重采样  * 采样声道数 * 2 * 采样精度所占字节数;这里的2的解释为We double the size of input buffer for ping pong use of record buffer,采样精度:PCM_8_BIT为unsigned char,PCM_16_BIT为short,PCM_32_BIT为int。

     

    由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!

  • 相关阅读:
    TPCx-BB官宣最新世界纪录,阿里巴巴计算力持续突破
    Java 编程技巧之数据结构
    从零开始入门 K8s | Kubernetes 网络概念及策略控制
    从零开始入门 K8s | 可观测性:监控与日志
    如何在 Knative 中部署 WebSocket 和 gRPC 服务?
    全球首个开放应用模型 OAM 开源
    开放应用模型(OAM):全球首个云原生应用标准定义与架构模型
    一文读懂分布式架构知识体系(内含超全核心知识大图)
    更强大的实时数仓构建能力!分析型数据库PostgreSQL 6.0新特性解读
    数论练习(1)——取余运算(快速幂)
  • 原文地址:https://www.cnblogs.com/pngcui/p/9978160.html
Copyright © 2011-2022 走看看