Android 之 audiotrack

一、成员

mReleased

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

1. 初始化
  • 构造函数mReleased 初始化为 0

    cpp 复制代码
    mReleased = 0; // 初始无帧释放
2. 写入数据时更新
  • write() 方法
    当应用通过 write() 写入音频数据时,每次成功写入后调用 releaseBuffer(),增加 mReleased
3. 播放控制时的重置
  • stop()
    停止播放时,若非 offloaded 模式,重置 mReleased = 0(清空进度):

    cpp 复制代码
    if (!isOffloaded_l()) {
        mState = STATE_STOPPED;
        mReleased = 0;  // 重置释放计数器
    }
  • flush()
    清空缓冲区时,调用 flush_l() 重置状态:

    cpp 复制代码
    void flush_l() {
        mState = STATE_FLUSHED;
        mReleased = 0;  // 释放帧数归零
    }
4. 位置查询与同步
  • getPosition()
    通过 updateAndGetPosition_l() 计算服务端已消费的帧数时,会参考 mReleased

    cpp 复制代码
    Modulo<uint32_t> position = updateAndGetPosition_l(); // 依赖 mReleased
  • 位置恢复(如轨道重建)
    restoreTrack_l() 中恢复播放位置时,会从之前的 mReleased 恢复:

    cpp 复制代码
    if (mStaticProxy == 0) { // 流模式
        mPosition = mReleased; // 从释放位置恢复客户端位置
    }
5. 状态跟踪与调试
  • 日志输出
    stop() 时记录当前释放帧数(用于调试卡顿问题):

    cpp 复制代码
    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 源码为例):

cpp 复制代码
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() 配对使用

      cpp 复制代码
      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()
    静态模式无需主动调用;
    避免跨线程操作时状态冲突。
相关推荐
inmK13 小时前
蓝奏云官方版不好用?蓝云最后一版实测:轻量化 + 不限速(避更新坑) 蓝云、蓝奏云第三方安卓版、蓝云最后一版、蓝奏云无广告管理工具、安卓网盘轻量化 APP
android·工具·网盘工具
giaoho3 小时前
Android 热点开发的相关api总结
android
咖啡の猫4 小时前
Android开发-常用布局
android·gitee
程序员老刘5 小时前
Google突然“变脸“,2026年要给全球开发者上“紧箍咒“?
android·flutter·客户端
Tans55 小时前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
雨白5 小时前
实现双向滑动的 ScalableImageView(下)
android
峥嵘life5 小时前
Android Studio新版本编译release版本apk实现
android·ide·android studio
studyForMokey8 小时前
【Android 消息机制】Handler
android
敲代码的鱼哇8 小时前
跳转原生系统设置插件 支持安卓/iOS/鸿蒙UTS组件
android·ios·harmonyos
翻滚丷大头鱼8 小时前
android View详解—动画
android