移动开发

exoplayer 代码片段逻辑分析

  private fun getDefaultBufferSizeInBytes(audioFormat: AudioProcessor.AudioFormat): Int {
        val outputPcmFrameSize = Util.getPcmFrameSize(audioFormat.encoding, audioFormat.channelCount)
        val minBufferSize =
            AudioTrack.getMinBufferSize(
                audioFormat.sampleRate,
                Util.getAudioTrackChannelConfig(audioFormat.channelCount),
                audioFormat.encoding
            )
        Assertions.checkState(minBufferSize != ERROR_BAD_VALUE)
        val multipliedBufferSize = minBufferSize * EXO_BUFFER_MULTIPLICATION_FACTOR
        val minAppBufferSize =
            durationUsToFrames(EXO_MIN_BUFFER_DURATION_US).toInt() * outputPcmFrameSize
        val maxAppBufferSize = max(
            minBufferSize.toLong(),
            durationUsToFrames(EXO_MAX_BUFFER_DURATION_US) * outputPcmFrameSize
        ).toInt()
        val bufferSizeInFrames = Util.constrainValue(
            multipliedBufferSize,
            minAppBufferSize,
            maxAppBufferSize
        ) / outputPcmFrameSize
        return bufferSizeInFrames * outputPcmFrameSize
    }

这段代码是用于计算 默认音频缓冲区大小 的方法,通常用于 AudioTrack 配置,以确保音频播放流畅且延迟低。它的作用是:

1. 计算 PCM 帧大小

2. 获取 AudioTrack 最小缓冲区

3. 扩展缓冲区大小,确保应用层缓冲区适合不同场景

4. 在合理范围内调整缓冲区大小


详细解析

1. 获取 PCM 帧大小

val outputPcmFrameSize = Util.getPcmFrameSize(audioFormat.encoding, audioFormat.channelCount)

• Util.getPcmFrameSize(encoding, channelCount) 计算 每个 PCM 采样帧的字节大小

示例

PCM 16-bit,双声道:2 (bytes per sample) × 2 (channels) = 4 bytes per frame

PCM 8-bit,单声道:1 (bytes per sample) × 1 (channel) = 1 byte per frame


2. 获取 AudioTrack 允许的最小缓冲区

val minBufferSize =
    AudioTrack.getMinBufferSize(
        audioFormat.sampleRate,
        Util.getAudioTrackChannelConfig(audioFormat.channelCount),
        audioFormat.encoding
    )

• AudioTrack.getMinBufferSize() 获取 Android 系统推荐的最小音频缓冲区大小(以字节为单位)

• Util.getAudioTrackChannelConfig(audioFormat.channelCount) 转换声道数到 AudioTrack 配置,如:

• 单声道(1 channel):AudioFormat.CHANNEL_OUT_MONO

• 立体声(2 channels):AudioFormat.CHANNEL_OUT_STEREO

• audioFormat.encoding 是音频格式,如 ENCODING_PCM_16BIT。

如果 getMinBufferSize() 返回 ERROR_BAD_VALUE,表示格式不支持,程序会抛出异常:

Assertions.checkState(minBufferSize != ERROR_BAD_VALUE)

3. 扩展缓冲区大小

val multipliedBufferSize = minBufferSize * EXO_BUFFER_MULTIPLICATION_FACTOR

• EXO_BUFFER_MULTIPLICATION_FACTOR 用于扩展缓冲区大小,确保播放不卡顿。

为什么扩展?

• Android 提供的 minBufferSize 可能不足以处理所有场景,比如较高的 sampleRate 可能需要更大缓冲区。

• 例如,如果 minBufferSize = 8192 且 EXO_BUFFER_MULTIPLICATION_FACTOR = 4,则 multipliedBufferSize = 32768


4. 计算 minAppBufferSize

val minAppBufferSize =
    durationUsToFrames(EXO_MIN_BUFFER_DURATION_US).toInt() * outputPcmFrameSize

• EXO_MIN_BUFFER_DURATION_US 是 最小缓冲区持续时间(单位:微秒),比如 250000(250ms)。

• durationUsToFrames(EXO_MIN_BUFFER_DURATION_US) 将持续时间转换为帧数

• frames = (采样率 × 持续时间) / 1_000_000

• 例如 44100 Hz,250ms:

frames = (44100 × 250000) / 1_000_000 = 11025 frames

• 计算出的帧数再 乘以 outputPcmFrameSize,得到以 字节为单位 的缓冲区大小。


5. 计算 maxAppBufferSize

val maxAppBufferSize = max(
    minBufferSize.toLong(),
    durationUsToFrames(EXO_MAX_BUFFER_DURATION_US) * outputPcmFrameSize
).toInt()

• EXO_MAX_BUFFER_DURATION_US 是最大缓冲区持续时间(比如 750ms)。

• 计算方法同 minAppBufferSize,但这里取 minBufferSize 和 maxBufferSize 的最大值,保证缓冲区足够大。


6. 计算最终的缓冲区大小

val bufferSizeInFrames = Util.constrainValue(
    multipliedBufferSize,
    minAppBufferSize,
    maxAppBufferSize
) / outputPcmFrameSize
return bufferSizeInFrames * outputPcmFrameSize

• Util.constrainValue(value, min, max) 确保 multipliedBufferSize 在 minAppBufferSize 和 maxAppBufferSize 之间

最后转换回字节单位,返回最终的缓冲区大小。


完整流程示例

假设:

采样率:44100 Hz

声道数:2(立体声)

格式:PCM 16-bit

EXO_MIN_BUFFER_DURATION_US = 250_000(250ms)

EXO_MAX_BUFFER_DURATION_US = 750_000(750ms)

EXO_BUFFER_MULTIPLICATION_FACTOR = 4

那么:

1. 计算 PCM 帧大小

outputPcmFrameSize = 2 bytes * 2 channels = 4 bytes

2. 获取 minBufferSize

minBufferSize = AudioTrack.getMinBufferSize(44100, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT)
minBufferSize = 8192 bytes

3. 扩展缓冲区

multipliedBufferSize = 8192 * 4 = 32768 bytes

4. 计算 minAppBufferSize

minAppBufferSize = (44100 × 250_000 / 1_000_000) * 4 = 11025 * 4 = 44100 bytes

5. 计算 maxAppBufferSize

maxAppBufferSize = max(8192, (44100 × 750_000 / 1_000_000) * 4)
                  = max(8192, 33075 * 4)
                  = max(8192, 132300)
                  = 132300 bytes

6. 计算最终缓冲区大小

bufferSizeInFrames = constrainValue(32768, 44100, 132300) / 4
                   = 44100 / 4
                   = 11025 frames
return 11025 * 4 = 44100 bytes

最终缓冲区大小:44100 bytes(44 KB)


总结

目的是计算合适的音频缓冲区大小,既不会太小(导致音频中断),也不会太大(增加延迟)。

确保 AudioTrack 可以流畅播放,避免 Buffer Underrun(缓冲区数据不足)。

动态调整,基于音频采样率、格式和最小/最大缓冲区大小。

这段代码优化了 音频播放的稳定性低延迟性,在 ExoPlayer 音频处理流程中非常重要。 🎵

留言

您的邮箱地址不会被公开。 必填项已用 * 标注