在 Android 中使用 AudioRecord 录制 AAC 音频可以按照以下步骤进行:

一、添加权限

AndroidManifest.xml 文件中添加录音权限:

1
2
3
4
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!--android 11 以上-->

二、初始化参数

  1. 确定音频输入源,通常可以使用 MediaRecorder.AudioSource.MIC(麦克风)。
  2. 选择音频采样率,常见的有 44100Hz、48000Hz 等。
  3. 确定音频通道配置,如单声道(AudioFormat.CHANNEL_IN_MONO)或立体声(AudioFormat.CHANNEL_IN_STEREO)。
  4. 选择音频编码格式,这里需要选择支持 AAC 编码的格式,如 AudioFormat.ENCODING_PCM_16BIT,后续再进行 AAC 编码转换。
  5. 计算最小缓冲区大小,可以使用 AudioRecord.getMinBufferSize() 方法来获取满足上述参数要求的最小缓冲区大小。

三、初始化 AudioRecord 对象

1
2
3
4
5
6
int audioSource = MediaRecorder.AudioSource.MIC; //指定音频源为mic
int sampleRateInHz = 44100; //指定音频采样率
int channelConfig = AudioFormat.CHANNEL_IN_MONO; //单声道
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; //每次采样16位
int bufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);//计算最小读取的buffer
AudioRecord audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSize);

四、初始化AAC编码器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
try {
        mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);//创建编码器
        MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 1); //编码器参数需要和录制参数对应
        format.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//设置编码码率
        format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);//设置aac profile
        mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    	mediaCodec.start();
    } catch (IOException e) {
        e.printStackTrace();
    }

五、开始录制

一般会开启一个线程,循环读取音频数据,然后进行 AAC 编码转换或保存为临时文件等待后续处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public void startRecording() {
    if (isRecording) {
        return;
    }
    isRecording = true;
    outputFile = new File(Environment.getExternalStorageDirectory(), "audio_recorded_" + System.currentTimeMillis() + ".aac");
    recordingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try (FileOutputStream fos = new FileOutputStream(outputFile)) {
                ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                audioRecord.startRecording(); //开始录制
                while (isRecording) {
                    int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                        int readSize = audioRecord.read(inputBuffer, inputBuffer.capacity());
                        if (readSize > 0) {
                            mediaCodec.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime(), 0);
                        }
                    }
                    int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1);
                    while (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                        byte[] data = new byte[bufferInfo.size];
                        outputBuffer.get(data);
                        fos.write(data);
                        mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                        outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1);
                    }
                }
                
                
                audioRecord.stop();
                audioRecord.release(); //释放录音资源
                mediaCodec.stop();
                mediaCodec.release(); //释放编码器资源
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    recordingThread.start();
}

六、结束录制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void stopRecording() {
    if (!isRecording) {
        return;
    }
    isRecording = false;
    try {
        recordingThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

七、AudioRecorder完整代码参考

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class AudioRecorder {
    private static final String TAG = "AudioRecorder";
    private AudioRecord audioRecord;
    private MediaCodec mediaCodec;
    private Thread recordingThread;
    private boolean isRecording = false;
    private File outputFile;

    public AudioRecorder() {
        int minBufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize);

        try {
            mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 1);
            format.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mediaCodec.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startRecording() {
        if (isRecording) {
            return;
        }
        isRecording = true;
        outputFile = new File(Environment.getExternalStorageDirectory(), "audio_recorded_" + System.currentTimeMillis() + ".aac");
        recordingThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try (FileOutputStream fos = new FileOutputStream(outputFile)) {
                    ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                    ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    audioRecord.startRecording();
                    while (isRecording) {
                        int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
                        if (inputBufferIndex >= 0) {
                            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                            int readSize = audioRecord.read(inputBuffer, inputBuffer.capacity());
                            if (readSize > 0) {
                                mediaCodec.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime(), 0);
                            }
                        }
                        int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1);
                        while (outputBufferIndex >= 0) {
                            ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                            byte[] data = new byte[bufferInfo.size];
                            outputBuffer.get(data);
                            fos.write(data);
                            mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1);
                        }
                    }
                    audioRecord.stop();
                    audioRecord.release();
                    mediaCodec.stop();
                    mediaCodec.release();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        recordingThread.start();
    }

    public void stopRecording() {
        if (!isRecording) {
            return;
        }
        isRecording = false;
        try {
            recordingThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}