基於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數據)

開始錄音

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

 

 

 

注:本文著作權歸作者,由demo大師發表,拒絕轉載,轉載需要作者授權

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM