Android MediaCodec 录制系统音频功能实现完整代码
时间: 2023-11-15 12:09:02 浏览: 141
这里是一个基于 Android MediaCodec 录制系统音频的完整代码示例:
```java
public class AudioRecorder {
private static final String TAG = "AudioRecorder";
private static final int TIMEOUT_US = 10000;
private AudioRecord mAudioRecord;
private MediaCodec mMediaCodec;
private String mMimeType = "audio/mp4a-latm";
private int mSampleRate = 44100;
private int mChannelCount = 2;
private int mBitRate = 128000;
private int mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannelCount, AudioFormat.ENCODING_PCM_16BIT);
private String mOutputFilePath;
private volatile boolean mIsRecording;
private volatile boolean mIsEncoding;
public AudioRecorder(String outputPath) {
mOutputFilePath = outputPath;
}
public void start() {
if (mIsRecording) {
return;
}
mIsRecording = true;
new Thread(() -> {
try {
prepareEncoder();
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, mSampleRate, mChannelCount, AudioFormat.ENCODING_PCM_16BIT, mBufferSize);
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "AudioRecord initialize failed");
return;
}
mAudioRecord.startRecording();
ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int inputBufferIndex;
int outputBufferIndex;
while (mIsRecording) {
inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_US);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
int readSize = mAudioRecord.read(inputBuffer, mBufferSize);
if (readSize > 0) {
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime() / 1000, 0);
}
}
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// 忽略配置帧
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
continue;
}
if (bufferInfo.size > 0) {
muxAudio(outputBuffer, bufferInfo);
}
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
}
}
mAudioRecord.stop();
} catch (Exception e) {
Log.e(TAG, "Exception on AudioRecorder: " + e.getMessage());
} finally {
release();
}
}).start();
}
public void stop() {
mIsRecording = false;
}
private void prepareEncoder() throws IOException {
mMediaCodec = MediaCodec.createEncoderByType(mMimeType);
MediaFormat mediaFormat = new MediaFormat();
mediaFormat.setString(MediaFormat.KEY_MIME, mMimeType);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, mChannelCount);
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
}
private void muxAudio(ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo) throws IOException {
if (!mIsEncoding) {
mIsEncoding = true;
MediaMuxer mediaMuxer = new MediaMuxer(mOutputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int audioTrackIndex = mediaMuxer.addTrack(mMediaCodec.getOutputFormat());
mediaMuxer.start();
Log.i(TAG, "start mediaMuxer");
}
ByteBuffer outputBuffer = buffer.duplicate();
MediaMuxerRunnable mediaMuxerRunnable = new MediaMuxerRunnable(mMediaCodec.getOutputFormat(), outputBuffer, bufferInfo);
mediaMuxerRunnable.run();
}
private void release() {
try {
if (mMediaCodec != null) {
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
} catch (Exception e) {
Log.e(TAG, "Exception on release MediaCodec: " + e.getMessage());
}
try {
if (mIsEncoding) {
MediaMuxerRunnable.release();
}
} catch (Exception e) {
Log.e(TAG, "Exception on release MediaMuxerRunnable: " + e.getMessage());
}
}
private static class MediaMuxerRunnable implements Runnable {
private static final String TAG = "MediaMuxerRunnable";
private static final Object sLock = new Object();
private static MediaMuxer sMediaMuxer;
private static int sTrackIndex = -1;
private MediaFormat mMediaFormat;
private ByteBuffer mBuffer;
private MediaCodec.BufferInfo mBufferInfo;
public MediaMuxerRunnable(MediaFormat mediaFormat, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo) {
mMediaFormat = mediaFormat;
mBuffer = buffer;
mBufferInfo = bufferInfo;
}
public void run() {
synchronized (sLock) {
if (sMediaMuxer == null) {
try {
sMediaMuxer = new MediaMuxer(getOutputMediaPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
sTrackIndex = sMediaMuxer.addTrack(mMediaFormat);
sMediaMuxer.start();
Log.i(TAG, "start mediaMuxer from runnable");
} catch (IOException e) {
Log.e(TAG, "Exception on MediaMuxerRunnable: " + e.getMessage());
}
}
}
if (sMediaMuxer != null && sTrackIndex >= 0) {
mBufferInfo.offset = 0;
mBufferInfo.presentationTimeUs = System.nanoTime() / 1000;
sMediaMuxer.writeSampleData(sTrackIndex, mBuffer, mBufferInfo);
}
}
private static String getOutputMediaPath() {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), "AudioRecordTest");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.e(TAG, "failed to create directory");
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String mediaFilePath = mediaStorageDir.getPath() + File.separator + "AUD_" + timeStamp + ".m4a";
return mediaFilePath;
}
public static void release() {
synchronized (sLock) {
if (sMediaMuxer != null) {
sMediaMuxer.stop();
sMediaMuxer.release();
sMediaMuxer = null;
}
}
}
}
}
```
使用该类,你只需要创建一个 `AudioRecorder` 对象并指定输出文件路径,然后调用 `start()` 方法即可开始录制,调用 `stop()` 方法结束录制。例如:
```java
AudioRecorder audioRecorder = new AudioRecorder("/sdcard/output.m4a");
audioRecorder.start();
...
audioRecorder.stop();
```
注意,如果你想要测试该代码,需要添加以下权限:
```xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
阅读全文