当前位置: 首页 > news >正文

Android 之 audiotrack

一、成员

mReleased

mReleasedAudioTrack 中的一个关键状态变量,类型为 Modulo<uint32_t>(用于处理环形缓冲区的位置回绕)。它记录了 客户端已释放给音频服务器处理的音频帧数量,在流式传输(非静态缓冲区)模式下尤为重要。以下是其核心作用及变化点:

1. 初始化
  • 构造函数mReleased 初始化为 0
    mReleased = 0; // 初始无帧释放
    
2. 写入数据时更新
  • write() 方法
    当应用通过 write() 写入音频数据时,每次成功写入后调用 releaseBuffer(),增加 mReleased
3. 播放控制时的重置
  • stop()
    停止播放时,若非 offloaded 模式,重置 mReleased = 0(清空进度):
    if (!isOffloaded_l()) {mState = STATE_STOPPED;mReleased = 0;  // 重置释放计数器
    }
    
  • flush()
    清空缓冲区时,调用 flush_l() 重置状态:
    void flush_l() {mState = STATE_FLUSHED;mReleased = 0;  // 释放帧数归零
    }
    
4. 位置查询与同步
  • getPosition()
    通过 updateAndGetPosition_l() 计算服务端已消费的帧数时,会参考 mReleased
    Modulo<uint32_t> position = updateAndGetPosition_l(); // 依赖 mReleased
    
  • 位置恢复(如轨道重建)
    restoreTrack_l() 中恢复播放位置时,会从之前的 mReleased 恢复:
    if (mStaticProxy == 0) { // 流模式mPosition = mReleased; // 从释放位置恢复客户端位置
    }
    
5. 状态跟踪与调试
  • 日志输出
    stop() 时记录当前释放帧数(用于调试卡顿问题):
    ALOGD_IF(mSharedBuffer == nullptr,"%s(%d): called with %u frames delivered", __func__, mPortId, mReleased.value());
    
6.关键场景总结
操作mReleased 变化说明
初始化设为 0初始状态
成功写入数据 (write())增加写入帧数表示数据已提交给服务端
停止 (stop())非 offloaded 模式时重置为 0播放停止,进度清零
清空 (flush())重置为 0缓冲区清空,丢弃所有数据
轨道恢复从之前保存的值恢复服务崩溃后恢复播放位置
7.重点
  1. 进度同步
    mReleased 是客户端与服务端之间的“数据移交点”,确保双方对已处理数据量达成一致。
  2. 流控基础
    结合 mFrameCount(缓冲区大小)和 mPosition(服务端消费位置),实现写入阻塞(避免溢出)。
  3. 状态恢复
    在音频服务重启或轨道重建时,通过 mReleased 恢复播放进度,保证连续性。

:在静态缓冲区模式(mSharedBuffer != 0)下,mReleased 作用较弱,因为数据一次性加载,无需流式更新。

二、成员函数

releaseBuffer()

是 Android AudioTrack 中管理共享内存缓冲区的核心方法,主要用于更新客户端写入位置通知服务端有新数据可读

一、核心作用
  1. 更新环形缓冲区写指针

    • MODE_STREAM 模式下,AudioTrackAudioFlinger 通过环形缓冲区传递数据。
    • 当应用通过 write() 写入数据后,需调用 releaseBuffer() 更新缓冲区的写指针位置mReleased 变量),标记已写入的数据范围。
  2. 触发服务端数据消费

    • releaseBuffer() 内部通过 mProxy->releaseBuffer() 修改共享内存控制块(audio_track_cblk_t)的状态,通知 AudioFlingerPlaybackThread 有新数据待读取。
二、内部工作流程

以下为 releaseBuffer() 的典型执行流程(以 Android 源码为例):

void AudioTrack::releaseBuffer(const Buffer* audioBuffer) {// 1. 计算本次写入的帧数size_t stepCount = audioBuffer->size / mFrameSize;  // 2. 封装缓冲区信息Proxy::Buffer buffer;buffer.mFrameCount = stepCount;buffer.mRaw = audioBuffer->raw;// 3. 通过代理更新共享内存控制块mProxy->releaseBuffer(&buffer);
}
  1. 帧数计算
    stepCount = size / mFrameSize 确定本次写入的音频帧数量mFrameSize 由采样位宽和声道数决定)[citation:4][citation:6]。

  2. 代理操作

    • mProxyAudioTrackClientProxy(流模式)或 StaticAudioTrackClientProxy(静态模式)的实例。
    • 调用其 releaseBuffer() 会更新共享内存中的 u.mStreaming.mRear 指针(环形缓冲区的写位置),并清除 UNDERRUN 标志[citation:8]。
  3. 服务端唤醒
    AudioFlinger 的播放线程因缓冲区无数据而阻塞,写指针更新会触发其唤醒并读取新数据[citation:2][citation:5]。

