AudioRecord录制PCM音频
看一下AudioRecord安卓源码的构造函数(targetAPi29)
主要看一下几个参数
- audioSource 声音来源,参考 MediaRecorder.AudioSource 这里的种类还比较多,暂时先使用MIC,其他的后续用到再说吧
- sampleRateInHz 音频采样率 注释上说44100Hz是全部设备都支持的,其他的如22050, 16000, 11025可能或有部分设备支持。 44100Hz 即每秒采样44100次
- channelConfig 音频通道,即声道数,CHANNEL_IN_MONO单声道,所有设备都支持, CHANNEL_IN_STEREO立体声,多声道,可能是两个声道?
public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
- audioFormat 音频数据格式,看源码种类也比较多,8位16位32位的pcm 这个类型有多达29种,网上常用的案例都是使用16位的pcm,我们这里先使用ENCODING_PCM_16BIT 16位的PCM
- bufferSizeInBytes 音频缓冲区大小,这个注释有点没看懂,全部尺寸的缓冲区当音频数据被录制时? 后面说这个值通过getMinBufferSize这个方法获取。 直接上代码看下吧
private int mReadMinBufferSize;
public enum RecorderStatus {
NO_READY,
START,
STOP
}
private AudioRecord mAudioRecord;
private volatile RecorderStatus mStatus;
private static final int mAudioSource = MediaRecorder.AudioSource.MIC;
private static final int mSampleRateInHz = 44100;
private static final int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;
private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
private final ExecutorService mExecutors = Executors.newSingleThreadExecutor();
public RecorderStatus getStatus() {
return mStatus;
}
public void startRecordOnMainThread(String filePath) {
mExecutors.execute(() -> {
try {
startRecord(filePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
@SuppressLint("MissingPermission")
@WorkerThread
public void startRecord(String filePath) throws IOException {
if (mStatus == RecorderStatus.START) {
Log.i(TAG, "startRecord: already in recording");
return;
}
File file = new File(filePath);
if (file.exists()) {
boolean delete = file.delete();
Log.i(TAG, "startRecord: delete already exist audio file result = " + delete);
}
String dir = filePath.substring(0, filePath.lastIndexOf(File.separator) + 1);
if (!new File(dir).exists()) {
boolean mkdirs = new File(dir).mkdirs();
Log.i(TAG, "startRecord: mkdir directory result = " + mkdirs + " and path = " + dir);
}
boolean newFile = file.createNewFile();
Log.i(TAG, "startRecord: create new Audio file result = " + newFile + " and path = " + filePath);
mReadMinBufferSize = AudioRecord.getMinBufferSize(mSampleRateInHz, mChannelConfig, mAudioFormat);
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(mAudioSource, mSampleRateInHz, mChannelConfig, mAudioFormat, mReadMinBufferSize);
}
mStatus = RecorderStatus.START;
mAudioRecord.startRecording();
try (FileOutputStream fos = new FileOutputStream(file)) {
byte[] buf = new byte[mReadMinBufferSize];
while (mStatus == RecorderStatus.START) {
int bufferReadResult = mAudioRecord.read(buf, 0, mReadMinBufferSize);
if (bufferReadResult != AudioRecord.ERROR_INVALID_OPERATION) {
fos.write(buf);
}
}
}
}
public void stopRecord() {
mStatus = RecorderStatus.STOP;
if (mAudioRecord != null) {
mAudioRecord.stop();
}
}
public void release() {
mStatus = RecorderStatus.NO_READY;
if (mAudioRecord != null) {
mAudioRecord.release();
mAudioRecord = null;
}
}
使用FFmpeg播放PCM音频
ffplay -ar 44100 -ac 1 -f s16le -i 2023_11_09_11_06_01.pcm
-i 表示指定的输入文件
-f 表示强制使用的格式
-ar 表示播放的音频数据的采样率
-ac 表示播放的音频数据的通道数
标签:mAudioRecord,private,音视频,从零开始,RecorderStatus,new,Android,public,CHANNEL
From: https://blog.51cto.com/u_13893439/8274952