概述
在Android上实现录音,并利用 FFmpeg将PCM数据编码成AAC。
详细
之前做的一个demo,Android录音获取pcm数据(音频原始数据),然后利用 FFmpeg将PCM数据编码成AAC。
一、准备工作
开发环境
jdk1.8
Eclipse Luna Service Release 1 (4.4.1)
运行环境:
华为荣耀6(Android4.4)、华为p9(Android7.0)
二、程序实现
代码截图如下:
该demo依赖v7包。
FFAacEncoderDemo工程src目录
-
MainActivity.java是主界面,用来控制录音开始和结束。
-
FFAacEncoder.java时native方法,用来和jni通信。
jni目录:
-
include目录下是FFmpeg的一些.h文件
-
libs目录下是FFmpeg编译的so
-
AacCoderc 音频编码
-
cn_vn_aacEncoder_jni_FFAacEncoderJni jni代码与java通信。
1 录音(获取pcm数据)
开始录音
private void startRecord(){ Log.i(TAG, "startRecord mIsRecording="+mIsRecording); if(!mIsRecording){ mIsRecording = true; synchronized (mLock) { mAudioRecordGetExit = false; } //初始化ffmpeg 编码器 mFFAacEncoderJni.start(); //创建录音线程、开始录音 mAudioRecordGetThread = new Thread(new AudioRecordGet()); mAudioRecordGetThread.start(); } }
关闭录音
private void stoptRecord(){ if(mIsRecording){ synchronized (mLock) { mAudioRecordGetExit = true; } mIsRecording = false; } }
具体录音通过使用AudioRecord。
private class AudioRecordGet implements Runnable{ private AudioRecord mAudioRecord; private static final boolean PCM_DUMP_DEBUG = true; private static final boolean AAC_DUMP_DEBUG = false; private int mAudioSource = MediaRecorder.AudioSource.MIC; //采样频率,采样频率越高,音质越好。44100 、22050、 8000、4000等 private int mSampleRateHz = 8000; //MONO为单声道 ,STEREO为双声道 private int mChannelConfig = AudioFormat.CHANNEL_IN_MONO; //编码格式和采样大小,pcm编码;支持的采样大小16bit和8bit,采样大小越大,信息越多,音质越好。 private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; //该size设置为AudioRecord.getMinBufferSize(mSampleRateHz, mChannelConfig, mAudioFormat); 编码aac时会失败。 private int mBufferSizeInBytes = 2048;//AudioRecord.getMinBufferSize(mSampleRateHz, mChannelConfig, mAudioFormat); private AudioPCMData mAudioPCMData; public AudioRecordGet() { Log.i(TAG, "AudioRecordGet "); mAudioPCMData = new AudioPCMData(mBufferSizeInBytes); mAudioRecord = new AudioRecord(mAudioSource, mSampleRateHz, mChannelConfig, mAudioFormat, mBufferSizeInBytes); Log.i(TAG,"mBufferSizeInBytes="+mBufferSizeInBytes); } @Override public void run() { mAudioRecord.startRecording(); FileOutputStream outPCM = null; try { if (PCM_DUMP_DEBUG) { String File = "/sdcard/test.pcm"; outPCM = new FileOutputStream(File); } } catch (Exception e) { e.printStackTrace(); } for(;;){ synchronized (mLock) { if(mAudioRecordGetExit){ break; } } //读取录音数据 int readSize = mAudioRecord.read(mAudioPCMData.mData, 0, mBufferSizeInBytes); if (AudioRecord.ERROR_INVALID_OPERATION != readSize) { if (PCM_DUMP_DEBUG && null != outPCM) { try { outPCM.write(mAudioPCMData.mData, 0, readSize); } catch (Exception e) { e.printStackTrace(); } } mAudioPCMData.mFrameSize = readSize; Log.i(TAG, "audio pcm size="+readSize); //设置pcm数据,进行aac编码 mFFAacEncoderJni.setPcmData(mAudioPCMData.mData, readSize); } } if(PCM_DUMP_DEBUG && null != outPCM){ try { outPCM.close(); } catch (IOException e) { e.printStackTrace(); } } //停止录音、释放 mAudioRecord.stop(); mAudioRecord.release(); //停止音频编码 mFFAacEncoderJni.stop(); Log.i(TAG,"AudioRecordGet thread exit success"); } }
2 PCM编码成AAC
1 初始化编码器
public class FFAacEncoder { private String TAG = "FFAacEncoder java"; //load .so static{ System.loadLibrary("avcodec-57"); System.loadLibrary("avdevice-57"); System.loadLibrary("avfilter-6"); System.loadLibrary("avformat-57"); System.loadLibrary("avutil-55"); System.loadLibrary("postproc-54"); System.loadLibrary("swresample-2"); System.loadLibrary("swscale-4"); System.loadLibrary("aacEncoder"); } private int mNativeContext = 0; //初始化编码器 private native final void nativeStart(); //对pcm数据进行编码 private native final void nativeSetPcmData(byte[] pcm, int len); //必要的清理 private native final void nativeStop(); public void start(){ nativeStart(); } public void setPcmData(byte[] pcm, int len){ nativeSetPcmData(pcm, len); } public void stop(){ nativeStop(); } }
调用nativeStart方法。
2 音频编码
//设置pcm数据,进行aac编码 mFFAacEncoderJni.setPcmData(mAudioPCMData.mData, readSize);
调用nativeSetPcmData
C++层代码,通过编码获取的AAC原始数据不同播放(存储在/sdcard/test.aac文件中,不能播放),
需要添加adts header(不懂的可以了解一下AAC格式),这样才可以正常播放。/sdcard/adts.aac该文件添加了header,可以正常播放。
三、运行效果
运行效果:
运行结果将生成文件 /sdcard/test.aac
四、其他补充
博客地址:http://blog.csdn.net/vnanyesheshou/article/details/54560684