【Android 高性能音频】AAudio 音频流 数据回调细节 ( 数据回调函数优先级 | 数据回调函数 | 采样率 | 采样数 | 缓冲区调整 | 线程不安全 )

简介: 【Android 高性能音频】AAudio 音频流 数据回调细节 ( 数据回调函数优先级 | 数据回调函数 | 采样率 | 采样数 | 缓冲区调整 | 线程不安全 )

文章目录

I . 数据回调函数优先级

II . 数据回调函数 相关内容

III . 采样率 处理细节

IV . 数据回调函数 每次 采样个数 numFrames

V . 数据回调函数 缓冲区 ( AAudio 内部缓冲区 ) 调整

VI . AAudio 音频系统的线程安全性分析



I . 数据回调函数优先级


1 . 普通线程操作 : 从普通线程中读写 AAudio 音频流的 音频数据 , 普通线程的优先级比较低 , 容易被抢占 , 或者遇到资源抖动 , 对需要连续性能的音频流操作造成干扰 , 出现卡顿 电流 等情况 ;



2 . 增加 AAudio 内部缓冲区 : 解决上述音频干扰的方案就是 增加 AAudio 音频流的内部缓冲区 , 这个缓冲区在上一篇博客中有详细介绍 , 该缓冲区是维护在音频设备 , 增加该缓冲区大小会提高整体 AAudio 系统采样播放的容错率 , 采样足够多 , 即使某一时刻出现了采样不足的情况 , 也能掩盖过去 , 不会出现卡顿电流等情况 , 让用户无法发现 , 但是这样音频的延迟会增大 ;


缓冲区相关细节 : 【Android 高性能音频】AAudio 音频流 缓冲区 简介 ( AAudio 音频流内部缓冲区 | 缓冲区帧容量 | 缓冲区帧大小 | 音频数据读写缓冲区 )



3 . 低延迟推荐方案 : AAudio 音频流 提供了一个 异步的 数据回调函数 AAudioStream_dataCallback , 该函数运行在优先级很高的线程中 , 该线程的资源不容易被抢占 , 可以提供一个较稳定的性能支持 ;


AAudio 音频流开启播放后 , 会自动回调该异步数据回调函数 , 在该函数中执行采样播放的过程 , 将采样数据写入缓冲区 , 这组数据消费完毕后 , 又会调用回调函数 , 申请新的数据 ;

数据回调函数基本工作流程 : 【Android 高性能音频】AAudio 音频流 PCM 采样 的 采样 缓冲 播放 的 连续机制 ( 数据回调机制 | 数据回调函数指针 | 实现数据回调函数 | 设置数据回调函数 )




II . 数据回调函数 相关内容


1 . 数据回调函数原型 : 由开发者实现 , 返回值 aaudio_data_callback_result_t 类型 , 参数 按照如下参数顺序实现 ;


typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames);



2 . 数据回调函数注册 : 开发者实现了 AAudioStream_dataCallback 类型的数据回调函数后 , 需要 调用 AAudioStreamBuilder_setDataCallback 函数 设置给 AAudio 音频流 , 然后 AAudio 处于 Started 状态后 , 就会立刻第一次回调该数据回调函数 ;


AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

1

数据回调函数基本工作流程 : 【Android 高性能音频】AAudio 音频流 PCM 采样 的 采样 缓冲 播放 的 连续机制 ( 数据回调机制 | 数据回调函数指针 | 实现数据回调函数 | 设置数据回调函数 )




III . 采样率 处理细节


1 . 采样率 : 每秒钟的采样个数 , 单位是 赫兹 ( Hz ) , 一般是 44100 Hz , 或 48000 Hz ;


在 Android 手机中 , 一般是 48000 Hz , 即每秒需要处理 48000 个采样 ;



2 . AAudio 中采样率处理 : 在 AAudio 音频流中 不建议设置采样率 , 一般使用默认采样率即可 , 每个音频设备都有一个最佳采样率 , 如果不设置 , 默认就按照该最佳采样率进行工作 , 如果设置错了 , 那么音频流在打开时就会失败 ;



3 . 采样率获取 : 如果不设置采样率 , 那么使用默认的采样率 , 该默认采样率通过调用 AAudioStream_getSampleRate () 方法获得 ;



4 . 采样率使用 : 获取采样率后 , 需要准备样本 , 这些样本的采样率需要转换成指定的采样率 , 才能向 AAudio 音频流中读写 , 如果采样率不对 , 播出来的声音就会出问题 ;


Android 的音频设备采样率一般是 48000 Hz , 需要将准备的读写缓冲区的音频样本数据采样率转为 48000Hz 后才能向 AAudio 音频流中读写 ;




IV . 数据回调函数 每次 采样个数 numFrames


1 . 采样个数 : 数据回调函数中有如下细节 , stream , userData , audioData 是指针类型 , 需要从外部传入到 函数中使用这些数据 , 但是唯独 numFrames 参数不是由用户指定的 , 每次的采样个数是由 AAudio 系统指定的 ;


typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames);


2 . 采样个数实际测试值 : 这个值在不同系统 , 版本 , 硬件手机上可能不同 , 但是我测试的 三星 小米 华为等手机 , 该值是 192 , 意味着 每次采集 192 帧的数据 , 每帧的样本数是 通道数 ;