三、关键参数解析
参数作用
audioBuffer->size本次写入的字节数,需为帧大小的整数倍(否则截断处理)[citation:6]。
mFrameSize单帧大小(字节)= 采样位宽(如16bit=2字节) × 声道数(如双声道=2)[citation:6]。
mProxy代理对象,封装了共享内存操作(如指针更新、状态同步)[citation:8]。
四、使用场景与注意事项
  1. 流模式(MODE_STREAM

    • 必须与 obtainBuffer() 配对使用
      AudioTrack::Buffer buffer;
      obtainBuffer(&buffer, ...);  // 获取可写空间
      memcpy(buffer.i8, data, size); // 填充数据
      releaseBuffer(&buffer);       // 提交数据
      
    • 未调用 releaseBuffer() 会导致服务端无法读取新数据,引发播放卡顿或中断
  2. 静态模式(MODE_STATIC

    • 数据一次性写入,无需多次调用 `releaseBuffer()。
    • 仅在初始化时通过 write() 提交全部数据,后续直接 play() 即可。
  3. 线程安全

    • 共享内存控制块通过原子操作和内存屏障保证多线程安全,无需额外锁。
    • 若跨线程调用,需确保 AudioTrack 状态一致(如不在 stop() 后调用。
五、与 obtainBuffer() 的关系
  1. 数据流协作
获取空闲缓冲区
更新写指针
obtainBuffer
write数据
releaseBuffer
AudioFlinger消费数据
  • obtainBuffer() 获取可写空间,返回 Buffer 结构体(含起始地址和最大容量)。
  • releaseBuffer() 提交实际写入量(若写入量小于申请量,会调整剩余空间。
  1. 环形缓冲区管理
    共享内存控制块(audio_track_cblk_t)通过以下字段协调读写:
    • mFront:服务端读位置(由 AudioFlinger 更新)。
    • mRear:客户端写位置(由 releaseBuffer() 更新)。
    • mUnderrun:标志位,缓冲区无数据时置位。
六、总结
  • 核心作用releaseBuffer() 是流模式下数据提交的终点,通过更新环形缓冲区写指针驱动播放流程。
  • 性能影响:延迟调用会导致播放卡顿;过早调用可能覆盖未读取的数据(需结合 obtainBuffer() 精确控制)。
  • 最佳实践
    write() 后立即调用 releaseBuffer()
    静态模式无需主动调用;
    避免跨线程操作时状态冲突。
http://www.dtcms.com/a/286158.html

相关文章:

  • 协作机器人操作与编程-PE系统示教编程和脚本讲解(直播回放)
  • 多模态大模型重构人机交互,全感官时代已来
  • PPIO × Lemon AI:一键解锁全流程自动化开发能力
  • Rust交叉编译自动化实战
  • 服务器内存满了怎么清理缓存?
  • 【DPDK】高性能网络测试工具Testpmd使用指南
  • ARINC818航空总线机载视频处理系统设计
  • 第一篇htmlcss详细讲解
  • 铁路基础设施无人机巡检技术及管理平台
  • 基于R、Python的Copula变量相关性分析及AI大模型应用
  • Altera Quartus:BAT批处理实现一键sof文件转换为jic文件
  • Altera Quartus:编译完成后自动生成pof文件
  • 闲庭信步使用图像验证平台加速FPGA的开发:第二十二课——图像直方图统计的FPGA实现
  • 28、鸿蒙Harmony Next开发:不依赖UI组件的全局气泡提示 (openPopup)和不依赖UI组件的全局菜单 (openMenu)、Toast
  • 开源Docmost知识库管理工具
  • Win11安装Docker,并使用Docker安装RabbitMQ
  • 智能算法优化储能系统充放电策略
  • 基于R语言piecewiseSEM结构方程模型在生态环境领域实践技术应用
  • 指定阿里镜像原理
  • 创建线程的方式有哪些?相比继承Thread类,实现Runable接口的好处是什么?
  • 线上 CPU 过高怎么排查
  • mac系统安装、启动Jenkins,创建pytest接口自动化任务
  • 基于 RocketMQ Prometheus Exporter 打造定制化 DevOps 平台
  • 力扣面试150(33/150)
  • 事务处理与AOP(web后端笔记第四期)
  • linux 脚本解释
  • 数据库防止数组字符串序列化
  • 后端参数校验
  • 20250718-FDU-HDUOJ钉耙编程一
  • 商汤将发布日日新6.5大模型及具身智能平台该咋看?