android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM音频数据
16lz
2021-01-23
音视频实践学习
- android全平台编译ffmpeg以及x264与fdk-aac实践
- ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器
- android全平台编译ffmpeg合并为单个库实践
- android-studio使用cmake编译ffmpeg实践
- android全平台下基于ffmpeg解码MP4视频文件为YUV文件
- android全平台编译ffmpeg支持命令行实践
- android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
- android平台下音频编码之编译LAME库转码PCM为MP3
- ubuntu平台下编译vlc-android视频播放器实践
- 图解YU12、I420、YV12、NV12、NV21、YUV420P、YUV420SP、YUV422P、YUV444P的区别
- 图解RGB565、RGB555、RGB16、RGB24、RGB32、ARGB32等格式的区别
- YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片
- android全平台编译libyuv库实现YUV和RGB的转换
- android平台下基于ffmpeg对相机采集的NV21数据编码为MP4视频文件
- android平台下基于ffmpeg采集Camera数据编码成H.264推流到RTMP服务器
- android平台下基于ffmpeg和ANativeWindow实现简单的视频播放器
- android平台下基于ffmpeg实现对相机预览截图的功能(NV21数据编码为JPEG文件)
- android平台下基于ffmpeg的swscale模块实现对YUV和RGB数据进行转换
- android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM音频数据
概述
在android sdk
中提供了两种方式来实现音频的采集:MediaRecorder
和AudioRecord
,其中的MediaRecorder
处于更上层,它可以对音频录制的数据编码成AMR
,MP3
等格式,并存储为文件,而AudioRecord
则更灵活,因为它可以录制最原始的PCM流数据
,这个在直播中很常见。
录制接口
使用不同的方式处理音频,API肯定有所不同,这里暂时先抽象一个接口:
public interface IAudioRecorder { interface RECORD_STATE { int STATE_RECORDING = 0; int STATE_SUCCESS = 1; int STATE_ERROR = 2; } /** * 初始化 */ void initRecorder(); /** * 开始录制音频 */ int recordStart(); /** * 停止录制音频 */ void recordStop();}
基于MediaRecorder
系统都已经给你提供好了的轮子,我们只需要设置几个参数就可以了:
public class MediaRecordRecorder implements IAudioRecorder { /** * 基于MediaRecorder录制音频 */ private MediaRecorder mMediaRecorder; /** * 是否正在录制 */ private boolean isRecord = false; private String filePath; public MediaRecordRecorder(String filePath){ this.filePath = filePath; } @Override public void initRecorder() { //实例化MediaRecorder对象 mMediaRecorder = new MediaRecorder(); //从麦克风采集声音数据 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); //设置输出格式为MP4 mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //设置采样频率44100 频率越高,音质越好,文件越大 mMediaRecorder.setAudioSamplingRate(44100); //设置声音数据编码格式,音频通用格式是AAC mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); //设置编码频率 mMediaRecorder.setAudioEncodingBitRate(96000); //设置输出文件 mMediaRecorder.setOutputFile(filePath); } @Override public int recordStart() { //判断是否有外部存储设备sdcard if (isRecord) { return RECORD_STATE.STATE_RECORDING; } else { if (mMediaRecorder != null) { try { mMediaRecorder.prepare(); mMediaRecorder.start(); // 让录制状态为true isRecord = true; return RECORD_STATE.STATE_SUCCESS; } catch (IOException e) { e.printStackTrace(); } } return RECORD_STATE.STATE_ERROR; } } @Override public void recordStop() { if (mMediaRecorder != null) { isRecord = false; mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder = null; } }}
笔者这里只是作演示操作,因此部分参数直接硬编码,实际过程最好以参数的形式对外提供
基于AudioRecord
这种方式需要我们自己在线程中,循环读取音频数据:
public class AudioRecordRecorder implements IAudioRecorder { private static final String TAG = "AudioRecordRecorder"; private int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC; /** * 音频采样率 */ public static int SAMPLE_RATE = 44100; /** * 单声道 */ public final static int CHANNEL = AudioFormat.CHANNEL_IN_STEREO; /** * 16比特 */ public final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; /** * 音频录制实例 */ protected AudioRecord audioRecord; /** * 录制线程 */ private Thread recordThread; /** * 输出的文件路径 */ private String pcmPath; /** * 缓冲区大小 */ private int bufferSize = 0; /** * 是否正在录制 */ private boolean isRecording = false; /** * 回调原始的PCM数据 */ private OnAudioRecordListener mOnAudioRecordListener; public AudioRecordRecorder(String filePath) { this.pcmPath = filePath; } @Override public void initRecorder() { if (null != audioRecord) { audioRecord.release(); } try { bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL, AUDIO_FORMAT); audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE, CHANNEL, AUDIO_FORMAT, bufferSize); } catch (Exception e) { e.printStackTrace(); } } @Override public int recordStart() { if (isRecording) { return RECORD_STATE.STATE_RECORDING; } else if (audioRecord != null && audioRecord.getState() == AudioRecord.STATE_INITIALIZED) { try { audioRecord.startRecording(); isRecording = true; recordThread = new Thread(new AudioRecordRunnable()); recordThread.start(); return RECORD_STATE.STATE_SUCCESS; } catch (Exception e) { e.printStackTrace(); } } return RECORD_STATE.STATE_ERROR; } /** * 停止音频录制 */ @Override public void recordStop() { try { if (audioRecord != null) { isRecording = false; try { if (recordThread != null) { recordThread.join(); recordThread = null; } } catch (InterruptedException e) { e.printStackTrace(); } //释放资源 recordRelease(); } } catch (Exception e) { e.printStackTrace(); } } private void recordRelease() { if (audioRecord != null) { if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) { audioRecord.stop(); } audioRecord.release(); audioRecord = null; } } private class AudioRecordRunnable implements Runnable { private FileOutputStream outputStream = null; @Override public void run() { try { if (!TextUtils.isEmpty(pcmPath)) { outputStream = new FileOutputStream(pcmPath); } byte[] audioBuffer = new byte[bufferSize]; while (isRecording && audioRecord != null) { int audioSampleSize = audioRecord.read(audioBuffer, 0, bufferSize); if (audioSampleSize > 0) { if (outputStream != null) { outputStream.write(audioBuffer); } if (mOnAudioRecordListener != null) { mOnAudioRecordListener.onAudioBuffer(audioBuffer, audioBuffer.length); } } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { close(outputStream); outputStream = null; } } } private void close(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } public void setOnAudioRecordListener(OnAudioRecordListener onAudioRecordListener) { this.mOnAudioRecordListener = onAudioRecordListener; } public interface OnAudioRecordListener { void onAudioBuffer(byte[] buffer, int length); }}
工程实践
新建工程ffmpeg-audio-encode
,在应用的点击事件中处理如下:
/** * 点击开始录制按钮 * @param view */ public void onRecordStart(View view) { File targetDir = getExternalFilesDir(null); if (audioRecorder == null) { audioRecorder = new MediaRecordRecorder(targetDir.getAbsolutePath()+ File.separator+"output.aac"); //audioRecorder = new AudioRecordRecorder(targetDir.getAbsolutePath()+ File.separator+"output.pcm"); } audioRecorder.initRecorder(); audioRecorder.recordStart(); //更新按钮状态 mBtnStart.setEnabled(false); mBtnStop.setEnabled(true); } /** * 点击停止按钮 * @param view */ public void onRecordStop(View view) { if (audioRecorder != null) { audioRecorder.recordStop(); audioRecorder = null; } //更新按钮状态 mBtnStart.setEnabled(true); mBtnStop.setEnabled(false); }
基于MediaRecorder
方式我们可以直接得到output.aac
文件,这个直接使用系统的音频播放器
即可播放
基于AudioRecord
方式我们可以直接得到output.ocm
文件,需要导出到电脑上,使用ffplay播放器
进行播放
ffplay -ar 44100 -channels 2 -f s16le -i output.pcm
项目地址:ffmpeg-audio-encode
https://github.com/byhook/ffmpeg4android
更多相关文章
- 详解Android中的SQLite数据库存储
- android基于tcpdump的数据包捕获完整解决方案
- 【最近读书】《Android平台开发之旅(第2版)》
- Android 判断用户2G/3G/4G移动数据网络
- android数据库 Android自带数据库SQLite 操作 一步一步 图示教程
- Android Studio中使用adb shell查看数据库
- 共享全局数据 android
- Android 数据Parcel序列化过程源码分析
- Android Studio 2.2 发布,速度、智能和 Android 平台支持三个方面