Files
ScrcpyForAndroid/doc/scheme/低延迟音频_kotlin_side.md

3.0 KiB

低延迟音频_kotlin_side

使用“低延迟模式”标志

val audioTrack = AudioTrack.Builder()
    .setAudioAttributes(AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME) // 游戏用途延迟最低
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        // 请求低延迟硬件标志
        .setFlags(AudioAttributes.FLAG_LOW_LATENCY) 
        .build())
    .setAudioFormat(AudioFormat.Builder()
        .setEncoding(AudioFormat.ENCODING_PCM_16BIT) // TODO: 查看 scrcpy 官方仓库了解实际 PCM 参数 `B:\Git\scrcpy`
        .setSampleRate(48000) // 匹配硬件原生采样率
        .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
        .build())
    // 性能模式设置为 LOW_LATENCY
    .setBufferSizeInBytes(minBufferSize)
    .setTransferMode(AudioTrack.MODE_STREAM)
    .setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY) 
    .build()

// audioTrack.performanceMode 检查结果

匹配“原生采样率”与“缓冲区步长”

val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager

// 获取原生采样率 (通常是 48000)
val sampleRate = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)?.toInt() ?: 48000

// 获取硬件每秒处理的帧数步长 (Frames Per Burst)
val framesPerBurst = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)?.toInt() ?: 128

val minBuffer = AudioTrack.getMinBufferSize(
    sampleRate,
    AudioFormat.CHANNEL_OUT_STEREO,
    AudioFormat.ENCODING_PCM_16BIT
)

// 16bit 2 bytes
// stereo 2 channels
// 双缓冲 2 个 burst
val targetBuffer = framesPerBurst * 2 * 2 * 2

val bufferSize = max(minBuffer, targetBuffer)

使用 WRITE_NON_BLOCKING // 已用

audioTrack 启动预填充静音音频 // 没必要做

查看 scrcpy-server 回传的 PCM 具体参数 // 先搁置

如果音频源支持,直接使用 AudioFormat.ENCODING_PCM_FLOAT

获取并应用系统“最优缓冲区大小”

// 获取硬件每一跳的帧数 (例如 128, 256 或 512)
val framesPerBurst = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER).toInt()
// 缓冲区设置为 2 倍的 Burst 帧数(双缓冲),这是理论上的延迟平衡点
val optimizedBufferSize = framesPerBurst * 2 * (if (isStereo) 2 else 1) * (if (isFloat) 4 else 2)

绕过音频效果器

.setAudioAttributes(AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_GAME)
    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
    .setFlags(AudioAttributes.FLAG_LOW_LATENCY)
    // 告诉系统不要过各种 DSP 效果器
    .setAllowedCapturePolicy(AudioAttributes.ALLOW_CAPTURE_BY_NONE) 
    .build())

音频线程优先级

// 在负责读取 Socket 并写入 AudioTrack 的线程内执行
// 单独线程 + 无锁队列,跟网络线程分离
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO)

缓冲区复用减少 GC

val buffer = ByteArray(FIXED_SIZE)