实现类:
package com.ar;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.widget.Button;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;
public class AudioRecordManager {
private final int sampleRateInHz = 44100;//44100 16000
private final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private String audioFilePath;
private String tag="AudioRecordManager";
private class AudioData {
private ByteBuffer buffer;
private int size;
}
private AudioRecorder mAudioRecorder;
private AudioEncorder mAudioEncorder;
private ArrayBlockingQueue queue;
/**
*
* @param audioFilePath 文件名
*/
public AudioRecordManager(String audioFilePath) {
this.audioFilePath=audioFilePath;
queue = new ArrayBlockingQueue<>(1024);
mAudioRecorder = new AudioRecorder();
mAudioEncorder = new AudioEncorder();
}
public void startRecord() {
mAudioRecorder.start();
mAudioEncorder.start();
}
public void stopRecord() {
mAudioRecorder.stopRecording();
mAudioEncorder.stopEncording();
}
/**
* 录音线程
*/
public class AudioRecorder extends Thread {
private AudioRecord mAudioRecord;
private boolean isRecording;
private int minBufferSize;
public AudioRecorder() {
isRecording = true;
initRecorder();
}
@Override
public void run() {
super.run();
startRecording();
}
/**
* 初始化录音
*/
public void initRecorder() {
minBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)*2;// 乘以2 加大缓冲区,防止其他意外
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, minBufferSize);
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
isRecording = false;
return;
}
}
/**
* 释放资源
*/
public void release() {
if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
mAudioRecord.stop();
}
}
/**
* 开始录音
*/
public void startRecording() {
if (mAudioRecord == null) {
return;
}
mAudioRecord.startRecording();
while (isRecording) {
long a=System.currentTimeMillis();
AudioData audioDate = new AudioData();
audioDate.buffer = ByteBuffer.allocateDirect(minBufferSize);
audioDate.size = mAudioRecord.read(audioDate.buffer, minBufferSize);
try {
if (queue != null) {
queue.put(audioDate);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
long b=System.currentTimeMillis()-a;
Log.i(tag,"录制耗时-毫秒=="+b);
}
release();
}
/**
* 结束录音
*/
public void stopRecording() {
isRecording = false;
}
}
/**
* 音频编码线程
*/
public class AudioEncorder extends Thread {
private MediaCodec mEncorder;
private Boolean isEncording = false;
private int minBufferSize;
private OutputStream mFileStream;
public AudioEncorder() {
isEncording = true;
initEncorder();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
super.run();
startEncording();
}
/**
* 初始化编码器
*/
private void initEncorder() {
minBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)*2;// 乘以2 加大缓冲区,防止其他意外
try {
mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
} catch (Exception e) {
e.printStackTrace();
}
MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRateInHz, 1);
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
format.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, minBufferSize * 4);
mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
}
/**
* 开始编码
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void startEncording() {
if (mEncorder == null) {
return;
}
mEncorder.start();
try {
mFileStream = new FileOutputStream(audioFilePath);
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
AudioData audioData;
while (isEncording) {
long a=System.currentTimeMillis();
// 从队列中取出录音的一帧音频数据
audioData = getAudioData();
if (audioData == null) {
continue;
}
// 取出InputBuffer,填充音频数据,然后输送到编码器进行编码
int inputBufferIndex = mEncorder.dequeueInputBuffer(0);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
inputBuffer.clear();
inputBuffer.put(audioData.buffer);
mEncorder.queueInputBuffer(inputBufferIndex, 0, audioData.size, System.nanoTime(), 0);
}
// 取出编码好的一帧音频数据,然后给这一帧添加ADTS头
int outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
while (outputBufferIndex >= 0) {
// int outBitsSize = mBufferInfo.size;
// int outPacketSize = outBitsSize + 7; // ADTS头部是7个字节
// ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
// outputBuffer.position(mBufferInfo.offset);
// outputBuffer.limit(mBufferInfo.offset + outBitsSize);
// byte[] outData = new byte[outPacketSize];
// addADTStoPacket(outData, outPacketSize);
// outputBuffer.get(outData, 7, outBitsSize);
// outputBuffer.position(mBufferInfo.offset);
// mFileStream.write(outData);
ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
int outBufferSize = outputBuffer.limit() + 7;
byte[] aacBytes = new byte[outBufferSize];
addADTStoPacket(aacBytes, outBufferSize);
outputBuffer.get(aacBytes, 7, outputBuffer.limit());
mFileStream.write(aacBytes);
mEncorder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
}
long b=System.currentTimeMillis()-a;
Log.i(tag,"编码耗时-毫秒=="+b);
}
release();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止编码
*/
public void stopEncording() {
isEncording = false;
}
/**
* 从队列中取出一帧待编码的音频数据
*
* @return
*/
public AudioData getAudioData() {
if (queue != null) {
try {
return (AudioData) queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself !!! .
*注意,这里的packetLen参数为raw aac Packet Len + 7; 7 bytes adts header
**/
private void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC;
int freqIdx = 4; //见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码
int chanCfg = 1; //见后面注释channel_configuration,AudioFormat.CHANNEL_IN_MONO 单声道(声道数量)
/*int avpriv_mpeg4audio_sample_rates[] = {96000, 88200, 64000, 48000, 44100, 32000,24000, 22050, 16000, 12000, 11025, 8000, 7350};
channel_configuration: 表示声道数chanCfg
0: Defined in AOT Specifc Config
1: 1 channel: front-center
2: 2 channels: front-left, front-right
3: 3 channels: front-center, front-left, front-right
4: 4 channels: front-center, front-left, front-right, back-center
5: 5 channels: front-center, front-left, front-right, back-left, back-right
6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
8-15: Reserved
*/
// fill in ADTS data
packet[0] = (byte)0xFF;
//packet[1] = (byte)0xF9;
packet[1] = (byte)0xF1;//解决ios 不能播放问题
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;
}
/**
* 释放资源
*/
public void release() {
if (mFileStream != null) {
try {
mFileStream.flush();
mFileStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mEncorder != null) {
mEncorder.stop();
}
}
}
}