基于FFmpeg的音频编码(PCM数据编码成AAC android)
概述
详细
之前做的一个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数据)
开始录音
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(); } } |
关闭录音
1 2 3 4 5 6 7 8 | private void stoptRecord(){ if (mIsRecording){ synchronized (mLock) { mAudioRecordGetExit = true ; } mIsRecording = false ; } } |
具体录音通过使用AudioRecord。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 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 初始化编码器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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 音频编码
1 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
注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?