Android G711(PCMA/PCMU)、G726、PCM音頻轉碼到AAC

Android G711(PCMA/PCMU)、G726、PCM音頻轉碼到AAC

首先先介紹下使用的開源庫,當然還是要感謝開源庫的作者們爲我們提供了方便。

採用EasyAACEncoder 此是EasyDarwin開源流媒體服務團隊整理、開發的一款音頻轉碼到AAC的工具庫,目前支持G711a/G711u/G726/PCM等音頻格式的轉碼,跨平臺,支持Windows/Linux/arm.

說明

EasyAACEncoder目前支持的音視頻格式:

/* Audio Codec */
enum Law
{
    Law_ULaw    =   0,      /**< U law */
    Law_ALaw    =   1,      /**< A law */
    Law_PCM16   =   2,      /**< 16 bit uniform PCM values. 原始 pcm 數據 */  
    Law_G726    =   3       /**< G726 */
};

/* Rate Bits */
enum Rate
{
    Rate16kBits=2,  /**< 16k bits per second (2 bits per ADPCM sample) */
    Rate24kBits=3,  /**< 24k bits per second (3 bits per ADPCM sample) */
    Rate32kBits=4,  /**< 32k bits per second (4 bits per ADPCM sample) */
    Rate40kBits=5   /**< 40k bits per second (5 bits per ADPCM sample) */
};

更多信息

詳情請到:GitHub地址

迴歸正題

具體Android怎麼使用該庫呢,有以下步驟:

1.在上面地址下載開源庫解壓

2.把解壓後的開源庫放到項目jni文件夾裏

3.去除無用東西,對有錯誤的進行剔除與修正

4.新建個調用c方法的類文件並寫好調用方法

5.新建Android.mk文件與Application.mk文件完成相應配置

6.編輯打包so即可完成

熟練的大神們分分鐘鍾就搞定了,像我這樣不熟悉的還是多多看看資料先寫個小的demo練練手之後在來編譯就簡單多了。
在此過程中第三步與第四步麻煩是最多的。
現在簡單說下
在第三步中要先去掉無用的文件,我直接把非.c源文件與.h頭文件的直接剔除如下圖我的:

該開源庫也採用了libfacc如圖:

之後如果仍然會有報錯的文件,找到報錯的所在地再解決,有的需要項目裏關聯C/C++ build纔可以,有的還需要配置環境,具體網上教程有很多,就不在累贅,找到適合自己的就行。
還有就是c的方法與c++的方法有的地方不一樣的需要格外注意下比如說env。
對四步主要看你的Java層怎麼寫方法調用了,然後編譯自動生成即可,或者自己編寫也行。
Java代碼文件方法比如我的:

    public class JNIAACEncode {

        public static final int Law_ULaw = 0;/**< U law */
        public static final int Law_ALaw = 1;/**< A law */
        public static final int Law_PCM16 = 2;/**< 16 bit uniform PCM values. 原始 pcm 數據 */  
        public static final int Law_G726 = 3;/**< G726 */

        //首先進行實例化audioCodec爲上面聲明的音頻類型
        public static native void init(int audioCodec);
        //原音頻文件路徑與轉碼後的音頻文件路徑
        public static native int encode(String infilename, String outAacname);

        static {
            System.loadLibrary("AACEncode");
        }
    }

對應的c代碼爲:

    JNIEXPORT void JNICALL Java_com_aacencoder_JNIAACEncode_init(JNIEnv *env,jobject obj, jint law) {
        InitParam initParam;
        initParam.u32AudioSamplerate = 8000;
        initParam.ucAudioChannel = 1;
        initParam.u32PCMBitSize = 16;
        if (Law_ALaw == law) {
            initParam.ucAudioCodec = Law_ALaw;
        } else if (Law_ULaw == law) {
            initParam.ucAudioCodec = Law_ULaw;
        } else if (Law_PCM16 == law) {
            initParam.ucAudioCodec = Law_PCM16;
        } else if (Law_G726 == law) {
            initParam.ucAudioCodec = Law_G726;
            initParam.g726param.ucRateBits = Rate40kBits;
        } else {
            LOGE("Java_com_aacencoder_JNIAACEncode_g711Ainit law failure =%d", law);
            return;
        }
        handle = Easy_AACEncoder_Init(initParam);
        LOGE("Java_com_aacencoder_JNIAACEncode_g711Ainit env=%p", env);
    }

