Android(安卓)AudioRecord录音实时pcm 编码为 aac 文件
16lz
2021-12-19
实现类:
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(); } } }}
更多相关文章
- Android拍照、录像、录音代码范例
- android 录音
- Android音频系统之音量控制详解(Android(安卓)5.1)
- 如何向android程序添加音效或者音频文件
- Android支持的音频格式
- 快速的APK厂商快速和免费的Android应用软件,而无需编码技巧
- 快速的APK制作简易免费的Android应用软件,而无需编码技巧
- Android(安卓)二维码开发功能实现(四)------基于Zxing实现编码功
- Pro Android(安卓)Media:5 Android音频处理入门