You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android MediaCodec编码PCM转AAC仅44100采样率可正常解码问题

问题分析与解决方案

你的问题核心在于硬编码的AAC配置参数(csd-0和ADTS头中的采样率索引)没有随采样率变化而动态调整,导致解码器接收到的编码元数据和实际音频流参数不匹配——只有44100Hz采样率刚好匹配你固定的参数,所以能正常解码,其他采样率自然失败。

具体问题点拆解

  1. 固定的csd-0参数
    你直接使用了byte[] data = new byte[]{(byte) 0x11, (byte) 0x90};作为csd-0,这个值对应的是:

    • AAC LC profile
    • 采样率索引4(仅对应44100Hz)
    • 通道数1(但你的编码配置是双通道KEY_CHANNEL_COUNT=2
      换采样率时这个参数完全没变化,解码器拿到的配置信息和实际编码的音频参数严重不符,根本无法正确解析。
  2. ADTS头中硬编码的采样率索引
    addADTStoPacket方法里,你固定了int freqIdx = FREQ_IDX;FREQ_IDX=4,这只对应44100Hz。切换到其他采样率时,ADTS头里的采样率信息还是44100Hz,解码器用错误的参数去解析,必然失败。

  3. 输入缓冲区的标记错误
    你在queueInputBuffer时给每帧PCM数据都加了BUFFER_FLAG_CODEC_CONFIG标记,这个标记只应该用于发送配置数据(比如csd-0),重复发送会干扰编码器的正常工作逻辑。

修复后的代码实现

1. 动态生成适配当前参数的csd-0

根据当前的profile、采样率、通道数计算符合标准的csd-0:

// 替换原来固定的csd-0生成逻辑
private ByteBuffer generateCsd0(int profile, int sampleRate, int channelCount) {
    int freqIdx = getSampleRateIndex(sampleRate);
    if (freqIdx == -1) {
        throw new IllegalArgumentException("Unsupported sample rate: " + sampleRate);
    }
    byte[] csd0 = new byte[2];
    csd0[0] = (byte) ((profile << 3) | (freqIdx >> 1));
    csd0[1] = (byte) (((freqIdx & 0x01) << 7) | (channelCount << 3));
    return ByteBuffer.wrap(csd0);
}

// 映射采样率到AAC标准规定的索引值
private int getSampleRateIndex(int sampleRate) {
    switch (sampleRate) {
        case 96000: return 0;
        case 88200: return 1;
        case 64000: return 2;
        case 48000: return 3;
        case 44100: return 4;
        case 32000: return 5;
        case 24000: return 6;
        case 22050: return 7;
        case 16000: return 8;
        case 12000: return 9;
        case 11025: return 10;
        case 8000: return 11;
        case 7350: return 12;
        default: return -1;
    }
}

然后在prepare方法中替换原来的csd-0设置:

// 替换原来的byte[] data = ...和setByteBuffer部分
ByteBuffer csd_0 = generateCsd0(KEY_AAC_PROFILE, KEY_SAMPLE_RATE, KEY_CHANNEL_COUNT);
mediaFormat.setByteBuffer("csd-0", csd_0);

2. 动态生成ADTS头的采样率索引

修改addADTStoPacket方法,根据当前采样率获取对应的索引:

private void addADTStoPacket(byte[] packet, int packetLen) {
    int profile = KEY_AAC_PROFILE; // AAC LC
    int freqIdx = getSampleRateIndex(KEY_SAMPLE_RATE); // 动态匹配当前采样率
    int chanCfg = KEY_CHANNEL_COUNT; // 直接用配置的通道数,不用硬编码1
    packet[0] = (byte) 0xFF;
    packet[1] = (byte) 0xF9;
    packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
    packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
    packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
    packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
    packet[6] = (byte) 0xFC;
}

3. 修正输入缓冲区的标记

encode方法中,BUFFER_FLAG_CODEC_CONFIG只用于发送配置数据,普通PCM数据用0标记:

// 替换原来的queueInputBuffer行
mEncoder.queueInputBuffer(inputBufferIndex, 0, data.length, computePresentationTime(presentationTimeUs), 0);

额外注意事项

  • 确保修改KEY_SAMPLE_RATE时,AudioRecord的初始化参数也保持一致(你的代码已经做到了,但要注意不要遗漏)。
  • 部分设备的编码器可能对特定采样率/通道数组合有兼容性限制,建议在选择编码器时,通过MediaCodecInfo.CodecCapabilities确认当前编码器支持的参数范围。

内容的提问来源于stack exchange,提问作者YLC

火山引擎 最新活动