【Android 高性能音频】AAudio 缓冲区控制 ( XRun | 欠载 UnderRun | 超限 OverRun | 获取缓冲区大小 | 设置缓冲区大小 )



注意 : 本文讲的是 AAudio 播放器的音频流缓冲区控制 , 可以将帧数理解成音频采样个数 ;
实际的采样帧数 , 与每帧的采样数 , 每帧的大小 是用户自己控制的 , 向 AAudio 音频流读写多少字节的采样 , 是用户自己控制的 ;


2 个缓冲区 : 播放器缓冲区 和 采样缓冲区 ;

  • ① 播放器缓冲区 : 本文讲解的是 播放器缓冲区 设置与调整 ;
  • ② 采样缓冲区 : 采样缓冲区指的是 , 一次性采集多少个字节的数据 , 写入到播放器中 ;
  • ③ 理解 : 这两个缓冲区是不同的概念 , 注意区分 ;

每帧采样数 : 该值就是通道数 , 如果是单声道 , 每帧只有一个采样 , 如果是 双声道立体声 , 每帧有 2 个采样 ;



I . AAudio 音频流 缓冲区控制



1. 判定现状 : 首先要判定当前的 AAudio 音频流是否需要调整 , 判定的依据是是否出现了 XRun , 即 欠载 ( UnderRun ) 或 超限 ( OverRun ) 的状况 ;

2. 计算缓冲区调整值 : 获取本次的 XRun 值 , 然后与上一次的进行对比 , 如果本次的 XRun 值高于上一次 , 那么增加本次缓冲区的帧数 ;

3. 代码示例 :

    //获取 欠载 或 超限 计数 , 这里是播放 , 是欠载
    // 该值只是用于判定当前是否欠载
    int32_t underrunCount = AAudioStream_getXRunCount(playStream_);

    // 获取 AAudio 音频流在不阻塞的情况下 , 可以读取 或 写入的最大帧数 ( 文档说法 感觉不对 )
    // 获取 当前缓冲区的值
    aaudio_result_t bufferSize = AAudioStream_getBufferSizeInFrames(playStream_);

    // 欠载数值是否增加
    bool hasUnderrunCountIncreased = false;

    // 是否应该改变缓冲区大小
    bool shouldChangeBufferSize = false;

    // 处理 播放完毕 数据还没来得及写入的情况
    // playStreamUnderrunCount_ 是打开音频流时的 欠载值 , 一般是 0
    // 如果当前的欠载值 大于 上一次的 欠载值
    // 将本次的欠载值 更新
    // 本次的欠载值将作为重新调整缓冲区大小的依据
    if (underrunCount > playStreamUnderrunCount_) {

        // 记录 本次的欠载值
        playStreamUnderrunCount_ = underrunCount;

        // 设置需要更新缓冲区大小
        hasUnderrunCountIncreased = true;
    }


    if (hasUnderrunCountIncreased && bufferSizeSelection_ == BUFFER_SIZE_AUTOMATIC) {

        // 用户没有设置缓冲区大小 , 此时 bufferSizeSelection_ 的值 为 BUFFER_SIZE_AUTOMATIC , 即 0

        /**
         * 这是一个调整缓冲区大小的算法 ;
         * 如果本次的 欠载 ( UnderRun ) 值 与 上一次回时的欠载值进行对比 , 本次高于上次的值 ,
         *  此时需要增加缓冲区的大小 , 增加数值为 单次写出的大小 ( Burst Size ) ;
         *  增加的 Burst Size 会防止未来出现 欠载 情形 , 同时该操作会以增加延迟为代价 ;
         *
         * 欠载 ( UnderRun ) 即 现有数据播放完毕 , 新数据还没有写入 , 出现空档 , 造成电流 ;
         *      无法提供足够的音频采样数据 ;
         */
        bufferSize += framesPerBurst_; // 缓冲区的大小增加 每次写入的帧数大小 Increase buffer size by one burst
        shouldChangeBufferSize = true;

    } else if (bufferSizeSelection_ > 0 && (bufferSizeSelection_ * framesPerBurst_) != bufferSize) {

        // 用户有设置缓冲区大小 , 并且这个大小与之前的大小不一致的情况 , 才修改缓冲区大小数值
        // 用户每次修改缓冲区大小 , 该分支代码逻辑就会执行一次

        // 如果用户修改了缓冲区大小 , 那么执行该逻辑
        bufferSize = bufferSizeSelection_ * framesPerBurst_;
        shouldChangeBufferSize = true;
    }

    //是否修改了缓冲区大小
    if (shouldChangeBufferSize) {
        LOGD("Setting buffer size to %d", bufferSize);

        //设置当前缓冲区是多少帧
        bufferSize = AAudioStream_setBufferSizeInFrames(stream, bufferSize);
        if (bufferSize > 0) {
            //更新当前类中维护的每帧缓冲区大小数值
            bufSizeInFrames_ = bufferSize;
        } else {
            LOGE("Error setting buffer size: %s", AAudio_convertResultToText(bufferSize));
        }
    }


II . AAudio 音频流 XRun ( UnderRun | OverRun )



1. XRun 概念 : XRun 是指 AAudio 音频流的 欠载 ( UnderRun ) 或 超限 ( OverRun ) ;

  • ① 欠载 ( UnderRun ) : 播放音频流时 , 如果当前现有数据已经播放完毕 , 新数据还没有来得及写入 , 此时会发生欠载情况 ;
  • ② 超限 ( OverRun ) : 录制音频流时 , 如果没有及时读取音频流数据 , 并且这些数据没有妥善保存 , 发生溢出 , 导致数据丢失 , 这种情况叫做超限 ;

