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下載