Android使用AudioRecord录制PCM音频、PCM转AAC、使用MediaRecorder直接录制AAC编码音频

Maeve ·
更新时间:2024-09-21
· 877 次阅读

Android为我们提供了两个音频处理的API:AudioRecordMediaRecorder

AudioRecord:偏底层的api
MediaRecorder:对AudioRecord进行包装的api

一、使用AudioRecord录制pcm编码的音频 首先需要录音权限,API>=6.0还需要动态申请 (动态申请权限代码略过,详情见文末源码) 二、录制主要分为一下几步 创建AudioRecord 设置参数 录音来源 采样率 录制的声道 数据格式 录制的缓冲区大小 开启一个线程读取音频数据 创建一个AduioRecord工具类 public class AudioRecordUtil { //设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025 private final int sampleRateInHz = 44100; //设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道 private final int channelConfig = AudioFormat.CHANNEL_IN_STEREO; //音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。 private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; //录制状态 private boolean recorderState = true; private byte[] buffer; private AudioRecord audioRecord; private static AudioRecordUtil audioRecordUtil = new AudioRecordUtil(); public static AudioRecordUtil getInstance() { return audioRecordUtil; } private AudioRecordUtil() { init(); } private void init() { int recordMinBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); //指定 AudioRecord 缓冲区大小 buffer = new byte[recordMinBufferSize]; //根据录音参数构造AudioRecord实体对象 audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, recordMinBufferSize); } /** * 开始录制 */ public void start() { if (audioRecord.getState() == AudioRecord.RECORDSTATE_STOPPED) { recorderState = true; audioRecord.startRecording(); new RecordThread().start(); } } /** * 停止录制 */ public void stop() { recorderState = false; if (audioRecord.getState() == AudioRecord.RECORDSTATE_RECORDING) { audioRecord.stop(); } audioRecord.release(); } private class RecordThread extends Thread { @Override public void run() { while (recorderState) { int read = audioRecord.read(buffer, 0, buffer.length); if (AudioRecord.ERROR_INVALID_OPERATION != read) { //获取到的pcm数据就是buffer了 Log.d("TAG", String.valueOf(buffer.length)); } } } } } 启动录音 AudioRecordUtil.getInstance().start(); 停止录音 AudioRecordUtil.getInstance().stop(); 在子线程中读取到的buffer数据就是音频数据了,还是比较简单的 三、PCM的音频原数据已经获取到了,现在就对他进行AAC编码 编码器的采样率必须与录制的时候保持一致 public class PCMEncoderAAC { //比特率 private final static int KEY_BIT_RATE = 96000; //读取数据的最大字节数 private final static int KEY_MAX_INPUT_SIZE = 1024 * 1024; //声道数 private final static int CHANNEL_COUNT = 2; private MediaCodec mediaCodec; private ByteBuffer[] encodeInputBuffers; private ByteBuffer[] encodeOutputBuffers; private MediaCodec.BufferInfo encodeBufferInfo; private EncoderListener encoderListener; public PCMEncoderAAC(int sampleRate, EncoderListener encoderListener) { this.encoderListener = encoderListener; init(sampleRate); } /** * 初始化AAC编码器 */ private void init(int sampleRate) { try { //参数对应-> mime type、采样率、声道数 MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, CHANNEL_COUNT); //比特率 encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, KEY_BIT_RATE); encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, KEY_MAX_INPUT_SIZE); mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); mediaCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); } catch (IOException e) { e.printStackTrace(); } mediaCodec.start(); encodeInputBuffers = mediaCodec.getInputBuffers(); encodeOutputBuffers = mediaCodec.getOutputBuffers(); encodeBufferInfo = new MediaCodec.BufferInfo(); } /** * @param data */ public void encodeData(byte[] data) { //dequeueInputBuffer(time)需要传入一个时间值,-1表示一直等待,0表示不等待有可能会丢帧,其他表示等待多少毫秒 //获取输入缓存的index int inputIndex = mediaCodec.dequeueInputBuffer(-1); if (inputIndex >= 0) { ByteBuffer inputByteBuf = encodeInputBuffers[inputIndex]; inputByteBuf.clear(); //添加数据 inputByteBuf.put(data); //限制ByteBuffer的访问长度 inputByteBuf.limit(data.length); //把输入缓存塞回去给MediaCodec mediaCodec.queueInputBuffer(inputIndex, 0, data.length, 0, 0); } //获取输出缓存的index int outputIndex = mediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0); while (outputIndex >= 0) { //获取缓存信息的长度 int byteBufSize = encodeBufferInfo.size; //添加ADTS头部后的长度 int bytePacketSize = byteBufSize + 7; //拿到输出Buffer ByteBuffer outPutBuf = encodeOutputBuffers[outputIndex]; outPutBuf.position(encodeBufferInfo.offset); outPutBuf.limit(encodeBufferInfo.offset + encodeBufferInfo.size); byte[] aacData = new byte[bytePacketSize]; //添加ADTS头部 addADTStoPacket(aacData, bytePacketSize); /* get(byte[] dst,int offset,int length):ByteBuffer从position位置开始读,读取length个byte,并写入dst下 标从offset到offset + length的区域 */ outPutBuf.get(aacData, 7, byteBufSize); outPutBuf.position(encodeBufferInfo.offset); //编码成功 if (encoderListener != null) { encoderListener.encodeAAC(aacData); } //释放 mediaCodec.releaseOutputBuffer(outputIndex, false); outputIndex = mediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0); } } /** * 添加ADTS头 */ private void addADTStoPacket(byte[] packet, int packetLen) { // AAC LC int profile = 2; // 44.1KHz int freqIdx = 4; // CPE int chanCfg = 2; // fill in ADTS data packet[0] = (byte) 0xFF; packet[1] = (byte) 0xF9; packet[2] = (byte) (((profile - 1) << 6) + (freqIdx <> 2)); packet[3] = (byte) (((chanCfg & 3) <> 11)); packet[4] = (byte) ((packetLen & 0x7FF) >> 3); packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F); packet[6] = (byte) 0xFC; } public interface EncoderListener { void encodeAAC(byte[] data); } } 使用只需要在上面拿到pcm数据的那里调用encodeData()方法即可 初始化AudioRecordUtil时也同时将编码器初始化好 private PCMEncoderAAC pcmEncoderAAC; private void init() { //.... //初始化编码器 pcmEncoderAAC = new PCMEncoderAAC(sampleRateInHz, this); } @Override public void encodeAAC(byte[] data) { Log.d("TAG", "AAC数据长度:" + data.length); } 开始编码 private class RecordThread extends Thread { @Override public void run() { while (recorderState) { int read = audioRecord.read(buffer, 0, buffer.length); if (AudioRecord.ERROR_INVALID_OPERATION != read) { //获取到的pcm数据就是buffer了 Log.d("TAG", String.valueOf(buffer.length)); pcmEncoderAAC.encodeData(buffer); } } } } 四、将编码好的音频数据写入文件中 创建的文件输出流 private FileOutputStream fileOutputStream; private void init() { //... try { fileOutputStream = new FileOutputStream(new File(App.getAppContext().getExternalCacheDir(), "test.aac")); } catch (FileNotFoundException e) { e.printStackTrace(); } } @Override public void encodeAAC(byte[] data) { Log.d("TAG", "AAC数据长度:" + data.length); try { fileOutputStream.write(data); } catch (IOException e) { e.printStackTrace(); } } 生成的文件

在这里插入图片描述

五、 使用MediaRecorder直接录制AAC编码的音频到文件中 这种录制方式就简单许多了 public class MediaRecordUtil { private MediaRecorder mediaRecorder; private static MediaRecordUtil mediaRecordUtil = new MediaRecordUtil(); public static MediaRecordUtil getInstance() { return mediaRecordUtil; } private MediaRecordUtil() { init(); } private void init() { mediaRecorder = new MediaRecorder(); //配置采集方式,这里用的是麦克风的采集方式 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); //配置输出方式,这里用的是AAC mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS); //配置采样频率,频率越高月接近原始声音,Android所有设备都支持的采样频率为44100 mediaRecorder.setAudioSamplingRate(44100); //配置文件的编码格式,AAC是比较通用的编码格式 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); //配置码率,这里一般通用的是96000 mediaRecorder.setAudioEncodingBitRate(96000); //配置录音文件的位置 String path = App.getAppContext().getExternalCacheDir() + "/audio.aac"; mediaRecorder.setOutputFile(path); } /** * 开始录制 */ public void start() { try { mediaRecorder.prepare(); mediaRecorder.start(); } catch (IOException e) { e.printStackTrace(); } } /** * 停止录制 */ public void stop() { mediaRecorder.stop(); } } 启动录音 MediaRecordUtil.getInstance().start(); 停止录音 MediaRecordUtil.getInstance().stop(); 生成的文件

在这里插入图片描述

六、使用MediaRecorder可以很方便的直接将音频流写入到文件中,那么如果我们需要像AudioRecord那样获取到实时的音频流需要怎么弄呢?setOutputFile()的值传递为一个FileDescriptor文件描述符 然后开启子线程去读取文件描述符的数据 就是音频流数据了 public class MediaRecordUtil { private MediaRecorder mediaRecorder; private static MediaRecordUtil mediaRecordUtil = new MediaRecordUtil(); private ParcelFileDescriptor parcelWrite; private DataInputStream inputStream; private boolean recorderState; public static MediaRecordUtil getInstance() { return mediaRecordUtil; } private MediaRecordUtil() { intPipLine(); init(); } private void intPipLine() { try { ParcelFileDescriptor[] parcelFileDescriptors = ParcelFileDescriptor.createPipe(); ParcelFileDescriptor parcelRead = new ParcelFileDescriptor(parcelFileDescriptors[0]); parcelWrite = new ParcelFileDescriptor(parcelFileDescriptors[1]); inputStream = new DataInputStream(new ParcelFileDescriptor.AutoCloseInputStream(parcelRead)); } catch (IOException e) { e.printStackTrace(); } } private void init() { mediaRecorder = new MediaRecorder(); //.... //设置获取音频流的方式 mediaRecorder.setOutputFile(parcelWrite.getFileDescriptor()); } private class RecordThread extends Thread { private byte[] buffer = new byte[900]; @Override public void run() { while (recorderState) { try { int read = inputStream.read(buffer); if (read != -1) { byte[] data = Arrays.copyOfRange(buffer, 0, read); Log.e("TAG", "获取到的音频数据:" + data.length); } } catch (IOException e) { e.printStackTrace(); } } } } /** * 开始录制 */ public void start() { try { recorderState = true; new RecordThread().start(); mediaRecorder.prepare(); mediaRecorder.start(); } catch (IOException e) { e.printStackTrace(); } } /** * 停止录制 */ public void stop() { recorderState = false; mediaRecorder.stop(); } } Demo 下载地址
作者:Code-Porter



aac mediarecorder pcm Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章