MediaCodec解码AAC音频,用AudioTrack播放解码后的数据

前言:

这里是做实时流AAC Audio解码,音频存储形式是byte[]保存的字节流。

1.AudioTrack的初始化

private AudioTrack mAudioTrack = null;
int channelConfig = 2;
int audioFormat = 2;
int mMinBufSize = 0;
channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
mMinBufSize = AudioTrack.getMinBufferSize(8000, channelConfig, audioFormat);
try {

    mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, channelConfig, AudioFormat
            .ENCODING_PCM_16BIT, mMinBufSize, AudioTrack.MODE_STREAM);

} catch (IllegalArgumentException iae) {
    iae.printStackTrace();
}
mAudioTrack.setStereoVolume(1.0f, 1.0f);
mAudioTrack.play();

AudioTrack 初始化完成后,准备初始化MediaCodec。使用完成后,记得释放AudioTrack

if (mAudioTrack != null) {
    mAudioTrack.stop();
    mAudioTrack.release();
    mAudioTrack = null;
}

2.初始化MediaCodec

变量和常量的申明

//声道数
private static final int KEY_CHANNEL_COUNT = 2;
//采样率
private static final int KEY_SAMPLE_RATE = 8000;
//解码器
public MediaCodec mediaCodec = null;
private ByteBuffer[] inputBuffers;
private ByteBuffer[] outputBuffers;
private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

private int inIndex;
private int outIndex;


private String MINE = "audio/mp4a-latm";
private  void prepare() {
   try {
       //需要解码数据的类型
       //初始化解码器
       mediaCodec = MediaCodec.createDecoderByType(MINE);
       //MediaFormat用于描述音视频数据的相关参数
       MediaFormat mediaFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, KEY_SAMPLE_RATE, KEY_CHANNEL_COUNT);
       //数据类型
       mediaFormat.setString(MediaFormat.KEY_MIME, MINE);
       //声道个数
       mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
       //采样率
       mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, KEY_SAMPLE_RATE);
       //比特率
       mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
//            //用来标记AAC是否有adts头,1->有
       mediaFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
//            //用来标记aac的类型
       mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
//            //ByteBuffer key
       byte[] data = new byte[]{(byte) 0x15,(byte) 0x90};
       ByteBuffer csd_0 = ByteBuffer.wrap(data);
       mediaFormat.setByteBuffer("csd-0", csd_0);
       //解码器配置
       mediaCodec.configure(mediaFormat, null, null, 0);
   } catch (IOException e) {
       Log.e(TAG, " error:" + e.toString());
   }
   mediaCodec.start();
   Log.i(TAG, "start mediacodec success");
   inputBuffers = mediaCodec.getInputBuffers();
   outputBuffers = mediaCodec.getOutputBuffers();
   bufferInfo = new MediaCodec.BufferInfo();
}

byte data[]的初始化:
AAC Profile 5bits | 采样率 4bits | 声道数 4bits | 其他 3bits |
AAC Profile(如果是其他格式,请自行查询格式编码)

AAC Main 0x01 
AAC LC    0x02 
AAC SSR  0x03

采样率编码

0x00   96000 
0x01   88200 
0x02   64000 
0x03   48000 
0x04   44100
0x05   32000
0x06   24000 
0x07   22050 
0x08   16000 
0x09   12000 
0x0A   11025 
0x0B    8000 
0x0C   reserved 
0x0D   reserved 
0x0E   reserved 
0x0F   escape value

声道KEY_CHANNEL_COUNT编码

0x00 - defined in audioDecderSpecificConfig 
0x01 单声道(center front speaker) 
0x02 双声道(left, right front speakers) 
0x03 三声道(center, left, right front speakers) 
0x04 四声道(center, left, right front speakers, rear surround speakers) 
0x05 五声道(center, left, right front speakers, left surround, right surround rear speakers) 
0x06 5.1声道(center, left, right front speakers, left surround, right surround rear speakers, front low frequency effects speaker) 
0x07 7.1声道(center, left, right center front speakers, left, right outside front speakers, left surround, right surround rear speakers, front low frequency effects speaker) 
0x08-0x0F - reserved

我这里的音频格式是:AAC LC,采样率为8000,声道数2,其他参数没有,默认为0
所以这里的编码格式是:0x02,0x0b,0x02,0x00;转换成二进制是:00010(5bits)1011(4bits)0010(4bits)000(3bits)
在这里插入图片描述所以,

