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

发布时间:2017-07-04
技术:Android4.4+jdk1.8

概述

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

开始录音

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

三、运行效果

运行效果:

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

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

四、其他补充

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




本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