3 . 采集的样本缓冲区大小 :


① 帧 : numFrames 单位是帧 ;

② 样本数 : 每帧的样本数 等于 通道数 , 如果是单声道 每帧有 1 个样本 , 如果是立体声 , 每帧有 2 个样本 ;

③ 每个样本字节数 : AAUDIO_FORMAT_PCM_I16 每个样本 2 字节 , AAUDIO_FORMAT_PCM_FLOAT 每个样本 4 字节 ;

④ 立体声 16 位整形采样 : AAudio 中每个样本都有指定的个数 , 16 位整形样本 AAUDIO_FORMAT_PCM_I16 代表 16 位采样 , 每个样本有 两个字节 , 那么需要采集的样本缓冲区大小为 n u m F r a m e s × 2 × 2 numFrames \times 2 \times 2numFrames×2×2 个样本 ;



V . 数据回调函数 缓冲区 ( AAudio 内部缓冲区 ) 调整


1 . 降低延迟 : 如果要求 AAudio 延迟尽可能低 , 需要将其内部缓冲区大小降到最低 ;



2 . 增加容错 : 缓冲区太小 , 容错空间也跟着变小 , 稍有风吹草动 , 就会出现卡顿 电流等播放异常的情况 , 这就需要增加缓冲区 ;



3 . 动态修改 : 上述两个需求相互冲突 , 就必须在二者之间找到平衡 , 在不出现播放异常的情况下 , 找到能够在当前性能下容错的最小缓冲区 , 该值要随着系统环境变化而动态修改 ;



4 . 调整缓冲区方法 : 在下面两篇博客中有调整缓冲区的细节 ;


① 【Android 高性能音频】AAudio 音频流 缓冲区 简介 ( AAudio 音频流内部缓冲区 | 缓冲区帧容量 | 缓冲区帧大小 | 音频数据读写缓冲区 )

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



VI . AAudio 音频系统的线程安全性分析


1 . 线程不安全 : AAudio 的 API 大部分都是线程不安全的 ;



2 . 线程不安全原理 : 线程安全就意味着存在线程同步机制 , 线程同步就涉及到了线程的阻塞等待机制 , 在 AAudio 系统中显然不能出现线程的阻塞 , 每秒钟回调几千次 , 一旦阻塞1毫秒 , 整个系统都无法正常运行 ; 此外线程阻塞后 , 其会被抢占甚至干扰 , 导致后续无法以高效率运行 ;



3 . 避免多线程操作 : 在调用 AAudio 时 , 尽量避免多线程操作 AAudio ;


① 等待状态改变操作 : AAudioStream_waitForStateChange() 操作会造成线程阻塞 , 禁止在不同线程中调用该方法 ;

② 读写操作 : 禁止在 不同线程中 读写同一个 AAudio 音频流 ;


4 . 线程安全的操作 :


① 获取 AAudio 配置的操作 : 除 AAudioStream_getTimestamp 方法是线程不安全的之外 , 其它的 AAudioStream_get*() 类的方法 都是线程安全的 ;

② 创建 AAudio 音频流构建器 : AAudio_createStreamBuilder() 方法是线程安全的 ;

③ 输出 AAudio 文本 : AAudio_convert*ToText() 类的方法也是线程安全的 ;


目录
相关文章
|
2月前
|
存储 前端开发 Java
【C++ 多线程 】C++并发编程:精细控制数据打印顺序的策略
【C++ 多线程 】C++并发编程:精细控制数据打印顺序的策略
45 1
|
2月前
|
缓存 安全 Java
为什么全局变量可能成为多线程环境中的安全隐患
为什么全局变量可能成为多线程环境中的安全隐患
|
4月前
|
缓存 安全 Java
高性能解决线程饥饿的利器 StampedLock
高性能解决线程饥饿的利器 StampedLock
41 1
|
4月前
|
存储 Java 数据安全/隐私保护
【JUC】ThreadLocal 如何实现数据的线程隔离?
【1月更文挑战第15天】【JUC】ThreadLocal 如何实现数据的线程隔离?ThreadLocal 导致内存泄漏问题?
|
4月前
|
安全 Java
JAVA 线程安全
【1月更文挑战第4天】JAVA 线程安全
|
3月前
|
安全 Java 开发者
丢失的8小时去哪里了?SimpleDateFormat线程不安全,多线程初始化异常解决方案
丢失的8小时去哪里了?SimpleDateFormat线程不安全,多线程初始化异常解决方案
38 0
|
11天前
|
安全 Java 调度
HashMap很美好,但线程不安全怎么办?ConcurrentHashMap告诉你答案!
HashMap很美好,但线程不安全怎么办?ConcurrentHashMap告诉你答案!
31 1
|
18天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
16 1
|
2月前
|
Java easyexcel 应用服务中间件
【二十五】springboot使用EasyExcel和线程池实现多线程导入Excel数据
【二十五】springboot使用EasyExcel和线程池实现多线程导入Excel数据
228 0
|
3月前
|
存储 安全
除了Lock对象,还有其他方法可以实现多线程安全的单例模式吗?
【2月更文挑战第5天】【2月更文挑战第12篇】除了Lock对象,还有其他方法可以实现多线程安全的单例模式吗?

热门文章

最新文章