MediaCodec decodes AAC audio and uses AudioTrack to play the decoded data

Foreword:

Here is the real-time stream AAC Audio decoding, the audio storage format is the byte stream saved by byte[].

1. Initialization of 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();

After AudioTrack is initialized, MediaCodec is ready to be initialized. After using, remember to release AudioTrack

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

2. Initialize MediaCodec

Declaration of variables and constants

//声道数
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();
}

Initialization of byte data[]:
AAC Profile 5bits | Sampling rate 4bits | Number of channels 4bits | Other 3bits |
AAC Profile (If it is other formats, please check the format code by yourself )

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

Sampling rate encoding

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

Channel KEY_CHANNEL_COUNT encoding

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

My audio format here is: AAC LC, sampling rate is 8000, number of channels is 2, other parameters are not, the default is 0
so the encoding format here is: 0x02,0x0b,0x02,0x00; converted to binary is: 00010 (5bits )1011(4bits)0010(4bits)000(3bits)
Insert picture description hereSo,

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

Add into the audio stream through MediaFormat setByteBuffer, and identify your decoding configuration parameters through this group of codes when decoding.

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;
}

Remember to release the MediaCodec resource after decoding, if it is a real-time stream, decide according to the execution order of your own code

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

4. Play

Here, receiveBuffer is the AAC byte[] byte stream that needs to be decoded.

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:

Possible problems:
AAC decoding here only encounters one problem, that is, the decoded data is blank. Later, after continuous printing of logs, it is found that there is a problem with the configuration parameters of MediaFormat. There is no specific explanation here, because the decoded data The configuration needs to be changed slightly. The more important parameter is
(1) Whether the data stream is ADTS or not, it needs to be judged by printing the decoded byte stream

MediaFormat.KEY_IS_ADTS

(2) Adding data data, which is an important parameter for manual configuration of decoding and recognition. If something goes wrong, there will be many problems with decoding data.

 byte[] data

I haven't encountered other problems yet, and I hope you can communicate with you when you encounter other AAC hard decoding problems.

Guess you like

Origin blog.csdn.net/mozushixin_1/article/details/92830785