移植ffmpeg到android_1

.移植ffmpeg到android


-----------------------另一个方法

FFMPEG移植到Android(ubuntu环境下)

分类: androidndk+jni 2010-10-28 15:43 1964人阅读 评论(11) 收藏 举报

搞左2日,找了好多资料,终于把ffmpeg移植到android上了。其中参考了一篇介绍移植方法的doc文档,里面是用windows+Cygwin+ndk(windows版本)作为编译环境的,我这里直接用ubuntu了,省了很多配置了^_^。感谢前辈的无私奉献。。。

 

-->环境:

 

操作系统:Ubuntu 9.10

ffmpeg源码版本:ffmpeg-0.6.1(可以在http://ffmpeg.org/download.html下载源码)

android ndk版本:android-ndk-r4b-linux-x86(可以在http://androidappdocs.appspot.com/sdk/ndk/index.html下载)

 

-->设置NDK环境变量:

1、用root用户登陆,打开命令窗口,输入cd /root

2、输入ls -a,会显示一个隐藏文件.bashrc

3、输入vi .bashrc,按i进入编辑模式

4、文件底部添加以下两行:
NDK_ROOT=/home/ndk。这里指你ndk的目录路径
export NDK_ROOT
5、按esc退出,再按:wq保存

 

你可以测试一下有没有安装成功:
$ cd $NDK_ROOT
$ ./ndk-build NDK_PROJECT_PATH=$NDK_ROOT/samples/two-libs


找到生成的文件就代表安装正常了。

1、如果提示某个文件"Permission denied"之类的信息时,执行chmod 777your_filename就行了

2、如果提示找不到'cc1'等信息,执行chmod -R 777 *,就能解决了

 

-->配置源代码

 

在/home/ndk/samples/创建一个FFMPEG文件夹,在里面再新建一个jni文件夹,然后把ffmpeg源码放在jni里面,所以最后ffmpeg源码的路径是:
/home/ndk/samples/FFMPEG/jni/ffmpeg

 

1. 在ffmpeg源文件夹下创建一个config.sh,内容如下:
#!/bin/bash

PREBUILT=/home/ndk/build/prebuilt/linux/arm-eabi-4.4.0
PLATFORM=/home/ndk/build/platforms/android-3/arch-arm

./configure --target-os=linux /
 --arch=arm /
 --enable-version3 /
 --enable-gpl /
 --enable-nonfree /
 --disable-stripping /
 --disable-ffmpeg /
 --disable-ffplay /
 --disable-ffserver /
 --disable-ffprobe /
 --disable-encoders /
 --disable-muxers /
 --disable-devices /
 --disable-protocols /
 --enable-protocol=file /
 --enable-avfilter /
 --disable-network /
 --disable-mpegaudio-hp /
 --disable-avdevice /
 --enable-cross-compile /
 --cc=$PREBUILT/bin/arm-eabi-gcc /
 --cross-prefix=$PREBUILT/bin/arm-eabi- /
 --nm=$PREBUILT/bin/arm-eabi-nm /
 --extra-cflags="-fPIC -DANDROID" /
 --disable-asm /
 --enable-neon /
 --enable-armv5te /
 --extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib$PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o$PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"

记得PREBUILT和PLATFORM要设置正确,它在你的NDK里面,你可以按自己的填写

 

2. 然后打开命令窗口,cdffmpeg源文件夹下。
$ chmod +xconfig.sh       
$ ./config.sh

如果配置正确的话显示出来的最后两行是这样的:
License: nonfree and unredistributable
Creating config.mak and config.h...


3、Configure完成后接下做NDK编译的条件了。
Android的GCC是不支持restrict关键字的,所以把ffmpeg源码下configure生成的config.h文件中的这一行:
#define restrict restrict   改成#definerestrict   
如果重新Configure的话记得要把这个关键字去掉。

编辑libavutil/libm.h把其中的static的方法都删除(如果找不到libavutil/libm.h,就表明你的ffmpeg版本不是0.6而是0.5的。)

分别把libavutil、libavcodec、libavformat、libavfilter、libpostproct和libswscale下的Makefile文件中下面两行删除掉:
include $(SUBDIR)../subdir.mak
include $(SUBDIR)../config.mak

 

然后在ffmpeg源文件夹下新建一个av.mk文件,内容如下:(这些不用替换回车)
# LOCAL_PATH is one of libavutil, libavcodec, libavformat, orlibswscale

#include $(LOCAL_PATH)/../config-$(TARGET_ARCH).mak
include $(LOCAL_PATH)/../config.mak

OBJS :=
OBJS-yes :=
MMX-OBJS-yes :=
include $(LOCAL_PATH)/Makefile

# collect objects
OBJS-$(HAVE_MMX) += $(MMX-OBJS-yes)
OBJS += $(OBJS-yes)

FFNAME := lib$(NAME)
FFLIBS := $(foreach,NAME,$(FFLIBS),lib$(NAME))
FFCFLAGS  = -DHAVE_AV_CONFIG_H -Wno-sign-compare-Wno-switch -Wno-pointer-sign
FFCFLAGS += -DTARGET_CONFIG=/"config-$(TARGET_ARCH).h/"

ALL_S_FILES := $(wildcard $(LOCAL_PATH)/$(TARGET_ARCH)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES :=  /
 $(LOCAL_PATH)  /
 $(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
#LOCAL_SHARED_LIBRARIES :=(这行根据下面蓝色的来填写相应的lib)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_SHARED_LIBRARY)

 

----------------------------------------

 

ffmpeg/libavutil:不需要这一行,直接加#屏蔽掉。
ffmpeg/libswscale:LOCAL_SHARED_LIBRARIES :=libavutil
ffmpeg/libavfilter:LOCAL_SHARED_LIBRARIES :=libavutil libswscalelibavcodec
ffmpeg/libpostproc:LOCAL_SHARED_LIBRARIES :=libavutil

可能编译libavdevice也是用上面这个Android.mk,然后根据提示的错误没改变红色这行就OK了。


最后就是运行ndk-build了:
$ cd $NDK_ROOT
$ ./ndk-build NDK_PROJECT_PATH=$NDK_ROOT/samples/FFMPEG


最后生成libavcodec.so、libavutil.so、libavformat.so、libavfilter.so、libswscale.so和libpostproc.so以及libffmpeg.so,你会发现libffmpeg.so只有2K(1599个字节),其实这个文件是没有加载任何东西而编译成的空SO文件,没有任何函数的SO文件。所以不能用,但其它的都可以。估计是没有LOCAL_SRC_FILES的原因。

 

 

另外一篇不错的参考文章:http://www.eoeandroid.com/thread-42184-1-1.html,它生成的是静态库

分享到:
  • 上一篇:横竖屏切换测试
  • 下一篇:Ubuntu+旧版androidndk生成so库文件
查看评论
8楼 perfy001 2011-09-22 10:30发表 [回复] [引用] [举报]
FFMPEG移植到Android(ubuntu环境下)
遇到不少问题,想向楼主请教啊!可楼主见不着人了!!
7楼 abc526541845 2011-05-06 14:29发表 [回复] [引用] [举报]
FFMPEG移植到Android(ubuntu环境下)
关注 FFMPEG移植到Android(ubuntu环境下) FFMPEG移植到Android(ubuntu环境下)
最近也要做这方面的工作
6楼 HongMenzhouxiangang 2011-04-1414:28发表 [回复] [引用] [举报]
FFMPEG移植到Android(ubuntu环境下)
我也编译成功了。不过不是用root用户。
Re: shenchao8528 2011-04-21 18:37发表 [回复] [引用] [举报]
FFMPEG移植到Android(ubuntu环境下)
回复 HongMenzhouxiangang:你编译成功以后,是不是有两个libffmpeg.so?
5楼 hj475955463 2011-03-23 17:34发表 [回复] [引用] [举报]
FFMPEG移植到Android(ubuntu环境下)
楼主您好,现在我已经把ffmpeg编译成功了,但是不知道怎么在androidNDK下使用,网上的例子也都没说明白,您能提供一个一个可以使用的例子么?万分感谢,可以发我邮箱[email protected]


用NDK编译FFmpeg  [复制链接]

android内置的编解码器实在太少,于是我们需要FFmpeg。Android提供了NDK,为我们使用FFmpeg这种C语言代码提供了方便。
首先创建一个标准的Android项目vPlayer

  1. android create project -n vPlayer -t 8 -p vPlayer -kme.abitno.vplayer -a PlayerView
复制代码
然后在vPlayer目录里
  1. mkdir jni && cd jni
  2. wget http://ffmpeg.org/releases/ffmpeg-0.6.tar.bz2
  3. tar xf ffmpeg-0.6.tar.bz2 && mvffmpeg-0.6 ffmpeg && cd ffmpeg
复制代码
在ffmpeg下新建一个config.sh,内容如下,注意把PREBUILT和PLATFORM设置正确。另外里面有些参数你也可以自行调整,我主要是为了配置一个播放器而这样设置的。
  1. #!/bin/bash

  2. PREBUILT=/home/abitno/Android/android-ndk-r4/build/prebuilt/linux-x86/arm-eabi-4.4.0
  3. PLATFORM=/home/abitno/Android/android-ndk-r4/build/platforms/android-8/arch-arm

  4. ./configure --target-os=linux \
  5.        --arch=arm \
  6.        --enable-version3 \
  7.        --enable-gpl \
  8.        --enable-nonfree \
  9.        --disable-stripping \
  10.        --disable-ffmpeg \
  11.        --disable-ffplay \
  12.        --disable-ffserver \
  13.        --disable-ffprobe \
  14.        --disable-encoders \
  15.        --disable-muxers \
  16.        --disable-devices \
  17.        --disable-protocols \
  18.        --enable-protocol=file \
  19.        --enable-avfilter \
  20.        --disable-network \
  21.        --disable-mpegaudio-hp \
  22.        --disable-avdevice \
  23.        --enable-cross-compile \
  24.        --cc=$PREBUILT/bin/arm-eabi-gcc \
  25.        --cross-prefix=$PREBUILT/bin/arm-eabi-\
  26.        --nm=$PREBUILT/bin/arm-eabi-nm \
  27.        --extra-cflags="-fPIC -DANDROID" \
  28.        --disable-asm \
  29.        --enable-neon \
  30.        --enable-armv5te \
  31.       --extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib$PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o$PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"
复制代码
运行config.sh开始configure
  1. chmod +x config.sh
  2. ./config.sh
复制代码
configure完成后,编辑刚刚生成的config.h,找到这句
  1. #define restrict restrict
复制代码
Android的GCC不支持restrict关键字,于是修改成下面这样
  1. #define restrict
复制代码
编辑libavutil/libm.h,把其中的static方法都删除。

分别修改libavcodec、libavfilter、libavformat、libavutil、libpostproc和libswscale下的Makefile,把下面两句删除
  1. include $(SUBDIR)../subdir.mak
  2. include $(SUBDIR)../config.mak
复制代码
在ffmpeg下添加一个文件av.mk,内容如下
  1. # LOCAL_PATH is one of libavutil, libavcodec, libavformat, orlibswscale

  2. #include $(LOCAL_PATH)/../config-$(TARGET_ARCH).mak
  3. include $(LOCAL_PATH)/../config.mak

  4. OBJS :=
  5. OBJS-yes :=
  6. MMX-OBJS-yes :=
  7. include $(LOCAL_PATH)/Makefile

  8. # collect objects
  9. OBJS-$(HAVE_MMX) += $(MMX-OBJS-yes)
  10. OBJS += $(OBJS-yes)

  11. FFNAME := lib$(NAME)
  12. FFLIBS := $(foreach,NAME,$(FFLIBS),lib$(NAME))
  13. FFCFLAGS  =-DHAVE_AV_CONFIG_H -Wno-sign-compare -Wno-switch-Wno-pointer-sign
  14. FFCFLAGS +=-DTARGET_CONFIG=\"config-$(TARGET_ARCH).h\"

  15. ALL_S_FILES := $(wildcard$(LOCAL_PATH)/$(TARGET_ARCH)  
  16. JNIEXPORT void JNICALL Java_com_chnic_jni_SayHellotoCPP_sayHello  
  17.   (JNIEnv *, jobject, jstring);  

 

仔细观察一下这个方法,在注释上标注类名、方法名、签名(Signature),至于这个签名是做什么用的,我们以后再说。在这里最重要的是Java_com_chnic_jni_SayHellotoCPP_sayHello这个方法签名。在Java端我们执行sayHello(Stringname)这个方法之后,JVM就会帮我们唤醒在DLL里的Java_com_chnic_jni_SayHellotoCPP_sayHello这个方法。因此我们新建一个C++ source file来实现这个方法。

Cpp代码
  1. #include <iostream.h>  
  2. #include "com_chnic_jni_SayHellotoCPP.h"  
  3.   
  4.   
  5. JNIEXPORT void JNICALL Java_com_chnic_jni_SayHellotoCPP_sayHello   
  6.   (JNIEnv* env, jobject obj, jstring name)  
  7. {  
  8.     const char* pname = env->GetStringUTFChars(name, NULL);  
  9.     cout << "Hello, " << pname << endl;  
  10. }  

 

因为我们生成的那个头文件是在C++工程的根目录不是在环境目录,所以我们要把尖括号改成单引号,至于VC++的环境目录可以在Tools->Options->Directories里设置。F7编译工程发现缺少jni.h这个头文件。这个头文件可以在%JAVA_HOME%\include目录下找到。把这个文件拷贝到C++工程目录,继续编译发现还是找不到。原来是因为在我们刚刚生成的那个头文件里,jni.h这个文件是被 #include<jni.h>引用进来的,因此我们把尖括号改成双引号#include"jni.h",继续编译发现少了jni_md.h文件,接着在%JAVA_HOME%\include\win32下面找到那个头文件,放入到工程根目录,F7编译成功。在Debug目录里会发现生成了HelloEnd.dll这个文件。

 

这个时候后端的C++代码也已经完成,接下来的任务就是怎么把他们连接在一起了,要让前端的java程序“认识并找到”这个动态链接库,就必须把这个DLL放在windowspath环境变量下面。有两种方法可以做到:

 

  1. 把这个DLL放到windows下面的sysytem32文件夹下面,这个是windows默认的path
  2. 复制你工程的Debug目录,我这里是C:\Program Files\Microsoft VisualStudio\MyProjects\HelloEnd\Debug这个目录,把这个目录配置到Uservariable的Path下面。重启eclipse,让eclipse在启动的时候重新读取这个path变量。

 

比较起来,第二种方法比较灵活,在开发的时候不用来回copydll文件了,节省了很多工作量,所以在开发的时候推荐用第二种方法。在这里我们使用的也是第二种,eclipse重启之后打开SayHellotoCPP这个类。其实我们上面做的那些是不是是让JVM能找到那些DLL文件,接下来我们要让我们自己的java代码“认识”这个动态链接库。加入System.loadLibrary("HelloEnd");这句到静态初始化块里。

 

Java代码
  1. package com.chnic.jni;  
  2.   
  3. public class SayHellotoCPP {  
  4.       
  5.     static{  
  6.         System.loadLibrary("HelloEnd");  
  7.     }  
  8.     public SayHellotoCPP(){  
  9.     }  
  10.     public native void sayHello(String name);  
  11.       
  12. }  

 

这样我们的代码就能认识并加载这个动态链接库文件了。万事俱备,只欠测试代码了,接下来编写测试代码。

Java代码
  1. SayHellotoCPP shp = new SayHellotoCPP();  
  2. shp.sayHello("World");  

 

我们不让他直接Hello,World。我们把World传进去,执行代码。发现控制台打印出来Hello,World这句话。就此一个最简单的JNI程序已经开发完成。也许有朋友会对CPP代码里的

Cpp代码
  1. const char* pname = env->GetStringUTFChars(name, NULL);  

 

 这句有疑问,这个GetStringUTFChars就是JNI给developer提供的API,我们以后再讲。在这里不得不多句嘴。

  1. 因为JNI有一个Native这个特点,一点有项目用了JNI,也就说明这个项目基本不能跨平台了。
  2. JNI调用是相当慢的,在实际使用的之前一定要先想明白是否有这个必要。
  3. 因为C++和C这样的语言非常灵活,一不小心就容易出错,比如我刚刚的代码就没有写析构字符串释放内存,对于javadeveloper来说因为有了GC垃圾回收机制,所以大多数人没有写析构函数这样的概念。所以JNI也会增加程序中的风险,增大程序的不稳定性。
 

其实在Java代码中,除了对本地方法标注native关键字和加上要加载动态链接库之外, JNI基本上是对上层coder透明的,上层coder调用那些本地方法的时候并不知道这个方法的方法体究竟是在哪里,这个道理就像我们用JDK所提供的API一样。所以在Java中使用 JNI还是很简单的,相比之下在C++中调用java,就比前者要复杂的多了。

 

现在来介绍下JNI里的数据类型。在C++里,编译器会很据所处的平台来为一些基本的数据类型来分配长度,因此也就造成了平台不一致性,而这个问题在Java中则不存在,因为有JVM的缘故,所以Java中的基本数据类型在所有平台下得到的都是相同的长度,比如int的宽度永远都是32位。基于这方面的原因,java和c++的基本数据类型就需要实现一些mapping,保持一致性。下面的表可以概括:

 

    Java类型              本地类型               JNI中定义的别名   
int long jint
long _int64 jlong
byte signed char jbyte
boolean unsigned char jboolean
char unsigned short jchar
short short jshort
float float jfloat
double double jdouble
Object _jobject* jobject

 

上面的表格是我在网上搜的,放上来给大家对比一下。对于每一种映射的数据类型,JNI的设计者其实已经帮我们取好了相应的别名以方便记忆。如果想了解一些更加细致的信息,可以去看一些jni.h这个头文件,各种数据类型的定义以及别名就被定义在这个文件中。

 

了解了JNI中的数据类型,下面就来看这次的例子。这次我们用Java来实现一个前端的market(以下就用Foreground代替)用CPP来实现一个后端factory(以下用backend代替)。我们首先还是来编写包含本地方法的java类。

 

Java代码
  1. package com.chnic.service;  
  2.   
  3. import com.chnic.bean.Order;  
  4.   
  5. public class Business {  
  6.     static{  
  7.         System.loadLibrary("FruitFactory");  
  8.     }  
  9.       
  10.     public Business(){  
  11.           
  12.     }  
  13.       
  14.     public native double getPrice(String name);  
  15.     public native Order getOrder(String name, int amount);  
  16.     public native Order getRamdomOrder();  
  17.     public native void analyzeOrder(Order order);  
  18.       
  19.     public void notification(){  
  20.         System.out.println("Got a notification.");  
  21.     }  
  22.       
  23.     public static void notificationByStatic(){  
  24.         System.out.println("Got a notification in a static method.");  
  25.     }  
  26. }  
 

这个类里面包含4个本地方法,一个静态初始化块加载将要生成的dll文件。剩下的方法都是很普通的java方法,等会在backend中回调这些方法。这个类需要一个名为Order的JavaBean。

 

Java代码
  1. package com.chnic.bean;  
  2.   
  3. public class Order {  
  4.       
  5.     private String name = "Fruit";  
  6.     private double price;  
  7.     private int amount = 30;  
  8.       
  9.     public Order(){  
  10.           
  11.     }  
  12.   
  13.     public int getAmount() {  
  14.         return amount;  
  15.     }  
  16.    
  17.     public void setAmount(int amount) {  
  18.         this.amount = amount;  
  19.     }  
  20.   
  21.     public String getName() {  
  22.         return name;  
  23.     }  
  24.   
  25.     public void setName(String name) {  
  26.         this.name = name;  
  27.     }  
  28.   
  29.     public double getPrice() {  
  30.         return price;  
  31.     }  
  32.   
  33.     public void setPrice(double price) {  
  34.         this.price = price;  
  35.     }  
  36. }  

 

JavaBean中,我们为两个私有属性赋值,方便后面的例子演示。到此为止除了测试代码之外的Java端的代码就全部高调了,接下来进行生成.h头文件、建立C++工程的工作,在这里就一笔带过,不熟悉的朋友请回头看第一篇。在工程里我们新建一个名为Foctory的C++source file 文件,去实现那些native方法。具体的代码如下。

 

Cpp代码
  1. #include <iostream.h>  
  2. #include <string.h>  
  3. #include "com_chnic_service_Business.h"  
  4.   
  5. jobject getInstance(JNIEnv* env, jclass obj_class);  
  6.   
  7. JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice(JNIEnv* env,   
  8.                                                                    jobject obj,   
  9.                                                                    jstring name)  
  10. {  
  11.     const char* pname = env->GetStringUTFChars(name, NULL);  
  12.     cout << "Before release: "  << pname << endl;  
  13.   
  14.     if (strcmp(pname, "Apple") == 0)  
  15.     {  
  16.         env->ReleaseStringUTFChars(name, pname);  
  17.         cout << "After release: " << pname << endl;  
  18.         return 1.2;  
  19.     }   
  20.     else  
  21.     {  
  22.         env->ReleaseStringUTFChars(name, pname);  
  23.         cout << "After release: " << pname << endl;  
  24.         return 2.1;  
  25.     }     
  26. }  
  27.   
  28.   
  29. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder(JNIEnv* env,   
  30.                                                                    jobject obj,   
  31.                                                                    jstring name,   
  32.                                                                    jint amount)  
  33. {  
  34.     jclass order_class = env->FindClass("com/chnic/bean/Order");  
  35.     jobject order = getInstance(env, order_class);  
  36.       
  37.     jmethodID setName_method = env->GetMethodID(order_class, "setName""(Ljava/lang/String;)V");  
  38.     env->CallVoidMethod(order, setName_method, name);  
  39.   
  40.     jmethodID setAmount_method = env->GetMethodID(order_class, "setAmount""(I)V");  
  41.     env->CallVoidMethod(order, setAmount_method, amount);  
  42.   
  43.     return order;  
  44. }  
  45.   
  46. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder(JNIEnv* env,   
  47.                                                                          jobject obj)  
  48. {  
  49.     jclass business_class = env->GetObjectClass(obj);  
  50.     jobject business_obj = getInstance(env, business_class);  
  51.   
  52.     jmethodID notification_method = env->GetMethodID(business_class, "notification""()V");  
  53.     env->CallVoidMethod(obj, notification_method);  
  54.   
  55.     jclass order_class = env->FindClass("com/chnic/bean/Order");  
  56.     jobject order = getInstance(env, order_class);  
  57.     jfieldID amount_field = env->GetFieldID(order_class, "amount""I");  
  58.     jint amount = env->GetIntField(order, amount_field);  
  59.     cout << "amount: " << amount << endl;  
  60.     return order;  
  61. }  
  62.   
  63.   
  64. JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder (JNIEnv* env,   
  65.                                                                      jclass cls,   
  66.                                                                      jobject obj)  
  67. {  
  68.     jclass order_class = env->GetObjectClass(obj);  
  69.     jmethodID getName_method = env->GetMethodID(order_class, "getName""()Ljava/lang/String;");  
  70.     jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));  
  71.     const char* pname = env->GetStringUTFChars(name_str, NULL);  
  72.   
  73.     cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;  
  74.     jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic""()V");  
  75.     env->CallStaticVoidMethod(cls, notification_method_static);  
  76.   
  77. }  
  78.   
  79. jobject getInstance(JNIEnv* env, jclass obj_class)  
  80. {  
  81.     jmethodID construction_id = env->GetMethodID(obj_class, "<init>""()V");  
  82.     jobject obj = env->NewObject(obj_class, construction_id);  
  83.     return obj;  
  84. }  
 

可以看到,在我Java中的四个本地方法在这里全部被实现,接下来针对这四个方法来解释下,一些JNI相关的API的使用方法。先从第一个方法讲起吧:

 

1.getPrice(String name)

 

这个方法是从foreground传递一个类型为string的参数到backend,然后backend判断返回相应的价格。在cpp的代码中,我们用GetStringUTFChars这个方法来把传来的jstring变成一个UTF-8编码的char型字符串。因为jstring的实际类型是jobject,所以无法直接比较。

GetStringUTFChars方法包含两个参数,第一参数是你要处理的jstring对象,第二个参数是否需要在内存中生成一个副本对象。将jstring转换成为了一个constchar*了之后,我们用string.h中带strcmp函数来比较这两个字符串,如果传来的字符串是“Apple”的话我们返回1.2。反之返回2.1。在这里还要多说一下ReleaseStringUTFChars这个函数,这个函数从字面上不难理解,就是释放内存用的。有点像cpp里的析构函数,只不过Sun帮我们已经封装好了。由于在JVM中有GC这个东东,所以多数javacoder并没有写析构的习惯,不过在JNI里是必须的了,否则容易造成内存泄露。我们在这里在release之前和之后分别打出这个字符串来看一下效果。

 

粗略的解释完一些API之后,我们编写测试代码。

 

Java代码
  1. Business b = new Business();          
  2. System.out.println(b.getPrice("Apple"));  
 

运行这段测试代码,控制台上打出

 

Before release: Apple
After release:

我的更多文章
  • GCC 警告提示的用法 (2011-10-11 16:36:12)
  • BCB (2011-10-09 10:28:36)
  • Uboot对U盘的支持 (2011-10-07 21:57:05)
  • rvds 4.x 总结与延展 (2011-09-23 08:33:59)
  • NFS 挂载文件系统出现 not responding 错误的解决方法 (2011-09-20 15:41:20)
  • 使用 Strace 和 GDB 调试工具 (2011-08-26 19:23:49)

你可能感兴趣的:(移植ffmpeg到android_1)