轉碼方法爲:

    JNIEXPORT jint JNICALL Java_com_aacencoder_JNIAACEncode_encode(JNIEnv *env,
        jobject obj, jstring jinfilename, jstring joutAacname) {

        char *infilename = jstringTostr(env, jinfilename); //類型進行轉換
        char *outAacname = jstringTostr(env, joutAacname); //類型進行轉換

        LOGE("inFilename = %s", infilename);
        LOGE("outAacname = %s", outAacname);

        //1.打開 wav,MP3文件
        //  FILE* fwav = fopen(cwav, "rb");
        //  FILE* fmp3 = fopen(cmp3, "wb");

        //  char* infilename = "g711.g711a";  //標準
        //  char* outAacname = "g711.aac";

        FILE* fpIn = fopen(infilename, "rb");
        if (NULL == fpIn) {
            printf("%s:[%d] open %s file failed\n", __FUNCTION__, __LINE__,
                    infilename);
            return -1;
        }

        FILE* fpOut = fopen(outAacname, "wb");
        if (NULL == fpOut) {
            printf("%s:[%d] open %s file failed\n", __FUNCTION__, __LINE__,
                    outAacname);
            return -1;
        }

        int gBytesRead = 0;
        int bG711ABufferSize = 500;
        int bAACBufferSize = 4 * bG711ABufferSize;  //提供足夠大的緩衝區
        unsigned char *pbG711ABuffer = (unsigned char *) malloc(
                bG711ABufferSize * sizeof(unsigned char));
        unsigned char *pbAACBuffer = (unsigned char*) malloc(
                bAACBufferSize * sizeof(unsigned char));
        unsigned int out_len = 0;

        while ((gBytesRead = fread(pbG711ABuffer, 1, bG711ABufferSize, fpIn)) > 0) {
            if (Easy_AACEncoder_Encode(handle, pbG711ABuffer, gBytesRead,
                    pbAACBuffer, &out_len) > 0) {
                fwrite(pbAACBuffer, 1, out_len, fpOut);
            }
        }
        Easy_AACEncoder_Release(handle);
        free(pbG711ABuffer);
        free(pbAACBuffer);
        fclose(fpIn);
        fclose(fpOut);

        return 0;

    }

上面方法別忘了引入頭文件:

    #include <jni.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>

    #include "EasyAACEncoderAPI.h"
    #include "com_aacencoder_JNIAACEncode.h"
    #include <android/log.h>
    #define  LOG_TAG    "VIDEO_AAC"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    #define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
    #define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

把這些搞定基本完成80%啦,又可以愉快玩耍了。
下面要進行第五步了那就是編寫配置文件:
這配置文件很簡單,大家應該都會就不多說了,只不過要提醒下LOCAL_SRC_FILES列出所有文件要注意c文件與cpp文件區別。
如下我的:

    LOCAL_PATH := $(call my-dir)

    #生成so動態庫
    include $(CLEAR_VARS)

    LOCAL_MODULE    := AACEncode

    LOCAL_SRC_FILES := \
    ./libfaac/aacquant.c \
    ./libfaac/backpred.c \
    ./libfaac/bitstream.c \
    ./libfaac/channels.c \
    ./libfaac/fft.c \
    ./libfaac/filtbank.c \
    ./libfaac/frame.c \
    ./libfaac/huffman.c\
    ./libfaac/ltp.c \
    ./libfaac/midside.c \
    ./libfaac/psychkni.c \
    ./libfaac/tns.c \
    ./libfaac/util.c \
    ./libfaac/kiss_fft/kiss_fft.c \
    ./libfaac/kiss_fft/kiss_fftr.c \
    audio_buffer.cpp EasyAACEncoder.cpp EasyAACEncoderAPI.cpp g711.cpp G711AToPcm.cpp g726.cpp G726ToPcm.cpp IDecodeToPcm.cpp PcmToAac.cpp AACEncoder.c

    LOCAL_LDLIBS += -llog


    include $(BUILD_SHARED_LIBRARY)

然後編譯運行即可生成so庫啦啦啦~
在libs文件裏就能找到的~
本人使用的還Eclipse工具編譯,由於Android Studio支持不是很好也比較麻煩。
就簡單的說這些吧,提醒下對於第三步與第四步問題估計比較多,有問題那就多多去谷歌,問題會解決的!
GitHub源代碼
點擊CSDN下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章