【Audio开发三】音频audio中帧frameSize ,周期大小periodsize,缓冲区buffer原理详解以及代码流程分析

一、基础概述

在分析获取最小帧数前,我们先来了解几个相关的概念。

1,帧

帧(frame):表示一个完整的声音单元,所谓的声音单元是指一个采样样本。如果是双声道,那么一个完整的声音单元就是 2 个样本,如果是 5.1 声道,那么一个完整的声音单元就是 6 个样本了。帧的大小(一个完整的声音单元的数据量)等于声道数乘以采样位宽,即 frameSize = channelCount * bytesPerSample。这里bytesPerSample就是量化编码的字节大小。无论是框架层还是内核层,都是以帧为单位去管理音频数据缓冲区的。在音频开发领域通常也会说采样点来对应帧这个概念。因为将帧的个数除以采样率就可以直接获得对应音频数据的时长。(PCM格式),又因为采样频率的倒数,就是一个采样点的时间,乘以采样点的数量就是整体音频的时长了。

2,传输延迟

传输延迟(latency):一个处理单元引入的delay。

3,周期

周期(period):Linux ALSA 把数据缓冲区划分为若干个块,dma 每传输完一个块上的数据即发出一个硬件中断,CPU 收到中断信号后,再配置 dma 去传输下一个块上的数据。一个块即是一个周期。

4,周期大小

周期大小(periodSize):即是一个数据块的帧数。

再回到传输延迟(latency),每次传输产生的延迟等于周期大小除以采样率,即 latency = periodSize / sampleRate。因为periodSize 指的是帧数,而帧大小 channelCount * bytesPerSample,所以帧总数除以采样频率就是整个传输过程消耗的时间。

5,音频重采样

音频重采样是指这样的一个过程------把一个采样率的数据转换为另一个采样率的数据。Android 原生系统上,音频硬件设备一般都工作在一个固定的采样率上(如 48 KHz),因此所有音轨数据都需要重采样到这个固定的采样率上,然后再输出。因为系统中可能存在多个音轨同时播放,而每个音轨的采样率可能是不一致的,比如在播放音乐的过程中,来了一个提示音,这时需要把音乐和提示音混音并输出到硬件设备,而音乐的采样率和提示音的采样率不一致,问题来了,如果硬件设备工作的采样率设置为音乐的采样率的话,那么提示音就会失真,因此最简单见效的解决方法是:硬件设备工作的采样率固定一个值,所有音轨在 AudioFlinger 都重采样到这个采样率上,混音后输出到硬件设备,保证所有音轨听起来都不失真。 sample、frame、period、latency 这些概念与 Linux ALSA 及硬件设备的关系非常密切。

二,Audio buffersize实现流程分析

1.参数介绍

AudioSource:采样通道,一般为MediaRecorder.AudioSource.MIC;

SampleRate:采样率,一般在8000Hz~48000Hz之间,不同的硬件设备这个值不同;

Channel:采样声道数,一般为单通道或立体声,即 AudioFormat.CHANNEL_CONFIGURATION_MONO或 AudioFormat.CHANNEL_CONFIGURATION_STEREO;

AudioFormat:采样精度,一般为8位或16位,即AudioFormat.ENCODING_16BIT或8BIT;

bufferSize:缓冲区大小:可以通过getMinBufferSize来获取;

buffer:DMA缓冲区大小

period: 周期,是每两个硬件中断之间的帧数,即传输多少个采样帧数据,DMA反馈一个中断,一般一个周期包含1024个采样帧,在HAL层中定义。

frame: 每个采样帧的大小。为一个采样点的字节数*声道数,因为对于多通道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播放出来才行,所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表达全了。

sample: 采样声道,一般为2个字节。

在Audio系统中,buffer的组成如图所示

2,Audio子系统之AudioRecord.getMinBufferSize

为了让音频数据通路能正常运转,共享FIFO必须达到最小缓冲区的大小。如果数据缓冲区分配得过小,那么播放声音会频繁遭遇 underrun,underrun 是消费者(AudioFlinger::PlaybackThread)不能及时从缓冲区拿到数据,造成的后果就是声音断续卡顿。

1、获取方法

cpp 复制代码
AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

从函数参数来看,返回值取决于采样率、音频格式、声道数这三个属性。

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

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

复制代码
    frameworks/av/media/libmedia/AudioRecord.cpp
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= size2/(声道数采样精度),注意这里需要double一下,而size是由hal层得到的,AudioSystem::getInputBufferSize()函数最终会调用到HAL层

复制代码
    frameworks/av/media/libmedia/AudioSystem.cpp
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;
}

所以实际上会通过binder到达AudioFlinger中

frameworks\av\services\audioflinger\AudioFlinger.cpp

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大小

hardware\aw\audio\tulip\audio_hw.c

cpp 复制代码
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帧数据所占字节数

c 复制代码
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,
};

3,总结:

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 = (((1024sample_rate/44100)+15)/16)16channel_countsizeof(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。

相关推荐
suki45226292612 小时前
音视频转换器 AV 接口静电保护方案
音视频
Java&Develop12 小时前
ffmpeg 切割视频失败 ffmpeg 命令参数 -vbsf 在新版本中已经被弃用,需要使用 -bsf:v 替代
ffmpeg·音视频
批量小王子13 小时前
01_通过调过api文字生成音频示例
音视频
EQ-雪梨蛋花汤18 小时前
【Part 1全景视频拍摄与制作基础】第四节|基于UE/Unity的全景视频渲染与导出
unity·游戏引擎·音视频·vr·全景视频
却道天凉_好个秋19 小时前
音视频学习(三十四):H264中的宏块
学习·音视频·宏块
却道天凉_好个秋19 小时前
音视频学习(三十三):GOP详解
学习·音视频·gop
feiyangqingyun20 小时前
推流265视频,网页如何支持显示265的webrtc
音视频·webrtc
猫头虎1 天前
最新如何在服务器中解决FFmpeg下载、安装和配置问题教程(Linux|Windows|Mac|Ubuntu)
linux·服务器·windows·ffmpeg·音视频·pip·视频编解码
慕雪华年1 天前
【SLAM】将realsense-viewer录制的rosbag视频导出成图片序列(RealSense D435)
音视频
feiyangqingyun1 天前
用纯Qt实现GB28181协议/实时视频/云台控制/预置位/录像回放和下载/事件订阅/语音对讲
qt·音视频·gb28181·qt监控国标