data[] = new byte[]{(byte) 0x15, (byte) 0x90};
ByteBuffer csd_0 = ByteBuffer.wrap(data);
mediaFormat.setByteBuffer("csd-0", csd_0);

通过MediaFormat setByteBuffer,添加进音频流,解码时通过这组编码识别你的解码配置参数。

3.input buffer和output buffer

inputBuffers = mediaCodec.getInputBuffers();
outputBuffers = mediaCodec.getOutputBuffers();
bufferInfo = new MediaCodec.BufferInfo();
public void input(byte[] frameData) {
  //这里注释的代码是给AAC音频流添加ADTS头部的,我们的数据流中已经有了头,所以注释了,这里不删除,留给有需要的人
   /*byte[] ADTS = new byte[7];
    addADTStoPacket(ADTS, frameData.length + 7);
    byte[] frameDataAddADTS = new byte[frameData.length];
    System.arraycopy(ADTS, 0, frameDataAddADTS, 0, 7);
    System.arraycopy(frameData, 0, frameDataAddADTS, 0, frameData.length);*/
   
    ByteBuffer inputBuffer = null;
    //API>=21区分
    // 1.-1表示一直等待 2.0表示不等待 3.大于0表示等待的时间(us)
    inIndex = mediaCodec.dequeueInputBuffer(-1);
    Log.e("lvyouhai","inIndex:" + inIndex);
    if (inIndex >= 0) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            inputBuffer = mediaCodec.getInputBuffer(inIndex);
        } else {
            inputBuffer = inputBuffers[inIndex];
        }
        inputBuffer.clear();
        inputBuffer.put(frameData, 0, frameData.length);
        mediaCodec.queueInputBuffer(inIndex, 0, frameData.length, 0, 0);
        inIndex = mediaCodec.dequeueInputBuffer(0);
    }
}

public byte[] output() {
    ByteBuffer outputBuffer = null;
    byte[] getBuffer = null;
    outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 5000);
    while (outIndex >= 0) {
        getBuffer = new byte[bufferInfo.size];
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            outputBuffer = mediaCodec.getOutputBuffer(outIndex);
        } else {
            outputBuffer = outputBuffers[outIndex];
        }
        outputBuffer.get(getBuffer);
        outputBuffer.position(0);
        outputBuffer.limit(bufferInfo.size);
        outputBuffer.clear();
        mediaCodec.releaseOutputBuffer(outIndex, false);
        outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 5000);
    }
        return getBuffer;
}
private byte[] addADTStoPacket(byte[] packet, int packetLen) {
   int profile = 2; // AAC LC
   int freqIdx = 4; // 44.1KHz
   int chanCfg = 2; // CPE

   // fill in ADTS data
   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;
   return packet;
}

完成解码后记得释放MediaCodec资源,如果是实时流,根据自己代码的执行顺序决定

public void stop() {
    Log.e(TAG, "stop MediaCodec and release");
    if (mediaCodec != null) {
        mediaCodec.stop();
        mediaCodec.release();
    }
    mediaCodec = null;
}

4.播放

这里receiveBuffer就是需要解码的AAC byte[]字节流。

mediaCodecDecAudio.input(receiveBuffer);
byte[] outputAudioBuffer = null;
outputAudioBuffer = mediaCodecDecAudio.output();
if(outputAudioBuffer == null) {
    //do nothing
}else {
    try {
        mAudioTrack.write(outputAudioBuffer, 0, (int)outputAudioBuffer.length);
    }catch (IndexOutOfBoundsException e){
        e.getStackTrace();
    }
}

PS:

可能遇到的问题:
这里AAC解码只遇到了一个问题,就是解码出来的数据是空白的,后来经过不断打印日志,发现是MediaFormat的配置参数有问题,这里就不做具体解释了,因为解码数据的不同,配置也要做些许改变,其中比较重要的参数就是
(1)数据流是不是ADTS,需要通过打印解码的字节流来判断

MediaFormat.KEY_IS_ADTS

(2)data数据的添加,这是解码识别手动配置的重要参数,出错了,则解码数据问题会很多

 byte[] data

其他的问题暂时还没遇到,希望大家遇到其他AAC硬解码的问题一起交流。

猜你喜欢

转载自blog.csdn.net/mozushixin_1/article/details/92830785