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

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

一、使用AudioRecord录制pcm编码的音频

  • 首先需要录音权限,API>=6.0还需要动态申请 (动态申请权限代码略过,详情见文末源码)
<uses-permission android:name="android.permission.RECORD_AUDIO" />

二、录制主要分为一下几步

  • 创建AudioRecord
  • 设置参数
    • 录音来源
    • 采样率
    • 录制的声道
    • 数据格式
    • 录制的缓冲区大小
  • 开启一个线程读取音频数据

创建一个AduioRecord工具类

public class AudioRecordUtil {//设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025private 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 {@Overridepublic 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表示不等待有可能会丢帧,其他表示等待多少毫秒//获取输入缓存的indexint inputIndex = mediaCodec.dequeueInputBuffer(-1);if (inputIndex >= 0) {ByteBuffer inputByteBuf = encodeInputBuffers[inputIndex];inputByteBuf.clear();//添加数据inputByteBuf.put(data);//限制ByteBuffer的访问长度inputByteBuf.limit(data.length);//把输入缓存塞回去给MediaCodecmediaCodec.queueInputBuffer(inputIndex, 0, data.length, 0, 0);}//获取输出缓存的indexint outputIndex = mediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0);while (outputIndex >= 0) {//获取缓存信息的长度int byteBufSize = encodeBufferInfo.size;//添加ADTS头部后的长度int bytePacketSize = byteBufSize + 7;//拿到输出BufferByteBuffer 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 LCint profile = 2;// 44.1KHzint freqIdx = 4;// CPEint chanCfg = 2;// fill in ADTS datapacket[0] = (byte) 0xFF;packet[1] = (byte) 0xF9;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 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 {@Overridepublic 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);//配置输出方式,这里用的是AACmediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);//配置采样频率,频率越高月接近原始声音,Android所有设备都支持的采样频率为44100mediaRecorder.setAudioSamplingRate(44100);//配置文件的编码格式,AAC是比较通用的编码格式mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//配置码率,这里一般通用的是96000mediaRecorder.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];@Overridepublic 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 下载地址

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

  1. 【Android】【录音】Android录音--AudioRecord、MediaRecorder

    Android提供了两个API用于实现录音功能:android.media.AudioRecord.android.media.MediaRecorder. 网上有很多谈论这两个类的资料.现在大致总结 ...

  2. 《android多媒体api》之AudioRecord原始音频pcm录制api

    <android多媒体api>系列是整合梳理android开发中经常用到的媒体相关api:多媒体开发主要内容有音频.视频录制播放.摄像头操作.录制操作.流媒体.直播.推流.拉流等方面:最近 ...

  3. Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件(学习笔记)

    关于 AudioRecord Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风 ...

  4. [Android] [音视频系列]在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现读写音频 wav 文件

    参考 官方文档地址:https://developer.android.google.cn/reference/android/media/AudioRecord GitHub 地址:https:// ...

  5. Android 开发 AudioRecord音频录制

    前言 Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压 ...

  6. Android AudioRecord录音功能实现并pcm转wav

    简述 AudioRecord 类是管理Android应用程序的音频资源,以便开发者通过此类能够录制相关的硬件所收集的声音.这是通过从 AudioRecord 对象通过"pulling&quo ...

  7. 《android多媒体api》之AudioTrack原始音频pcm播放api

    <android多媒体api>系列是整合梳理android开发中经常用到的媒体相关api:多媒体开发主要内容有音频.视频录制播放.摄像头操作.录制操作.流媒体.直播.推流.拉流等方面:最近 ...

  8. Android如何将采集到的音频PCM文件转为WAV并保存

    1.Android音频采集 添加权限 <uses-permission android:name="android.permission.RECORD_AUDIO" /> ...

  9. Android 音频——PCM转WAV

    AudioRecord 录制音频PCM 数据转WAV PCM 简介 PCM(Puls Code Modulation)脉冲编码调制是数字通信的编码方式之一.主要过程是将话音.图像等模拟信号每隔一定时间 ...

最新文章

  1. 查找二叉树中出现次数最多的数 Find Mode in Binary Search Tree
  2. 水稻微生物组时间序列分析3-冲击图展示时间序序列变化
  3. 安装phpredis扩展
  4. php 类加载其它类,php 类自动载入的方法
  5. Android关机流程源码分析
  6. 除了游戏和医疗,腾讯区块链还准备做什么?
  7. 疑似小米11系列旗舰跑分曝光:骁龙875性能突破天际
  8. 微课|玩转Python轻松过二级(3.2节):元组与生成器推导式
  9. R语言系列:自定义function
  10. Android文件系统编译出错记录
  11. 关于浏览器及其内核以及什么是浏览器兼容性
  12. 笔记本显卡和台式显卡区别介绍
  13. 2023年2.14情人节最浪漫的表白烟花,送给自己的脑婆(源码)
  14. 2m带宽服务器多少个网站,2M带宽能撑起多少人访问?一文教会您所以带宽相关知识,以后选云服务器不求人...
  15. No MyBatis mapper was found in '[com.briup.demo]' package. Please check your
  16. 这几年各种新零售模式层出不穷
  17. 【计算机组成原理】计算机系统概述总结——基本知识要点汇总
  18. 比尔总动员警察职业详解
  19. Facebook要上演现实版纸牌屋?
  20. 敲黑板!《大数据产业发展规划(2016-2020年)》要点解读

热门文章

  1. 数据结构 - 树、常用树分类
  2. 截至4月印度可再生能源装机超57GW 光伏装机12.5GW
  3. 软考考试多少分通过?
  4. 卡西欧计算机有哪些功能介绍,卡西欧991计算机功能(考前必看)
  5. 基于ssm框架的汽车出租管理系统源码
  6. 28岁程序员网购生发丸吃成肝损伤;马斯克首次登上美国富豪榜首;微信新增退群保留聊天记录功能|极客头条
  7. php企业微信付款到零钱,企业付款到零钱功能介绍及常见问题
  8. XP如何显示文件扩展名
  9. jsp中include 的两种用法
  10. 我们基于kaldi开发的嵌入式语音识别系统升级成深度学习啦