基于FFmpeg的音频编码(PCM数据编码成AAC android)

概述

在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)

二、程序实现

代码截图如下:

QQ图片20170703214653.png

该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,可以正常播放。

三、运行效果

运行效果:

Screenshot_2017-07-03-22-04-41.png

运行结果将生成文件 /sdcard/test.aac

四、其他补充

博客地址:http://blog.csdn.net/vnanyesheshou/article/details/54560684

 

 

 

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

 

posted on   demo例子集  阅读(3894)  评论(0编辑  收藏  举报

(评论功能已被禁用)
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示