Android JNI入门第六篇——C调用Java
最后更新于:2022-04-01 07:04:03
本篇将介绍在JNI编程中C调用Java实现。
源码下载地址:[http://download.csdn.net/detail/xyz_lmn/4868265](http://download.csdn.net/detail/xyz_lmn/4868265)
关键代码:
**java:**
~~~
public class CCallJava {
public static String getTime() {
Log.d("CCallJava", "Call From C Java Static Method" +String.valueOf(System.currentTimeMillis()));
return String.valueOf(System.currentTimeMillis());
}
public void sayHello(String msg) {
Log.d("CCallJava", "Call From C Java void Method" +String.valueOf(System.currentTimeMillis()));
}
}
~~~
**C:**
~~~
#include "TestCCallJava.h"
#include <android/log.h>
extern JNIEnv* jniEnv;
jclass TestCCallJava;
jobject mTestCCallJava;
jmethodID getTime;
jmethodID sayHello;
int GetTestCCallJavaInstance(jclass obj_class);
/**
* 初始化 类、对象、方法
*/
int InitTestCCallJava() {
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitTestCCallJava Begin 1" );
if(jniEnv == NULL) {
return 0;
}
if(TestCCallJava == NULL) {
TestCCallJava = (*jniEnv)->FindClass(jniEnv,"com/trunkbow/ccalljava/CCallJava");
if(TestCCallJava == NULL){
return -1;
}
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitTestCCallJava Begin 2 ok" );
}
if (mTestCCallJava == NULL) {
if (GetTestCCallJavaInstance(TestCCallJava) != 1) {
(*jniEnv)->DeleteLocalRef(jniEnv, TestCCallJava);
return -1;
}
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitTestCCallJava Begin 3 ok" );
}
if (getTime == NULL) {
getTime = (*jniEnv)->GetStaticMethodID(jniEnv, TestCCallJava, "getTime","()Ljava/lang/String;");
if (getTime == NULL) {
(*jniEnv)->DeleteLocalRef(jniEnv, TestCCallJava);
(*jniEnv)->DeleteLocalRef(jniEnv, mTestCCallJava);
return -2;
}
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitTestCCallJava Begin 4 ok" );
}
if (sayHello == NULL) {
sayHello = (*jniEnv)->GetMethodID(jniEnv, TestCCallJava, "sayHello","(Ljava/lang/String;)V");
if (sayHello == NULL) {
(*jniEnv)->DeleteLocalRef(jniEnv, TestCCallJava);
(*jniEnv)->DeleteLocalRef(jniEnv, mTestCCallJava);
(*jniEnv)->DeleteLocalRef(jniEnv, getTime);
return -3;
}
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitTestCCallJava Begin 5 ok" );
}
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitTestCCallJava Begin 6" );
return 1;
}
int GetTestCCallJavaInstance(jclass obj_class) {
if(obj_class == NULL) {
return 0;
}
jmethodID construction_id = (*jniEnv)->GetMethodID(jniEnv, obj_class,
"<init>", "()V");
if (construction_id == 0) {
return -1;
}
mTestCCallJava = (*jniEnv)->NewObject(jniEnv, obj_class,
construction_id);
if (mTestCCallJava == NULL) {
return -2;
}
return 1;
}
/**
* 获取时间 ---- 调用 Java 方法
*/
void GetTime() {
if(TestCCallJava == NULL || getTime == NULL) {
int result = InitTestCCallJava();
if (result != 1) {
return;
}
}
jstring jstr = NULL;
char* cstr = NULL;
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "GetTime Begin" );
jstr = (*jniEnv)->CallStaticObjectMethod(jniEnv, TestCCallJava, getTime);
cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Success Get Time from Java , Value = %s",cstr );
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "GetTime End" );
(*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);
(*jniEnv)->DeleteLocalRef(jniEnv, jstr);
}
/**
* SayHello ---- 调用 Java 方法
*/
void SayHello() {
if(TestCCallJava == NULL || mTestCCallJava == NULL || sayHello == NULL) {
int result = InitTestCCallJava() ;
if(result != 1) {
return;
}
}
jstring jstrMSG = NULL;
jstrMSG =(*jniEnv)->NewStringUTF(jniEnv, "Hi,I'm From C");
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "SayHello Begin" );
(*jniEnv)->CallVoidMethod(jniEnv, mTestCCallJava, sayHello,jstrMSG);
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "SayHello End" );
(*jniEnv)->DeleteLocalRef(jniEnv, jstrMSG);
}
~~~
关键代码说明:
**C中定义映射的类、方法、对象**
~~~
jclass TestCCallJava;
jobject mTestCCallJava;
jmethodID getTime;
jmethodID sayHello;
~~~
**InitTestCCallJava()方法初始化类、方法、对象**
初始化类:
~~~
TestCCallJava = (*jniEnv)->FindClass(jniEnv,"com/trunkbow/ccalljava/CCallJava");
~~~
初始化对象:
~~~
mTestCCallJava = (*jniEnv)->NewObject(jniEnv, obj_class,construction_id);
~~~
初始化方法:
静态方法:
~~~
getTime = (*jniEnv)->GetStaticMethodID(jniEnv, TestCCallJava, "getTime","()Ljava/lang/String;");
~~~
非静态方法:
~~~
sayHello = (*jniEnv)->GetMethodID(jniEnv, TestCCallJava, "sayHello","(Ljava/lang/String;)V");
~~~
**C 中调用 Java的 方法**
调用静态方法:
~~~
jstring jstr = NULL;
char* cstr = NULL;
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "GetTime Begin" );
jstr = (*jniEnv)->CallStaticObjectMethod(jniEnv, TestCCallJava, getTime);
cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Success Get Time from Java , Value = %s",cstr );
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "GetTime End" );
~~~
调用非静态方法
~~~
jstring jstrMSG = NULL;
jstrMSG =(*jniEnv)->NewStringUTF(jniEnv, "Hi,I'm From C");
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "SayHello Begin" );
(*jniEnv)->CallVoidMethod(jniEnv, mTestCCallJava, sayHello,jstrMSG);
__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "SayHello End" );
~~~
注意 GetXXXMethodID 和 CallXXXMethod 。
第一个XXX 表示的是映射方法的类型,如: 静态 跟非静态
第二个 XXX 表示 调用方法的返回值 ,如:Void,Object,等等。(调用静态方法的时候Call后面要加Static)
详细 映射方法 和 调用方法 请参考 [JNI 文档](http://files.cnblogs.com/luxiaofeng54/JNI_Docs.rar) ,这个很重要 !
**工程的其他代码可在[http://download.csdn.net/detail/xyz_lmn/4868265](http://download.csdn.net/detail/xyz_lmn/4868265)下载**
**参考:**
[http://game.ceeger.com/Script/AndroidJNI/AndroidJNI.html](http://game.ceeger.com/Script/AndroidJNI/AndroidJNI.html)
Silk编解码在android实现
最后更新于:2022-04-01 07:04:01
Silk编解码是Skype向第三方开发人员和硬件制造商提供免版税认证(RF)的Silk宽带音频编码器。Skype已将其开源,可以访问[http://developer.skype.com/silk](http://developer.skype.com/silk)获取最新动向。SILK Codec是一个语音和音频编解码算法, 对于音频带宽、网络带宽和算法复杂度都具有很好的弹性。支持4种采样率:8KHz、12KHz、16KHz、24KHz;三种复杂度:低、中、高。编码码率在 6~40kbps(不同采样率具有不同的码率范围)以及还支持VAD、DTX、FEC等模块,感觉还是比较全面。最重要的一点是提供了定点C代码,非常有利于向ARM、DSP移植和优化。这一篇主要参考了pjsip中的silk实现。
1、获取silk源码([http://developer.skype.com/silk](http://developer.skype.com/silk))
2、创建新的android工程,并创建jni文件夹。
3、将silk源码拷贝到jni目录
4、在jni目录下新增Android.mk文件,编辑内容如下:
~~~
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
SILK := silk
LOCAL_MODULE := silkcommon
LOCAL_SRC_FILES := $(SILK)/src/SKP_Silk_A2NLSF.c \
$(SILK)/src/SKP_Silk_CNG.c \
$(SILK)/src/SKP_Silk_HP_variable_cutoff_FIX.c \
$(SILK)/src/SKP_Silk_LBRR_reset.c \
$(SILK)/src/SKP_Silk_LPC_inv_pred_gain.c \
$(SILK)/src/SKP_Silk_LPC_stabilize.c \
$(SILK)/src/SKP_Silk_LPC_synthesis_filter.c \
$(SILK)/src/SKP_Silk_LPC_synthesis_order16.c \
$(SILK)/src/SKP_Silk_LP_variable_cutoff.c \
$(SILK)/src/SKP_Silk_LSF_cos_table.c \
$(SILK)/src/SKP_Silk_LTP_analysis_filter_FIX.c \
$(SILK)/src/SKP_Silk_LTP_scale_ctrl_FIX.c \
$(SILK)/src/SKP_Silk_MA.c \
$(SILK)/src/SKP_Silk_NLSF2A.c \
$(SILK)/src/SKP_Silk_NLSF2A_stable.c \
$(SILK)/src/SKP_Silk_NLSF_MSVQ_decode.c \
$(SILK)/src/SKP_Silk_NLSF_MSVQ_encode_FIX.c \
$(SILK)/src/SKP_Silk_NLSF_VQ_rate_distortion_FIX.c \
$(SILK)/src/SKP_Silk_NLSF_VQ_sum_error_FIX.c \
$(SILK)/src/SKP_Silk_NLSF_VQ_weights_laroia.c \
$(SILK)/src/SKP_Silk_NLSF_stabilize.c \
$(SILK)/src/SKP_Silk_NSQ.c \
$(SILK)/src/SKP_Silk_NSQ_del_dec.c \
$(SILK)/src/SKP_Silk_PLC.c \
$(SILK)/src/SKP_Silk_VAD.c \
$(SILK)/src/SKP_Silk_VQ_nearest_neighbor_FIX.c \
$(SILK)/src/SKP_Silk_allpass_int.c \
$(SILK)/src/SKP_Silk_ana_filt_bank_1.c \
$(SILK)/src/SKP_Silk_apply_sine_window.c \
$(SILK)/src/SKP_Silk_array_maxabs.c \
$(SILK)/src/SKP_Silk_autocorr.c \
$(SILK)/src/SKP_Silk_biquad.c \
$(SILK)/src/SKP_Silk_biquad_alt.c \
$(SILK)/src/SKP_Silk_burg_modified.c \
$(SILK)/src/SKP_Silk_bwexpander.c \
$(SILK)/src/SKP_Silk_bwexpander_32.c \
$(SILK)/src/SKP_Silk_code_signs.c \
$(SILK)/src/SKP_Silk_control_codec_FIX.c \
$(SILK)/src/SKP_Silk_corrMatrix_FIX.c \
$(SILK)/src/SKP_Silk_create_init_destroy.c \
$(SILK)/src/SKP_Silk_dec_API.c \
$(SILK)/src/SKP_Silk_decode_core.c \
$(SILK)/src/SKP_Silk_decode_frame.c \
$(SILK)/src/SKP_Silk_decode_indices_v4.c \
$(SILK)/src/SKP_Silk_decode_parameters.c \
$(SILK)/src/SKP_Silk_decode_parameters_v4.c \
$(SILK)/src/SKP_Silk_decode_pulses.c \
$(SILK)/src/SKP_Silk_decoder_set_fs.c \
$(SILK)/src/SKP_Silk_detect_SWB_input.c \
$(SILK)/src/SKP_Silk_enc_API.c \
$(SILK)/src/SKP_Silk_encode_frame_FIX.c \
$(SILK)/src/SKP_Silk_encode_parameters.c \
$(SILK)/src/SKP_Silk_encode_parameters_v4.c \
$(SILK)/src/SKP_Silk_encode_pulses.c \
$(SILK)/src/SKP_Silk_find_LPC_FIX.c \
$(SILK)/src/SKP_Silk_find_LTP_FIX.c \
$(SILK)/src/SKP_Silk_find_pitch_lags_FIX.c \
$(SILK)/src/SKP_Silk_find_pred_coefs_FIX.c \
$(SILK)/src/SKP_Silk_gain_quant.c \
$(SILK)/src/SKP_Silk_init_encoder_FIX.c \
$(SILK)/src/SKP_Silk_inner_prod_aligned.c \
$(SILK)/src/SKP_Silk_interpolate.c \
$(SILK)/src/SKP_Silk_k2a.c \
$(SILK)/src/SKP_Silk_k2a_Q16.c \
$(SILK)/src/SKP_Silk_lin2log.c \
$(SILK)/src/SKP_Silk_log2lin.c \
$(SILK)/src/SKP_Silk_lowpass_int.c \
$(SILK)/src/SKP_Silk_lowpass_short.c \
$(SILK)/src/SKP_Silk_noise_shape_analysis_FIX.c \
$(SILK)/src/SKP_Silk_pitch_analysis_core.c \
$(SILK)/src/SKP_Silk_pitch_est_tables.c \
$(SILK)/src/SKP_Silk_prefilter_FIX.c \
$(SILK)/src/SKP_Silk_process_NLSFs_FIX.c \
$(SILK)/src/SKP_Silk_process_gains_FIX.c \
$(SILK)/src/SKP_Silk_pulses_to_bytes.c \
$(SILK)/src/SKP_Silk_quant_LTP_gains_FIX.c \
$(SILK)/src/SKP_Silk_range_coder.c \
$(SILK)/src/SKP_Silk_regularize_correlations_FIX.c \
$(SILK)/src/SKP_Silk_resample_1_2.c \
$(SILK)/src/SKP_Silk_resample_1_2_coarse.c \
$(SILK)/src/SKP_Silk_resample_1_2_coarsest.c \
$(SILK)/src/SKP_Silk_resample_1_3.c \
$(SILK)/src/SKP_Silk_resample_2_1_coarse.c \
$(SILK)/src/SKP_Silk_resample_2_3.c \
$(SILK)/src/SKP_Silk_resample_2_3_coarse.c \
$(SILK)/src/SKP_Silk_resample_2_3_coarsest.c \
$(SILK)/src/SKP_Silk_resample_2_3_rom.c \
$(SILK)/src/SKP_Silk_resample_3_1.c \
$(SILK)/src/SKP_Silk_resample_3_2.c \
$(SILK)/src/SKP_Silk_resample_3_2_rom.c \
$(SILK)/src/SKP_Silk_resample_3_4.c \
$(SILK)/src/SKP_Silk_resample_4_3.c \
$(SILK)/src/SKP_Silk_residual_energy16_FIX.c \
$(SILK)/src/SKP_Silk_residual_energy_FIX.c \
$(SILK)/src/SKP_Silk_scale_copy_vector16.c \
$(SILK)/src/SKP_Silk_scale_vector.c \
$(SILK)/src/SKP_Silk_schur.c \
$(SILK)/src/SKP_Silk_schur64.c \
$(SILK)/src/SKP_Silk_shell_coder.c \
$(SILK)/src/SKP_Silk_sigm_Q15.c \
$(SILK)/src/SKP_Silk_solve_LS_FIX.c \
$(SILK)/src/SKP_Silk_sort.c \
$(SILK)/src/SKP_Silk_sum_sqr_shift.c \
$(SILK)/src/SKP_Silk_tables_LTP.c \
$(SILK)/src/SKP_Silk_tables_NLSF_CB0_10.c \
$(SILK)/src/SKP_Silk_tables_NLSF_CB0_16.c \
$(SILK)/src/SKP_Silk_tables_NLSF_CB1_10.c \
$(SILK)/src/SKP_Silk_tables_NLSF_CB1_16.c \
$(SILK)/src/SKP_Silk_tables_gain.c \
$(SILK)/src/SKP_Silk_tables_other.c \
$(SILK)/src/SKP_Silk_tables_pitch_lag.c \
$(SILK)/src/SKP_Silk_tables_pulses_per_block.c \
$(SILK)/src/SKP_Silk_tables_sign.c \
$(SILK)/src/SKP_Silk_tables_type_offset.c
LOCAL_ARM_MODE := arm
LOCAL_CFLAGS = -O3
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := silk8_jni
LOCAL_SRC_FILES := silk8_jni.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface
LOCAL_CFLAGS = -O3
LOCAL_STATIC_LIBRARIES := silkcommon
LOCAL_ARM_MODE := arm
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := silk16_jni
LOCAL_SRC_FILES := silk16_jni.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface
LOCAL_CFLAGS = -O3
LOCAL_STATIC_LIBRARIES := silkcommon
LOCAL_ARM_MODE := arm
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := silk24_jni
LOCAL_SRC_FILES := silk24_jni.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface
LOCAL_CFLAGS = -O3
LOCAL_STATIC_LIBRARIES := silkcommon
LOCAL_ARM_MODE := arm
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
~~~
5、创建JNI包装类silk8_jni.cpp、silk16_jni.cpp、silk24_jni.cpp,用来调用Silk中的C代码函数,编辑内容如下
silk8_jni.cpp:
~~~
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* Define codec specific settings */
#define MAX_BYTES_ENC_PER_FRAME 250 // Equals peak bitrate of 100 kbps
#define MAX_BYTES_DEC_PER_FRAME 1024
#define MAX_INPUT_FRAMES 5
#define MAX_LBRR_DELAY 2
#define MAX_FRAME_LENGTH 480
#define MAX_FRAME 160
#include <android/log.h>
#define LOG_TAG "silk" // text for log tag
#include "SKP_Silk_SDK_API.h"
#include "SKP_Silk_SigProc_FIX.h"
#undef DEBUG_SILK8
// the header length of the RTP frame (must skip when en/decoding)
#define RTP_HDR_SIZE 12
static int codec_open = 0;
static JavaVM *gJavaVM;
const char *kInterfacePath = "org/sipdroid/pjlib/silk8";
/* encoder parameters */
SKP_int32 encSizeBytes;
void *psEnc;
/* default settings */
SKP_int fs_kHz = 8;
SKP_int targetRate_bps = 20000;
SKP_int packetSize_ms = 20;
SKP_int frameSizeReadFromFile_ms = 20;
SKP_int packetLoss_perc = 0, smplsSinceLastPacket;
SKP_int INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0;
SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder
/* decoder parameters */
jbyte payloadToDec[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr;
SKP_int32 decSizeBytes;
void *psDec;
SKP_SILK_SDK_DecControlStruct DecControl;
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK8_open
(JNIEnv *env, jobject obj, jint compression) {
int ret;
if (codec_open++ != 0)
return (jint)0;
/* Set the samplingrate that is requested for the output */
DecControl.sampleRate = 8000;
/* Create decoder */
ret = SKP_Silk_SDK_Get_Decoder_Size( &decSizeBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_Get_Decoder_Size returned %d", ret );
}
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"### INIT Decoder decSizeBytes = %d\n", decSizeBytes);
#endif
psDec = malloc( decSizeBytes );
/* Reset decoder */
ret = SKP_Silk_SDK_InitDecoder( psDec );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_InitDecoder returned %d", ret );
}
/* Create Encoder */
ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_Get_Encoder_Size returned %d", ret );
}
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"### INIT Encoder encSizeBytes = %d\n", encSizeBytes);
#endif
psEnc = malloc( encSizeBytes );
/* Reset Encoder */
ret = SKP_Silk_SDK_InitEncoder( psEnc, &encControl );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_InitEncoder returned %d", ret );
}
/* Set Encoder parameters */
encControl.sampleRate = fs_kHz * 1000;
encControl.packetSize = packetSize_ms * fs_kHz;
encControl.packetLossPercentage = packetLoss_perc;
encControl.useInBandFEC = INBandFec_enabled;
encControl.useDTX = DTX_enabled;
encControl.complexity = compression;
encControl.bitRate = targetRate_bps;
return (jint)0;
}
void Print_Decode_Error_Msg(int errcode) {
switch (errcode) {
case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nOutput sampling frequency lower than internal decoded sampling frequency\n", errcode);
break;
case SKP_SILK_DEC_PAYLOAD_TOO_LARGE:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPayload size exceeded the maximum allowed 1024 bytes\n", errcode);
break;
case SKP_SILK_DEC_PAYLOAD_ERROR:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPayload has bit errors\n", errcode);
break;
}
}
void Print_Encode_Error_Msg(int errcode) {
switch (errcode) {
case SKP_SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInput length is not a multiplum of 10 ms, or length is longer than the packet length\n", errcode);
break;
case SKP_SILK_ENC_FS_NOT_SUPPORTED:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nSampling frequency not 8000, 12000, 16000 or 24000 Hertz \n", errcode);
break;
case SKP_SILK_ENC_PACKET_SIZE_NOT_SUPPORTED:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPacket size not 20, 40, 60, 80 or 100 ms\n", errcode);
break;
case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nAllocated payload buffer too short \n", errcode);
break;
case SKP_SILK_ENC_WRONG_LOSS_RATE:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nLoss rate not between 0 and 100 percent\n", errcode);
break;
case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nComplexity setting not valid, use 0, 1 or 2\n", errcode);
break;
case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInband FEC setting not valid, use 0 or 1\n", errcode);
break;
case SKP_SILK_ENC_WRONG_DTX_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nDTX setting not valid, use 0 or 1\n", errcode);
break;
case SKP_SILK_ENC_INTERNAL_ERROR:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInternal encoder error\n", errcode);
break;
}
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK8_encode
(JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {
jbyte enc_payload[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES ];
jshort in[ MAX_FRAME_LENGTH * MAX_INPUT_FRAMES ];
int ret,i,frsz=MAX_FRAME;
SKP_int16 nBytes;
unsigned int lin_pos = 0;
if (!codec_open)
return 0;
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding frame size: %d\toffset: %d\n", size, offset);
#endif
for (i = 0; i < size; i+=MAX_FRAME) {
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding frame size: %d\toffset: %d i: %d\n", size, offset, i);
#endif
env->GetShortArrayRegion(lin, offset + i,frsz, in);
/* max payload size */
nBytes = MAX_BYTES_ENC_PER_FRAME * MAX_INPUT_FRAMES;
ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)frsz, (SKP_uint8 *)enc_payload, &nBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!! SKP_Silk_Encode returned: %d\n", ret);
Print_Encode_Error_Msg(ret);
break;
}
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"Enocded nBytes: %d\n", nBytes);
#endif
/* Write payload */
env->SetByteArrayRegion(encoded, RTP_HDR_SIZE+ lin_pos, nBytes, enc_payload);
lin_pos += nBytes;
}
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding **END** frame size: %d\toffset: %d i: %d lin_pos: %d\n", size, offset, i, lin_pos);
#endif
return (jint)lin_pos;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK8_decode
(JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {
jbyte buffer [MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort output_buffer[( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ];
// SKP_int16 *outPtr;
int ret;
SKP_int16 len;
// int tot_len,frames;
if (!codec_open)
return 0;
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"##### BEGIN DECODE ******** decoding frame size: %d\n", size);
#endif
env->GetByteArrayRegion(encoded, RTP_HDR_SIZE, size, buffer);
// outPtr = output_buffer;
// tot_len = 0;
// frames = 0;
// do {
ret = SKP_Silk_SDK_Decode( psDec, &DecControl, 0,(SKP_uint8 *) buffer, size, output_buffer,&len );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!! SKP_Silk_SDK_Decode returned: %d\n", ret);
Print_Decode_Error_Msg(ret);
}
#ifdef DEBUG_SILK8
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"##### DECODED length: %d\n\t Frame #: %d", len);
#endif
// frames++;
// outPtr += len;
// tot_len += len;
// } while( DecControl.moreInternalDecoderFrames );
env->SetShortArrayRegion(lin, 0, len,output_buffer);
return (jint)len;
}
extern "C"
JNIEXPORT void JNICALL Java_com_trunkbow_silk_SILK8_close
(JNIEnv *env, jobject obj) {
if (--codec_open != 0)
return;
/* Free decoder */
free( psDec );
/* Free Encoder */
free( psEnc );
}
~~~
silk_16.cpp:
~~~
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* Define codec specific settings */
#define MAX_BYTES_ENC_PER_FRAME 250 // Equals peak bitrate of 100 kbps
#define MAX_BYTES_DEC_PER_FRAME 1024
#define MAX_INPUT_FRAMES 5
#define MAX_LBRR_DELAY 2
#define MAX_FRAME_LENGTH 480
#define MAX_FRAME 320
#include <android/log.h>
#define LOG_TAG "silk" // text for log tag
#include "SKP_Silk_SDK_API.h"
#include "SKP_Silk_SigProc_FIX.h"
#undef DEBUG_SILK16
// the header length of the RTP frame (must skip when en/decoding)
#define RTP_HDR_SIZE 12
static int codec_open = 0;
static JavaVM *gJavaVM;
const char *kInterfacePath = "org/sipdroid/pjlib/silk16";
/* encoder parameters */
SKP_int32 encSizeBytes;
void *psEnc;
/* default settings */
SKP_int fs_kHz = 16;
SKP_int targetRate_bps = 20000;
SKP_int packetSize_ms = 20;
SKP_int frameSizeReadFromFile_ms = 20;
SKP_int packetLoss_perc = 0, smplsSinceLastPacket;
SKP_int INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0;
SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder
/* decoder parameters */
jbyte payloadToDec[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr;
SKP_int32 decSizeBytes;
void *psDec;
SKP_SILK_SDK_DecControlStruct DecControl;
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK16_open
(JNIEnv *env, jobject obj, jint compression) {
int ret;
if (codec_open++ != 0)
return (jint)0;
/* Set the samplingrate that is requested for the output */
DecControl.sampleRate = 16000;
/* Create decoder */
ret = SKP_Silk_SDK_Get_Decoder_Size( &decSizeBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_Get_Decoder_Size returned %d", ret );
}
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"### INIT Decoder decSizeBytes = %d\n", decSizeBytes);
#endif
psDec = malloc( decSizeBytes );
/* Reset decoder */
ret = SKP_Silk_SDK_InitDecoder( psDec );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_InitDecoder returned %d", ret );
}
/* Create Encoder */
ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_Get_Encoder_Size returned %d", ret );
}
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"### INIT Encoder encSizeBytes = %d\n", encSizeBytes);
#endif
psEnc = malloc( encSizeBytes );
/* Reset Encoder */
ret = SKP_Silk_SDK_InitEncoder( psEnc, &encControl );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_InitEncoder returned %d", ret );
}
/* Set Encoder parameters */
encControl.sampleRate = fs_kHz * 1000;
encControl.packetSize = packetSize_ms * fs_kHz;
encControl.packetLossPercentage = packetLoss_perc;
encControl.useInBandFEC = INBandFec_enabled;
encControl.useDTX = DTX_enabled;
encControl.complexity = compression;
encControl.bitRate = targetRate_bps;
return (jint)0;
}
void Print_Decode_Error_Msg(int errcode) {
switch (errcode) {
case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nOutput sampling frequency lower than internal decoded sampling frequency\n", errcode);
break;
case SKP_SILK_DEC_PAYLOAD_TOO_LARGE:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPayload size exceeded the maximum allowed 1024 bytes\n", errcode);
break;
case SKP_SILK_DEC_PAYLOAD_ERROR:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPayload has bit errors\n", errcode);
break;
}
}
void Print_Encode_Error_Msg(int errcode) {
switch (errcode) {
case SKP_SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInput length is not a multiplum of 10 ms, or length is longer than the packet length\n", errcode);
break;
case SKP_SILK_ENC_FS_NOT_SUPPORTED:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nSampling frequency not 8000, 12000, 16000 or 24000 Hertz \n", errcode);
break;
case SKP_SILK_ENC_PACKET_SIZE_NOT_SUPPORTED:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPacket size not 20, 40, 60, 80 or 100 ms\n", errcode);
break;
case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nAllocated payload buffer too short \n", errcode);
break;
case SKP_SILK_ENC_WRONG_LOSS_RATE:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nLoss rate not between 0 and 100 percent\n", errcode);
break;
case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nComplexity setting not valid, use 0, 1 or 2\n", errcode);
break;
case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInband FEC setting not valid, use 0 or 1\n", errcode);
break;
case SKP_SILK_ENC_WRONG_DTX_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nDTX setting not valid, use 0 or 1\n", errcode);
break;
case SKP_SILK_ENC_INTERNAL_ERROR:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInternal encoder error\n", errcode);
break;
}
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK16_encode
(JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {
jbyte enc_payload[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES ];
jshort in[ MAX_FRAME_LENGTH * MAX_INPUT_FRAMES ];
int ret,i,frsz=MAX_FRAME;
SKP_int16 nBytes;
unsigned int lin_pos = 0;
if (!codec_open)
return 0;
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding frame size: %d\toffset: %d\n", size, offset);
#endif
for (i = 0; i < size; i+=MAX_FRAME) {
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding frame size: %d\toffset: %d i: %d\n", size, offset, i);
#endif
env->GetShortArrayRegion(lin, offset + i,frsz, in);
/* max payload size */
nBytes = MAX_BYTES_ENC_PER_FRAME * MAX_INPUT_FRAMES;
ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)frsz, (SKP_uint8 *)enc_payload, &nBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!! SKP_Silk_Encode returned: %d\n", ret);
Print_Encode_Error_Msg(ret);
break;
}
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"Enocded nBytes: %d\n", nBytes);
#endif
/* Write payload */
env->SetByteArrayRegion(encoded, RTP_HDR_SIZE+ lin_pos, nBytes, enc_payload);
lin_pos += nBytes;
}
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding **END** frame size: %d\toffset: %d i: %d lin_pos: %d\n", size, offset, i, lin_pos);
#endif
return (jint)lin_pos;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK16_decode
(JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {
jbyte buffer [MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort output_buffer[( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ];
// SKP_int16 *outPtr;
int ret;
SKP_int16 len;
// int tot_len,frames;
if (!codec_open)
return 0;
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"##### BEGIN DECODE ******** decoding frame size: %d\n", size);
#endif
env->GetByteArrayRegion(encoded, RTP_HDR_SIZE, size, buffer);
// outPtr = output_buffer;
// tot_len = 0;
// frames = 0;
// do {
ret = SKP_Silk_SDK_Decode( psDec, &DecControl, 0,(SKP_uint8 *) buffer, size, output_buffer,&len );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!! SKP_Silk_SDK_Decode returned: %d\n", ret);
Print_Decode_Error_Msg(ret);
}
#ifdef DEBUG_SILK16
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"##### DECODED length: %d\n\t Frame #: %d", len);
#endif
// frames++;
// outPtr += len;
// tot_len += len;
// } while( DecControl.moreInternalDecoderFrames );
env->SetShortArrayRegion(lin, 0, len,output_buffer);
return (jint)len;
}
extern "C"
JNIEXPORT void JNICALL Java_com_trunkbow_silk_SILK16_close
(JNIEnv *env, jobject obj) {
if (--codec_open != 0)
return;
/* Free decoder */
free( psDec );
/* Free Encoder */
free( psEnc );
}
~~~
silk24_jni.cpp
~~~
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* Define codec specific settings */
#define MAX_BYTES_ENC_PER_FRAME 250 // Equals peak bitrate of 100 kbps
#define MAX_BYTES_DEC_PER_FRAME 1024
#define MAX_INPUT_FRAMES 5
#define MAX_LBRR_DELAY 2
#define MAX_FRAME_LENGTH 480
#define MAX_FRAME 480
#include <android/log.h>
#define LOG_TAG "silk" // text for log tag
#include "SKP_Silk_SDK_API.h"
#include "SKP_Silk_SigProc_FIX.h"
#undef DEBUG_SILK24
// the header length of the RTP frame (must skip when en/decoding)
#define RTP_HDR_SIZE 12
static int codec_open = 0;
static JavaVM *gJavaVM;
const char *kInterfacePath = "org/sipdroid/pjlib/SILK24";
/* encoder parameters */
SKP_int32 encSizeBytes;
void *psEnc;
/* default settings */
SKP_int fs_kHz = 24;
SKP_int targetRate_bps = 20000;
SKP_int packetSize_ms = 20;
SKP_int frameSizeReadFromFile_ms = 20;
SKP_int packetLoss_perc = 0, smplsSinceLastPacket;
SKP_int INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0;
SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder
/* decoder parameters */
jbyte payloadToDec[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr;
SKP_int32 decSizeBytes;
void *psDec;
SKP_SILK_SDK_DecControlStruct DecControl;
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK24_open
(JNIEnv *env, jobject obj, jint compression) {
int ret;
if (codec_open++ != 0)
return (jint)0;
/* Set the samplingrate that is requested for the output */
DecControl.sampleRate = 24000;
/* Create decoder */
ret = SKP_Silk_SDK_Get_Decoder_Size( &decSizeBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_Get_Decoder_Size returned %d", ret );
}
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"### INIT Decoder decSizeBytes = %d\n", decSizeBytes);
#endif
psDec = malloc( decSizeBytes );
/* Reset decoder */
ret = SKP_Silk_SDK_InitDecoder( psDec );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_InitDecoder returned %d", ret );
}
/* Create Encoder */
ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_Get_Encoder_Size returned %d", ret );
}
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"### INIT Encoder encSizeBytes = %d\n", encSizeBytes);
#endif
psEnc = malloc( encSizeBytes );
/* Reset Encoder */
ret = SKP_Silk_SDK_InitEncoder( psEnc, &encControl );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"\n!!!!!!!! SKP_Silk_SDK_InitEncoder returned %d", ret );
}
/* Set Encoder parameters */
encControl.sampleRate = fs_kHz * 1000;
encControl.packetSize = packetSize_ms * fs_kHz;
encControl.packetLossPercentage = packetLoss_perc;
encControl.useInBandFEC = INBandFec_enabled;
encControl.useDTX = DTX_enabled;
encControl.complexity = compression;
encControl.bitRate = targetRate_bps;
return (jint)0;
}
void Print_Decode_Error_Msg(int errcode) {
switch (errcode) {
case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nOutput sampling frequency lower than internal decoded sampling frequency\n", errcode);
break;
case SKP_SILK_DEC_PAYLOAD_TOO_LARGE:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPayload size exceeded the maximum allowed 1024 bytes\n", errcode);
break;
case SKP_SILK_DEC_PAYLOAD_ERROR:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPayload has bit errors\n", errcode);
break;
}
}
void Print_Encode_Error_Msg(int errcode) {
switch (errcode) {
case SKP_SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInput length is not a multiplum of 10 ms, or length is longer than the packet length\n", errcode);
break;
case SKP_SILK_ENC_FS_NOT_SUPPORTED:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nSampling frequency not 8000, 12000, 16000 or 24000 Hertz \n", errcode);
break;
case SKP_SILK_ENC_PACKET_SIZE_NOT_SUPPORTED:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nPacket size not 20, 40, 60, 80 or 100 ms\n", errcode);
break;
case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nAllocated payload buffer too short \n", errcode);
break;
case SKP_SILK_ENC_WRONG_LOSS_RATE:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nLoss rate not between 0 and 100 percent\n", errcode);
break;
case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nComplexity setting not valid, use 0, 1 or 2\n", errcode);
break;
case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInband FEC setting not valid, use 0 or 1\n", errcode);
break;
case SKP_SILK_ENC_WRONG_DTX_SETTING:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nDTX setting not valid, use 0 or 1\n", errcode);
break;
case SKP_SILK_ENC_INTERNAL_ERROR:
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!!!!! Decode_Error_Message: %d\nInternal encoder error\n", errcode);
break;
}
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK24_encode
(JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {
jbyte enc_payload[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES ];
jshort in[ MAX_FRAME_LENGTH * MAX_INPUT_FRAMES ];
int ret,i,frsz=MAX_FRAME;
SKP_int16 nBytes;
unsigned int lin_pos = 0;
if (!codec_open)
return 0;
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding frame size: %d\toffset: %d\n", size, offset);
#endif
for (i = 0; i < size; i+=MAX_FRAME) {
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding frame size: %d\toffset: %d i: %d\n", size, offset, i);
#endif
env->GetShortArrayRegion(lin, offset + i,frsz, in);
/* max payload size */
nBytes = MAX_BYTES_ENC_PER_FRAME * MAX_INPUT_FRAMES;
ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)frsz, (SKP_uint8 *)enc_payload, &nBytes );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!! SKP_Silk_Encode returned: %d\n", ret);
Print_Encode_Error_Msg(ret);
break;
}
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"Enocded nBytes: %d\n", nBytes);
#endif
/* Write payload */
env->SetByteArrayRegion(encoded, RTP_HDR_SIZE+ lin_pos, nBytes, enc_payload);
lin_pos += nBytes;
}
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"encoding **END** frame size: %d\toffset: %d i: %d lin_pos: %d\n", size, offset, i, lin_pos);
#endif
return (jint)lin_pos;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK24_decode
(JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {
jbyte buffer [MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort output_buffer[( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ];
// SKP_int16 *outPtr;
int ret;
SKP_int16 len;
// int tot_len,frames;
if (!codec_open)
return 0;
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"##### BEGIN DECODE ******** decoding frame size: %d\n", size);
#endif
env->GetByteArrayRegion(encoded, RTP_HDR_SIZE, size, buffer);
// outPtr = output_buffer;
// tot_len = 0;
// frames = 0;
// do {
ret = SKP_Silk_SDK_Decode( psDec, &DecControl, 0,(SKP_uint8 *) buffer, size, output_buffer,&len );
if( ret ) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"!!!!!!!! SKP_Silk_SDK_Decode returned: %d\n", ret);
Print_Decode_Error_Msg(ret);
}
#ifdef DEBUG_SILK24
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"##### DECODED length: %d\n\t Frame #: %d", len);
#endif
// frames++;
// outPtr += len;
// tot_len += len;
// } while( DecControl.moreInternalDecoderFrames );
env->SetShortArrayRegion(lin, 0, len,output_buffer);
return (jint)len;
}
extern "C"
JNIEXPORT void JNICALL Java_com_trunkbow_silk_SILK24_close
(JNIEnv *env, jobject obj) {
if (--codec_open != 0)
return;
/* Free decoder */
free( psDec );
/* Free Encoder */
free( psEnc );
}
~~~
6、在Java层创建Speex工具类,内容如下:
Silk8.java:
~~~
class SILK8{
/*
* | fs (Hz) | BR (kbps)
* ----------------+---------+---------
* Narrowband | 8000 | 6 -20
* Mediumband | 12000 | 7 -25
* Wideband | 16000 | 8 -30
* Super Wideband | 24000 | 12 -40
*
* Table 1: fs specifies the audio sampling frequency in Hertz (Hz); BR
* specifies the adaptive bit rate range in kilobits per second (kbps).
*
* Complexity can be scaled to optimize for CPU resources in real-time,
* mostly in trade-off to network bit rate. 0 is least CPU demanding and
* highest bit rate.
*/
private static final int DEFAULT_COMPLEXITY = 0;
void load() {
System.loadLibrary("silk8_jni");
}
public native int open(int compression);
public native int decode(byte encoded[], short lin[], int size);
public native int encode(short lin[], int offset, byte encoded[], int size);
public native void close();
}
~~~
Silk16.java:
~~~
class SILK16 {
/*
* | fs (Hz) | BR (kbps)
* ----------------+---------+---------
* Narrowband | 8000 | 6 -20
* Mediumband | 12000 | 7 -25
* Wideband | 16000 | 8 -30
* Super Wideband | 24000 | 12 -40
*
* Table 1: fs specifies the audio sampling frequency in Hertz (Hz); BR
* specifies the adaptive bit rate range in kilobits per second (kbps).
*
* Complexity can be scaled to optimize for CPU resources in real-time,
* mostly in trade-off to network bit rate. 0 is least CPU demanding and
* highest bit rate.
*/
private static final int DEFAULT_COMPLEXITY = 0;
void load() {
System.loadLibrary("silk16_jni");
}
public native int open(int compression);
public native int decode(byte encoded[], short lin[], int size);
public native int encode(short lin[], int offset, byte encoded[], int size);
public native void close();
}
~~~
Silk24.java:
~~~
class SILK24 {
/*
* | fs (Hz) | BR (kbps)
* ----------------+---------+---------
* Narrowband | 8000 | 6 -20
* Mediumband | 12000 | 7 -25
* Wideband | 16000 | 8 -30
* Super Wideband | 24000 | 12 -40
*
* Table 1: fs specifies the audio sampling frequency in Hertz (Hz); BR
* specifies the adaptive bit rate range in kilobits per second (kbps).
*
* Complexity can be scaled to optimize for CPU resources in real-time,
* mostly in trade-off to network bit rate. 0 is least CPU demanding and
* highest bit rate.
*/
private static final int DEFAULT_COMPLEXITY = 0;
void load() {
System.loadLibrary("silk24_jni");
}
public native int open(int compression);
public native int decode(byte encoded[], short lin[], int size);
public native int encode(short lin[], int offset, byte encoded[], int size);
public native void close();
}
~~~
7、使用cygwin编译,生成so文件。
参考:http://developer.skype.com/silk
http://blog.csdn.net/wanggp_2007/article/details/5540686 Skype SILK vs. iLBC vs. Speex
ilbc编解码在android实现
最后更新于:2022-04-01 07:03:59
iLBC 是为专为提供稳健的 IP 语音通信而开发的语音 codec,以窄带语音为设计基础,具有 8 kHz 的采样率。iLBC codec 支持两种基本的帧长度:13.3 kbps 比特率下编码帧长度为 30 ms;而 15.2 kbps比特率下编码帧长度则为 20 ms。
采用 iLBC 算法可以获得一个具有丢包响应控制的语音编码系统。iLBC 对每一个数据包的处理都能够独立于其它数据包来进行,是数据包通信的理想选择。即使 IP 丢包和/或延迟现象的恶化,这种 codec 的语音质量下降情况也不会太差。这与基于 CEIP 模型的一般 codec 的行为不同,这类 codec 最先是为交换电路网络或无线网络而设计的,是设计来恢复位错误而非丢包的。
丢包现象发生时,语音 codec 的一项相关基准是从单个丢包情况下恢复过来所需的帧/包数量。在 iLBC 的情况中,数量是零。在丢包之后的第一个数据包总仍能按原本安排的被精确解码。
iLBC 是一种窄带语音 codec,使用了整个 4kHz 频带,而大多数标准低比特率 codec 只利用从 300 Hz 到 3400 Hz 的频带。这一点对音质的影响是相当明显的。此外,iLBC 语音编码的频谱特性精确模拟了原始信号的特性,其语音比标准低比特率 codec 的更自然清晰。
总而言之,iLBC 算法为数据包网络实现了尖端的固定比特率编码,在质量与比特率之间取得了非常出色的平衡。
iLBC算法也是开源算法,在GitHub可以下载到,源地址如下[https://github.com/bjdodson/iLBC-Android](https://github.com/bjdodson/iLBC-Android) ,同时code.google([http://code.google.com/p/android-ilbc/](http://code.google.com/p/android-ilbc/))上也有一份,code.google.上的比较清晰,这一篇主要参
考了android-ilbc。
git下android-ilbc工程,导入到eclipse中,jni部分使用cygwin编译。git对有些开发者还不方便,我在csdn的资源模块
上传了一份编译好的,地址如下:[http://download.csdn.net/detail/xyz_lmn/4594662](http://download.csdn.net/detail/xyz_lmn/4594662)
参考:
1、http://www.cnblogs.com/huaping-audio/archive/2008/11/23/1339527.html iLBC编解码相关知识
2、http://code.google.com/p/android-ilbc/source/browse/jni/ilbc-codec.c?r=35e61e06caa30a46c41885c37b380acd61577f5c
3、https://github.com/lukeweber/iLBC-Android
4、http://www.ilbcfreeware.org/software.html
5、http://www.cocoachina.com/bbs/read.php?tid=114755
speex编解码在android上实现
最后更新于:2022-04-01 07:03:56
以前在应用中使用到了Speex编解码,近来总结了一下Speex在android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛 。另外,相对于其它编解码,Speex也很适合网络应用,在网络应用上有着自己独特的优势。同时,Speex还是GNU工程的一部分,在改版的BSD协议中得到了很好的支持。Speex是基于CELP并且专门为码率在2-44kbps的语音压缩而设计的。Speex源码是基于c语音实现的(也有java实现,效率相对较低)。
1、去Speex官网下载最新[Speex源码](http://www.speex.org/downloads/)。
2、创建新的android工程,并创建jni文件夹。
3、把speex源码目录下的libspeex和include目录及其子目录文件全部拷贝到$project/jni目录下。
4、在jni目录下新增Android.mk文件,编辑内容如下:
~~~
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= libspeex
LOCAL_CFLAGS = -DFIXED_POINT -DUSE_KISS_FFT -DEXPORT="" -UHAVE_CONFIG_H
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES :=\
libspeex/bits.c \
libspeex/buffer.c \
libspeex/cb_search.c \
libspeex/exc_10_16_table.c \
libspeex/exc_10_32_table.c \
libspeex/exc_20_32_table.c \
libspeex/exc_5_256_table.c \
libspeex/exc_5_64_table.c \
libspeex/exc_8_128_table.c \
libspeex/fftwrap.c \
libspeex/filterbank.c \
libspeex/filters.c \
libspeex/gain_table.c \
libspeex/gain_table_lbr.c \
libspeex/hexc_10_32_table.c \
libspeex/hexc_table.c \
libspeex/high_lsp_tables.c \
libspeex/jitter.c \
libspeex/kiss_fft.c \
libspeex/kiss_fftr.c \
libspeex/lpc.c \
libspeex/lsp.c \
libspeex/lsp_tables_nb.c \
libspeex/ltp.c \
libspeex/mdf.c \
libspeex/modes.c \
libspeex/modes_wb.c \
libspeex/nb_celp.c \
libspeex/preprocess.c \
libspeex/quant_lsp.c \
libspeex/resample.c \
libspeex/sb_celp.c \
libspeex/scal.c \
libspeex/smallft.c \
libspeex/speex.c \
libspeex/speex_callbacks.c \
libspeex/speex_header.c \
libspeex/stereo.c \
libspeex/vbr.c \
libspeex/vq.c \
libspeex/window.c \
speex_jni.cpp \
include $(BUILD_SHARED_LIBRARY)
~~~
5.在jni目录下新增Application.mk文件,编辑内容如下
~~~
APP_ABI := armeabi armeabi-v7a
~~~
6.在$project/jni/include/speex/目录下新增speex_config_types.h文件,编辑内容如下
~~~
01.#ifndef __SPEEX_TYPES_H__
02.#define __SPEEX_TYPES_H__
03.typedef short spx_int16_t;
04.typedef unsigned short spx_uint16_t;
05.typedef int spx_int32_t;
06.typedef unsigned int spx_uint32_t;
07.#endif
~~~
7.创建JNI包装类speex_jni.cpp,用来调用Speex中的C代码函数,编辑内容如下
~~~
#include <jni.h>
#include <string.h>
#include <unistd.h>
#include <speex/speex.h>
static int codec_open = 0;
static int dec_frame_size;
static int enc_frame_size;
static SpeexBits ebits, dbits;
void *enc_state;
void *dec_state;
static JavaVM *gJavaVM;
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_open
(JNIEnv *env, jobject obj, jint compression) {
int tmp;
if (codec_open++ != 0)
return (jint)0;
speex_bits_init(&ebits);
speex_bits_init(&dbits);
enc_state = speex_encoder_init(&speex_nb_mode);
dec_state = speex_decoder_init(&speex_nb_mode);
tmp = compression;
speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &tmp);
speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size);
speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
return (jint)0;
}
extern "C"
JNIEXPORT jint Java_com_trunkbow_speextest_Speex_encode
(JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {
jshort buffer[enc_frame_size];
jbyte output_buffer[enc_frame_size];
int nsamples = (size-1)/enc_frame_size + 1;
int i, tot_bytes = 0;
if (!codec_open)
return 0;
speex_bits_reset(&ebits);
for (i = 0; i < nsamples; i++) {
env->GetShortArrayRegion(lin, offset + i*enc_frame_size, enc_frame_size, buffer);
speex_encode_int(enc_state, buffer, &ebits);
}
//env->GetShortArrayRegion(lin, offset, enc_frame_size, buffer);
//speex_encode_int(enc_state, buffer, &ebits);
tot_bytes = speex_bits_write(&ebits, (char *)output_buffer,
enc_frame_size);
env->SetByteArrayRegion(encoded, 0, tot_bytes,
output_buffer);
return (jint)tot_bytes;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_decode
(JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {
jbyte buffer[dec_frame_size];
jshort output_buffer[dec_frame_size];
jsize encoded_length = size;
if (!codec_open)
return 0;
env->GetByteArrayRegion(encoded, 0, encoded_length, buffer);
speex_bits_read_from(&dbits, (char *)buffer, encoded_length);
speex_decode_int(dec_state, &dbits, output_buffer);
env->SetShortArrayRegion(lin, 0, dec_frame_size,
output_buffer);
return (jint)dec_frame_size;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_getFrameSize
(JNIEnv *env, jobject obj) {
if (!codec_open)
return 0;
return (jint)enc_frame_size;
}
extern "C"
JNIEXPORT void JNICALL Java_com_trunkbow_speextest_Speex_close
(JNIEnv *env, jobject obj) {
if (--codec_open != 0)
return;
speex_bits_destroy(&ebits);
speex_bits_destroy(&dbits);
speex_decoder_destroy(dec_state);
speex_encoder_destroy(enc_state);
}
~~~
8.在Java层创建Speex工具类,内容如下:
~~~
package com.trunkbow.speextest;
public class Speex {
/* quality
* 1 : 4kbps (very noticeable artifacts, usually intelligible)
* 2 : 6kbps (very noticeable artifacts, good intelligibility)
* 4 : 8kbps (noticeable artifacts sometimes)
* 6 : 11kpbs (artifacts usually only noticeable with headphones)
* 8 : 15kbps (artifacts not usually noticeable)
*/
private static final int DEFAULT_COMPRESSION = 8;
Speex() {
}
public void init() {
load();
open(DEFAULT_COMPRESSION);
}
private void load() {
try {
System.loadLibrary("speex");
} catch (Throwable e) {
e.printStackTrace();
}
}
public native int open(int compression);
public native int getFrameSize();
public native int decode(byte encoded[], short lin[], int size);
public native int encode(short lin[], int offset, byte encoded[], int size);
public native void close();
}
~~~
9、使用cygwin编译,生成so文件。
参考:
http://code.google.com/p/android-recorder/downloads/list 这个是一个android recorder ,使用speex编码,
Android JNI开发提高篇
最后更新于:2022-04-01 07:03:54
有关JNI的开发技术,我们继续围绕Android平台进行,JNI可以支持C或C++,从目前为止我们写过的JNI代码均为C实现的,即文件名为.C而C++的和这些有什么不同呢? Android平台上的JNI一般使用C还是C++编写呢?
Android平台在中间层和大部分的类库的底层使用了C++的开发方式,后缀为.cpp,比如Android Framework、OpenCore、Webkit、SQLite等等。使用C++好处就是可以使用很多库但目前Android不支持STL,我们知道C表示字符串都是字符数组,但C++可以使用类似string这样的类型表示。
1\. 代码上编写C和C++有啥区别
这里Android123就以将Java的unicode字符串转为jni中的utf8,然后再返回一个jstring类型为例子,可以看到jni和java之间字符串的转换方法。
C的实现:
JNIEXPORT jstring JNICALL Java_Android123_CwjC (JNIEnv *env, jobject obj, jstring string)
{
const char *strUTF = (*env)->GetStringUTFChars(**env**, string, 0);
char szBuffer[255];
strcpy(szBuffer, strUTF);
(*env)->ReleaseStringUTFChars(**env**, string, strUTF);
return (*env)->NewStringUTF(**env**, szBuffer);
}
C++的实现:
JNIEXPORT jstring JNICALL Java_Android123_CwjCpp (JNIEnv *env, jobject obj, jstring string)
{
const char *strUTF = env->GetStringUTFChars(string, 0);
char szBuffer[255];
strcpy(szBuffer, strUTF);
env->ReleaseStringUTFChars(string, strUTF);
return env->NewStringUTF(szBuffer);
}
我们加粗了主要区别的关键字,可以看到C++的代码更简练。
2\. JNI操作数组代码
JNI中处理数组通用对象为jobjectArray 当然常规的类型比如整形为jintArray,布尔型为jbooleanArray,但没有出现jstringArray这样的类型,有关字符数组的处理我们将在下次的 [Android JNI开发进阶篇](http://writeblog.csdn.net/androidkaifa/681.html) 详细说明 。处理数组时我们需要考虑数组的长度不能为0才能继续操作,不然就会有访问越界等问题,在JNI中提供了通用类型的GetArrayLength函数。我们从Java传入一个以整形数组,在JNI中将每个元素相加为例返回一个整形告诉Java运算的结果。
JNIEXPORT jint JNICALL Java_Android123_CwjTest (JNIEnv *env, jobject obj, jintArray array)
{
int sum = 0;
jsize length = (*env)->GetArrayLength(env, array); //获取数组长度
if(length==0) //防止异常发生,如果是空的需要返回了
return 0;
jint *pointer = (*env)->GetIntArrayElements(env, array, 0); //获取数组指针
for (int i=0; i<length; i++)
{
sum += pointer[i]; //相加每个数组元素
}
(*env)->ReleaseIntArrayElements(env, array, pointer, 0); //释放内存,这个不能忘了
return sum;
}
如何在JNI中构造一个数组呢? Android开发网给大家一个简单的示例,返回一个整形数组:
JNIEXPORT **jobjectArray** JNICALL
Java_Android123_CwjTest2(JNIEnv *env, jclass clazz)
{
jobjectArray result; //定义返回对象
jclass intArrayClazz = (*env)->FindClass(env, "[I"); //查找整形数组
if (intArrayClazz == NULL)
{
return NULL;
}
result = (*env)->NewObjectArray(env, size, intArrayClazz, NULL); //构造一个新的数组对象
if (result == NULL)
{
return NULL;
}
for (int i = 0; i < 10 ; i++) //循环10次
{
jint szBuffer[256];
int j;
jintArray newIntArray = (*env)->NewIntArray(env, 10); //构造10个整形数组
if (newIntArray == NULL)
{
return NULL;
}
for (j = 0; j < 10 ; j++) //10个
{
szBuffer[j] = i + j;
}
(*env)->SetIntArrayRegion(env, newIntArray, 0, 10, szBuffer); //设置长度为10个
(*env)->SetObjectArrayElement(env, result, i, newIntArray);
(*env)->DeleteLocalRef(env, newIntArray);
}
return result;
}
3.JNI中有关异常的处理
JNI中抛出异常没有try...catch这样的,而是直接抛出错误
方法1: 使用ThrowNew,比如IOException类发生了FileNotFound
(*env)->ThrowNew(env,(*env)->FindClass("java/io/IOException"),"CWJLog Error, IOException");
方法2: 使用Throw,自己构造
jclass clazz = (*env)->FindClass(env, "java/io/IOException");
jmethodID methodId = (*env)->GetMethodID(env, clazz, "", "()V");
jthrowable throwable = (*env)->NewObject(env, clazz, methodId);
(*env)->Throw(env, throwable);
Android JNI入门第五篇——基本数据类型使用
最后更新于:2022-04-01 07:03:52
前面讲到了java和native数据类型,这里就开始做一下使用:
**第一步:新建工程**
**第二部:书写 java方法:**
~~~
public class NativeMethod {
static {
System.loadLibrary("com_nedu_jni_jnidemo5-jni");
}
public native boolean getBoolean(boolean b);
public native byte getByte(byte b);
public native char getChar(char c);
public native short getShort(short s);
public native int getInt(int i);
public native long getLong(long l);
public native float getFloat(float f);
public native double getDouble(double d);
}
~~~
**第三部:调用javac、javah命令生成h文件。**
**第四部:补充native方法,如下:**
~~~
#include<stdio.h>
#include <stdlib.h>
#include "com_nedu_jni_jnidemo5_NativeMethod.h"
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getBoolean
* Signature: (Z)Z
*/
JNIEXPORT jboolean JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getBoolean
(JNIEnv *e, jobject thiz, jboolean b){
return b;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getByte
* Signature: (B)B
*/
JNIEXPORT jbyte JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getByte
(JNIEnv *e, jobject thiz, jbyte by){
return by;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getChar
* Signature: (C)C
*/
JNIEXPORT jchar JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getChar
(JNIEnv *e, jobject thiz, jchar c){
return c;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getShort
* Signature: (S)S
*/
JNIEXPORT jshort JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getShort
(JNIEnv *e, jobject thiz, jshort s){
return s;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getInt
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getInt
(JNIEnv *e, jobject thiz, jint i){
return i;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getLong
* Signature: (J)J
*/
JNIEXPORT jlong JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getLong
(JNIEnv *e, jobject thiz, jlong l){
return l;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getFloat
* Signature: (F)F
*/
JNIEXPORT jfloat JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getFloat
(JNIEnv *e, jobject thiz, jfloat f){
return f;
}
/*
* Class: com_nedu_jni_jnidemo5_NativeMethod
* Method: getDouble
* Signature: (D)D
*/
JNIEXPORT jdouble JNICALL Java_com_nedu_jni_jnidemo5_NativeMethod_getDouble
(JNIEnv *e, jobject thiz, jdouble d){
return d;
}
~~~
**第五步:制作mk文件**
~~~
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_nedu_jni_jnidemo5-jni
LOCAL_SRC_FILES :=NativeMethod.c
include $(BUILD_SHARED_LIBRARY)
~~~
**第六步:调用native方法**
~~~
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView text=(TextView)findViewById(R.id.text);
NativeMethod method=new NativeMethod();
text.setText("返回boolean:"+method.getBoolean(true)+"\n"+
"返回byte:"+method.getByte((byte) 0)+"\n"+
"返回char:"+method.getChar('c')+"\n"+
"返回short:"+method.getShort((short) 1)+"\n"+
"返回int:"+method.getInt(1)+"\n"+
"返回long:"+method.getLong(9)+"\n"+
"返回float:"+method.getFloat((float) 1.0)+"\n"+
"返回double:"+method.getDouble(2.0)+"\n");
}
~~~
运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3fa96ae.gif)
总结:JNI中传过来的java基本类型可以直接使用。
Android JNI入门第四篇——Android.mk文件分析
最后更新于:2022-04-01 07:03:49
Android.mk文件是在使用NDK编译C代码时必须的文件,Android.mk文件中描述了哪些C文件将被编译且指明了如何编译。掌握Android.mk文件的编写主要是掌握其里头将要使用的一些关键字,先来看一个简单的例子,这个例子使用的是android NDK带的
HellJni的例子。
~~~
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
~~~
LOCAL_PATH 是描述所有要编译的C文件所在的根目录,这边的赋值为$(call my-dir),代表根目录即为Android.mk所在的目录。
include $(CLEAR_VARS) 代表在使用NDK编译工具时对编译环境中所用到的全局变量清零,如LOCAL_MODULE,LOCAL_SRC_FILES等,因为在一次NDK编译过程中可能会多次调用Android.mk文件,中间用到的全局变量可能是变化的。关于这个问题看了下面比较复杂的例子可能就明白了。
LOCAL_MODULE 是最后生成库时的名字的一部分,给其加上前缀lib和后缀.so就是生成的共享库的名字libhello-jni.so。
LOCAL_SRC_FILES 指明要被编译的c文件的文件名
include $(BUILD_SHARED_LIBRARY) 指明NDK编译时将生成一些共享库
参考:
[android编译系统makefile(Android.mk)写法](http://www.cnblogs.com/hesiming/archive/2011/03/15/1984444.html)
[android makefile(android.mk)分析(序)](http://blog.csdn.net/chief1985/article/details/4699831 "android makefile(android.mk)分析(序)")
[Android.mk的用法和基础](http://blog.csdn.net/zhandoushi1982/article/details/5316669 "Android.mk的用法和基础 ")
~~~
<!-- JiaThis Button BEGIN -->
<div id="ckepop">
<a href="http://www.jiathis.com/share" class="jiathis jiathis_txt" target="_blank"><img src="http://v2.jiathis.com/code_mini/images/btn/v1/jiathis1.gif" border="0" /></a>
<a class="jiathis_counter_style_margin:3px 0 0 2px"></a>
</div>
<script type="text/javascript" src="http://v2.jiathis.com/code_mini/jia.js" charset="utf-8"></script>
<!-- JiaThis Button END --><!-- JiaThis Button BEGIN -->
<div id="ckepop">
<a href="http://www.jiathis.com/share" class="jiathis jiathis_txt" target="_blank"><img src="http://v2.jiathis.com/code_mini/images/btn/v1/jiathis1.gif" border="0" /></a>
<a class="jiathis_counter_style_margin:3px 0 0 2px"></a>
</div>
<script type="text/javascript" src="http://v2.jiathis.com/code_mini/jia.js" charset="utf-8"></script>
<!-- JiaThis Button END -->
~~~
Android JNI入门第三篇——jni头文件分析
最后更新于:2022-04-01 07:03:47
## 一、 首先写了java文件:
~~~
public class HeaderFile {
private native void doVoid();
native int doShort();
native void doArray(Object[] o );
native int doInt(int i); //byte ,short ,int,long,float,double ,boolean,char
native int doInt(double d); //byte ,short ,int,long,float,double ,boolean,char
native int doInt(Object o);
native int doInt(double d1,double d2);
static native int doInt(double d1 ,double d2,double d3);
static native int doInt(double d1 ,float f,boolean b ,char[] c );
native int doInt(int[] i);
native int doInt(int[] i1,double[] i2 );
static native int doInt(int[] i1,double[] i2 ,Object[] o );
public native String doString(String s);
public native Object doObject(Object o );
public native Enumeration doInterface(Iterator it);
public native Student doStudent(Student s);
// native int[] doInt(int[] i); //byte ,short ,int,long,float,double ,boolean,char
public native String[] doString(String[] s);
public native Object[] doObjects(Object[] o );
public native Enumeration[] doInterface(Iterator[] it);
public native Student[] doStudent(Student[] s);
public native static Object doAll(int[] i , String[] s , Student[] student );
}
~~~
java文件中包含了private、public、protect等类型的方法,static 方法和非static 方法,返回类型有基础类型、对象等。
## 二、下面看一下生成的头文件:
~~~
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_nedu_jni_helloword_HeaderFile */
#ifndef _Included_com_nedu_jni_helloword_HeaderFile
#define _Included_com_nedu_jni_helloword_HeaderFile
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doVoid
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_HeaderFile_doVoid
(JNIEnv *, jobject);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doShort
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doShort
(JNIEnv *, jobject);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doArray
* Signature: ([Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_HeaderFile_doArray
(JNIEnv *, jobject, jobjectArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__I
(JNIEnv *, jobject, jint);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (D)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__D
(JNIEnv *, jobject, jdouble);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (Ljava/lang/Object;)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__Ljava_lang_Object_2
(JNIEnv *, jobject, jobject);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DD
(JNIEnv *, jobject, jdouble, jdouble);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (DDD)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DDD
(JNIEnv *, jclass, jdouble, jdouble, jdouble);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (DFZ[C)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DFZ_3C
(JNIEnv *, jclass, jdouble, jfloat, jboolean, jcharArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: ([I)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt___3I
(JNIEnv *, jobject, jintArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: ([I[D)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt___3I_3D
(JNIEnv *, jobject, jintArray, jdoubleArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: ([I[D[Ljava/lang/Object;)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt___3I_3D_3Ljava_lang_Object_2
(JNIEnv *, jclass, jintArray, jdoubleArray, jobjectArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doString
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_nedu_jni_helloword_HeaderFile_doString__Ljava_lang_String_2
(JNIEnv *, jobject, jstring);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doObject
* Signature: (Ljava/lang/Object;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doObject
(JNIEnv *, jobject, jobject);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInterface
* Signature: (Ljava/util/Iterator;)Ljava/util/Enumeration;
*/
JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInterface__Ljava_util_Iterator_2
(JNIEnv *, jobject, jobject);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doStudent
* Signature: (Lcom/nedu/jni/helloword/Student;)Lcom/nedu/jni/helloword/Student;
*/
JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doStudent__Lcom_nedu_jni_helloword_Student_2
(JNIEnv *, jobject, jobject);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doString
* Signature: ([Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doString___3Ljava_lang_String_2
(JNIEnv *, jobject, jobjectArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doObjects
* Signature: ([Ljava/lang/Object;)[Ljava/lang/Object;
*/
JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doObjects
(JNIEnv *, jobject, jobjectArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInterface
* Signature: ([Ljava/util/Iterator;)[Ljava/util/Enumeration;
*/
JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInterface___3Ljava_util_Iterator_2
(JNIEnv *, jobject, jobjectArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doStudent
* Signature: ([Lcom/nedu/jni/helloword/Student;)[Lcom/nedu/jni/helloword/Student;
*/
JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doStudent___3Lcom_nedu_jni_helloword_Student_2
(JNIEnv *, jobject, jobjectArray);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doAll
* Signature: ([I[Ljava/lang/String;[Lcom/nedu/jni/helloword/Student;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doAll
(JNIEnv *, jclass, jintArray, jobjectArray, jobjectArray);
#ifdef __cplusplus
}
#endif
#endif
~~~
## 三、头文件分析如下:
1、文件的前九行就不用说了,他们是是C、C++的头,应该很好理解。
2、方法的注释部分,每个方法都有它的注释部分,这些都是相似的,对其中一个分析:
~~~
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doVoid
* Signature: ()V
*/
~~~
注释部分分为三部分Class、Method、Signature。
Class:表示Native方法的类名称。
Method:表示方法名称
Signature:是方法的标识,它是一个标识符,主要供我们在JNI操作java对象的方法使用的。
Signature一般是两部分构成,一个方法的参数,另一个是返回类型。方法参数在括号里面,返回类型在后面,
例如
~~~
()V 返回为void,没有参数。
~~~
~~~
(DFZ[C)I 返回为int,参数为double、float、char[]
~~~
~~~
(Ljava/lang/String;)Ljava/lang/String;返回String,参数为String
~~~
如果不清楚其中的字符含义,就不能知道其中的意思,其中字符对应有基本类型、对象类型、数组类型。分析如下
1)基本类型的对应关系如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f7261f.gif)
2)方法参数或者返回值为java中的对象时,必须以“L”加上其路径,不过此路径必须以“/”分开,自定义的对象也使用本规则,不在包中时直接“L”加上类名称。比如说java.lang.String为“java/lang/String”,com.nedu.jni.helloword.Student为"com/nedu/jni/helloword/Student"
3)方法参数或者返回值为数组时类型前加上[,例如[I表示int[],[[[D表示 double[][][],即几维数组就加几个[。
看一下例子:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f8190f.gif)
3、方法的声明
~~~
JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_HeaderFile_doArray(JNIEnv *,jobject,jobjectArray);
~~~
从上面的头文件可以看出方法基本有7部分组成。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f95a78.gif)
1、3部分是都是JNI的关键字,表示此函数是要被JNI调用的。
2、表示方法的返回类型
4、为JNI中标识此方法来源于java的标识头
5、方法所在类的包名+类名
6、方法名
7、参数,它们有一个共同的特点,包含JNIEnv *――它是一个接口指针,用于定位函数表中的函数!
在JNI规范中一般称 为 “Interface Pointer”。看到这儿好像和过程调用很类似了!是的,JNI中的操作过程就是面向过程的!后面的jobject是 一个指向该类的指针,类似与C语言中的this。这个第二个参数是变化的,当该方法为类的实例方法时该参数为jobject;当该方法为类方法(即静态方法)时该参数为jclass,指向该类的class。
根据不同方法前缀生成的头文件比较如下:
1、static与非static的比较:
~~~
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DD
(JNIEnv *, <span style="background-color: rgb(255, 0, 0);">jobject</span>, jdouble, jdouble);
/*
* Class: com_nedu_jni_helloword_HeaderFile
* Method: doInt
* Signature: (DDD)I
*/
JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DDD
(JNIEnv *, <span style="color:#000000;background-color: rgb(255, 0, 0);">jclass</span>, jdouble, jdouble, jdouble);
~~~
第一个是非static方法,第二个是static方法,不同点如上红色标记。其中的不同将在以后提到。
2、 private、friendly、protected以及public这些方法限制符不会在JNI的头文件中出现。这些访问修饰符只有在其它类
使用这些方法时有效!JNI中不关心此修饰符!
Android JNI入门第二篇——Java参数类型与本地参数类型对照
最后更新于:2022-04-01 07:03:45
前面一篇通过简单的例子介绍了android中JNI的使用。这一篇从基础上了解一些Java参数类型与本地参数类型区别。
1)java中的返回值void和JNI中的void是完全对应的哦!(仅仅一个而已)。
2)java中的基本数据类型(byte ,short ,int,long,float,double ,boolean,char-8种)在JNI中对应的数据类型只要在前面加上j就对应了(jbyte ,jshort ,jint,jlong,jfloat,jdouble ,jboolean,jchar)。
3)java中的对象,包括类库中定义的类、接口以及自定义的类接口,都对应于JNI中的jobject。
4)java中基本数据类型的数组对应与JNI中的jarray类型。(type就是上面说的8种基本数据类型)
5)java中对象的数组对应于JNI中的jobjectArray类型。(在java中一切对象、接口以及数组都是对象)
下图是JNI规范中java数据类型和JNI数据类型的映射图。
第一幅为基本数据类型的映射图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f4bfc6.gif)
第二幅为引用数据类型的映射图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f5ebc3.gif)
Android JNI入门第一篇——HelloJni
最后更新于:2022-04-01 07:03:42
android支持使用NDK开发C程序,关于配置NDK环境问题应该不用再赘述了,这个网上有很多,这里通过一篇实例来讲述简单的JNI开发,大家可以参考这篇文章[(Get Your Eclipse-Integrated NDK On!)](http://blog.csdn.net/xyz_lmn/article/details/6050392)搭建Eclipse编译C语言为so文件的开发环境。
native方法实现步骤如下:
1、在Java中声明native()方法,然后编译(javac);
2、用javah产生一个.h文件;
3、编写包含.h文件的c文件
4、编译c文件
5、使用编译成功的so文件。
第一步:
1、声明native方法
~~~
public class Printf_Jni {
static {
System.loadLibrary("com_nedu_jni_helloword_printf-jni");
}
public native void printHello();
}
~~~
2、javac编译
进入java文件所在路径,调用javac命令,如图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f15c6c.gif)
第二步:使用javah命令生成.h头文件,如图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f2a6de.gif)
这个要回到src目录下,不知道什么原因,如果在上面的javac路径下会报错,如图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-14_56971f3f38718.gif)
使用javah命令生成的头文件如下:
~~~
public class Printf_Jni {
static {
System.loadLibrary("com_nedu_jni_helloword_printf-jni");
}
public native void printHello();
}
~~~
第三步:编写c文件,代码如下:
~~~
#include<stdio.h>
#include <stdlib.h>
#include "com_nedu_jni_helloword_Printf_Jni.h"
JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_Printf_1Jni_printHello
(JNIEnv *e, jobject j)
{
printf("Hello world!");
}
~~~
第四步,书写Android.mk文件,编译c文件
Android.mk文件如下:
~~~
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_nedu_jni_helloword_printf-jni
LOCAL_SRC_FILES :=Printf_Jni.c
include $(BUILD_SHARED_LIBRARY)
~~~
LOCAL_MODULE := com_nedu_jni_helloword_printf-jniLOCAL_MODULE := com_nedu_jni_helloword_printf-jniLOCAL_MODULE 表示so文件名
LOCAL_SRC_FILES 需要编译的文件
按照这篇文章[(Get Your Eclipse-Integrated NDK On!)](http://www.devdiv.com/home.php?mod=space&uid=14682&do=blog&id=2389)的介绍就可以在Eclipse编译了。
第五步:使用so文件:
通过下面的代码加载so文件
~~~
System.loadLibrary("com_nedu_jni_helloword_printf-jni");
~~~
通过下面的代码加载so文件通过下面的代码加载so文件
调用如下:
~~~
Printf_Jni print=new Printf_Jni();
~~~
~~~
print.printHello();
~~~
把NDK集成到Eclipse
最后更新于:2022-04-01 07:03:40
前言
最经在看Android方法的基础知识,看到一篇文章(http://www.rbgrn.net/content/348-get-your-eclipse-integrated-ndk-on)介绍如何把Android的NDK集成到Eclipse,顺便就把它翻译过来。
注:
* 技术名称保留不译,比如Cygwin
* Eclipse(英文版)的界面文本不译,比如按钮(Specify Resources)。
* 括号内的内容,是我添加的注释。
翻译正文:
在Android的游戏开发中,你迟早会发现有必要把部分代码跑得更快。用C写的Android代码比同样的Java版本,快上10~100倍。我可以证实这个事实,在我最新的3D游戏引擎中,我把部分组件用原生程序(即用C/C++写代码,编译成二进制的动态链接库文件)重写。
这样带来了极大的性能提升,但我们也不得不面对这么一个事实:Eclipse对Java的开发提供了极佳的支持的同时,对C的开发却极其糟糕。Eclipse不适合C的开发,对吗?错,现在向你展示如何在Eclipse中,建立一个快速的NDK开发环境。
首先声明一点,Eclipse不仅仅适合于开发Java应用程序。Eclilpse为Java开发提供了极好的支持,它的设计初衷也是为了Java。但得益于它的设计架构,Eclipse能够为包括C语言在内的任何语言的开发提供支持。一个名为CDT的插件,使得在Eclipse里能对C/C++进行开发。尽管我说得有些过了头。接下来这些,是你所需要的:
Android NDK(Native Development Kit)[http://developer.android.com/sdk/ndk/1.6_r1/index.html](http://developer.android.com/sdk/ndk/1.6_r1/index.html)
Eclipse CDT(C Development Tooling) [http://www.eclipse.org/cdt/](http://www.eclipse.org/cdt/)
如果用的是windows操作系统,你需要安装Cygwin,并安装上重要的包(特别是GCC和Make)。
接下来的步骤是:
安装上述三个包。我把NDK安装到C:/Android_NDK。在后面的文章中,我都默认为该目录是NDK的安装目录。
为了熟悉NDK,在C:/Android_NDK/apps目录中,你需要配置每个项目的“app”。就像NDK中的例子一样,它们能够正确编译运行,并要求严格的配置。
测试NDK的办法:
启动cygwin
cd /cygdrive/c/Android_NDK
make APP=hello-jni
根据上述的步骤,你将能够正确编译。如果cygwin漏装了GCC、Make或者其它开发包,你有必要重新安装Cygwin,并且确保全部必要的开发包已经安装。如果你遇到什么特殊的错误,我建议把这些错误报告给NDK用户组。
一旦NDK能够正常工作,你就能够添加和配置好你的NDK开发项目。参考NDK的example一样,你需要一个详细的build文件(Android.mk),该文件告诉NDK编译器编译什么文件。JNI代码文件通常放在应用程序项目的jni文件夹下。该目录下的Android.mk文件指引NDK编译器如何编译程序。
在完成这些基本配置后,你会想去写C代码。NDK使用Java标准的JNI技术实现。大量的关于JNI的文档在这里都适用。怎样去编写JNI代码,已经超出了本文的范畴。
接下来是有用的部分:
如果开发过NDK程序,你很有可能习惯使用文本编辑器或者vim去编辑的你C/C++代码,然后使用make APP=myapp的命令来编译,接着点击更新在Eclipse中的项目,最后盼望着那已部署上的共享库是最新的。多么痛苦的一件事啊!其实,有一个更好的方法。
目前,已经安装上了CDT,你就可以在Eclipse中编译你的C/C++文件。右击C/C++源文件,选择使用C/C++编辑器打开,就能够使用CDT编译器了。多友好!因为尚未编译C/C++源文件,你并不清楚代码正确与否。但这也使得你更友好地在一个界面下编辑代码。
生成项目也是快速而灵活的。你曾经使用过Eclipse的生成器吗?它们是一些可配置的开关,用于执行你所配置的命令和刷新工程资源。以下是我的配置:
右击你的项目,选择“properties”
在左边的列表中选择“Builders”
在右侧,点击“New…”按钮
在列表框,选择“Program”作为配置类型;点击“OK”按钮,进入下一个配置对话框。
命名name为“Native Builder”
选择Location为“c:/cygwin/bin/bash.exe”
“Working Directory”为“c:/cygwin/bin”
“Arguments”为:
--login –c “cd /cygdrive/c/Android_NDK && make APP=myapp”
确保在login前有两个连接号,-c后面的引号。
接着打开“refresh”的tab面板:
选中“Refresh resources upon completion”
选择“Specific resources”
点击“Specify resources”按钮,选择你项目的lib目录
选中“Recursively include sub-folders”
接着打开“Build Options”的tab面板:
选中“Allocate Console”
选中“Launch in background”
选中“Run the builder After a Clean”
选中“Run the builder During manual builds”
选中“Run the builder During auto builds”
选中“Specify working set of relevant resources”
点击“Specify Resources”按钮,选择你项目的JNI目录,并包含所有的文件。
点击“OK”按钮。
这里假设:cygwin安装在c:/cygwin;NDK安装在c:/Android_NDK;项目工程名称是“myapp”。根据你的安装配置,相应地替换这些参数。
你刚刚做了什么呢?你使得,在JNI的目录下,每次修改任何文件,Eclipse都通过cygwin自动启动NDK编译你的工程,并在控制台输出编译信息。当它编译完成,将自动刷新工程输出目录(lib),并触发ADT生成一个新的APK安装包。这一切都极其简便。
把NDK集成到Eclipse,实实在在地加速了我当前项目的开发进度。希望你能够在文本中受益。
前言
最后更新于:2022-04-01 07:03:38
> 原文出处:[移动开发专栏文章](http://blog.csdn.net/column/details/android-jni.html)
> 作者:[张兴业](http://blog.csdn.net/xyz_lmn)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
#Android JNI入门
> 专栏介绍了Eclipse集成NDK开发c程序,包括jni入门、java调C、C调java,语音编解码。