这里是做实时流AAC Audio解码,音频存储形式是byte[]保存的字节流。
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;
}
变量和常量的申明
//声道数
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,添加进音频流,解码时通过这组编码识别你的解码配置参数。
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;
}
这里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();
}
}
可能遇到的问题:
这里AAC解码只遇到了一个问题,就是解码出来的数据是空白的,后来经过不断打印日志,发现是MediaFormat的配置参数有问题,这里就不做具体解释了,因为解码数据的不同,配置也要做些许改变,其中比较重要的参数就是
(1)数据流是不是ADTS,需要通过打印解码的字节流来判断
MediaFormat.KEY_IS_ADTS
(2)data数据的添加,这是解码识别手动配置的重要参数,出错了,则解码数据问题会很多
byte[] data
其他的问题暂时还没遇到,希望大家遇到其他AAC硬解码的问题一起交流。