2. 获取 XRun 数据 : 使用 AAudioStream_getXRunCount() 方法 , 可以获取该 XRun 数值 ;

3. AAudioStream_getXRunCount 函数原型 :

AAUDIO_API int32_t AAudioStream_getXRunCount(
  AAudioStream *stream
)

4. AAudioStream_getXRunCount 方法参数 : 传入 AAudio 音频流 指针类型 , 可以获取该音频流的 欠载 ( UnderRun ) 或 超限 ( OverRun ) 值 ;

5. 欠载 ( UnderRun ) 或 超限 ( OverRun ) 导致的问题 : 会导致出现电流问题 ;

6. 不支持 XRun 统计 的情况 : 某些设备可能不支持统计该数据 , 此时返回的 XRun 值为 0 ;



III . AAudio 音频流 当前每次读写帧数



1. AAudio 音频流 每次读写的帧数 :

  • ① 当前读写帧数查询 : 在 AAudio 音频流读写音频数据时 , 为了达到性能最佳 , 需要查询当前音频流一次性可以读写的帧数 ;
  • ② 查询方法 : 为了达到流读写的最佳性能 , 可以使用 AAudioStream_getFramesPerBurst 方法 , 查询该数值 ;
  • ③ 帧数调整 : 应用中可以向 AAudio 音频流 读写 不同帧数的音频采样数据 , 但是为了避免 欠载 ( UnderRun ) 或 超限 ( OverRun ) , 我们可以增加该值 , 防止出现 数据不足 或 数据溢出 的情况 ;
  • ④ 帧数变动后果 : 实际音频设备一次性读写数据量可能与该帧数不匹配 ; 对于某些音频设备 , 该 Brust ( 一次性读写数据量 ) 大小可以动态改变 ; 该操作可能会增大音频的延迟 ;
  • ⑤ 每帧采样数 : 该值就是通道数 , 如果是单声道 , 每帧只有一个采样 , 如果是 双声道立体声 , 每帧有 2 个采样 ;

2. 获取每次读写帧数的函数 :

  • ① 函数原型 :
AAUDIO_API int32_t AAudioStream_getFramesPerBurst(
  AAudioStream *stream
)
  • ② 方法简介 : 该方法 传入 AAudioStream 指针类型参数 ( 代表 AAudio 音频流 ) , 可以获取该 AAudio 音频流每次读写的帧数 ;
  • ③ 代码示例 :
            // 获取每次写入的帧数
            framesPerBurst_ = AAudioStream_getFramesPerBurst(playStream_);


IV . AAudio 音频流 获取最大帧数



AAudio 音频流 获取最大帧数 : 调用 AAudioStream_getBufferSizeInFrames() 方法 , 可以获取 AAudio 音频流在不阻塞的情况下 , 可以读取 或 写入的最大帧数 ;

  • ① 方法原型 :
AAUDIO_API int32_t AAudioStream_getBufferSizeInFrames(
  AAudioStream *stream
)
  • ② 代码示例 :
    // 获取 AAudio 音频流在不阻塞的情况下 , 可以读取 或 写入的最大帧数 
    aaudio_result_t bufferSize = AAudioStream_getBufferSizeInFrames(playStream_);


V . AAudio 音频流 设置缓冲区大小



1. 函数作用 : 在音频流播放时 , 有可能会产生阻塞 , 即 采样播放完毕 , 新采样还没到达 , 该函数可以 通过 改变 缓冲区大小阈值 , 调整 缓冲区的延迟 , 即 如果出现 阻塞 , 可以增大该缓冲区大小 ( 帧数 ) ;

2. 结合 XRun 值使用 : 通过 AAudioStream_getXRunCount() 方法 , 可以获取 欠载 ( UnderRun ) 或 超限 ( OverRun ) 的值 , 根据该 XRun 值进行缓冲区大小的调整 , 达到为每个音频设备设置合适的延迟的目的 ;

3. 可设置的最大值 : 通过 AAudioStream_getBufferCapacityInFrames() 函数可以获取 缓冲区可设置的最大帧数 , 设置帧数时 , 不能超过该数值 ;

4. 查看当前缓冲区大小 : 调用 AAudioStream_getBufferSizeInFrames() 方法 , 可以查看当前的缓冲区帧数 ;

文档中的说法是 : 获取 AAudio 音频流在不阻塞的情况下 , 可以读取 或 写入的最大帧数 , 理解不通 ;

5. AAudioStream_setBufferSizeInFrames 函数简介 :

  • ① 函数原型 : numFrames 是设置的新的缓冲区帧数 , stream 代表 AAudio 音频流指针 ;
AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(
  AAudioStream *stream,
  int32_t numFrames
)
  • ② 代码示例 :
        //设置当前缓冲区是多少帧
        bufferSize = AAudioStream_setBufferSizeInFrames(stream, bufferSize);

每帧采样数 : 该值就是通道数 , 如果是单声道 , 每帧只有一个采样 , 如果是 双声道立体声 , 每帧有 2 个采样 ;

发布了252 篇原创文章 · 获赞 1013 · 访问量 168万+

猜你喜欢

转载自blog.csdn.net/han1202012/article/details/103073687