ndk部分
1、下载ndk
这里就一笔带过了。
2、解压ndk
不要解压,文件权限会出错。执行之,会自动解压,然后mv到想放的地方。我放到了”/usr/local/bin/android-ndk-r10d”(此目录之后用$ndk_dir指代)。
3、下载ffmpeg
我下的是2.5.3版本。
4、解压ffmpeg
解压ffmpeg到$ndk_dir/sources/ffmpeg-2.5.3。
5、修改ffmpeg编译配置
在ffmpeg-2.5.3目录下把configure文件中的这几行,目的是去掉默认生成的库名字libavcodec.so.55最后那个”55″的版本号。
1
2
3
4
|
slibname_with_major= '$(slibname).$(libmajor)' lib_install_extra_cmd= '$$(ranlib) "$(libdir)/$(libname)"' slib_install_name= '$(slibname_with_version)' slib_install_links= '$(slibname_with_major) $(slibname)' |
1
2
3
4
|
slibname_with_major= '$(slibpref)$(fullname)-$(libmajor)$(slibsuf)' lib_install_extra_cmd= '$$(ranlib) "$(libdir)/$(libname)"' slib_install_name= '$(slibname_with_major)' slib_install_links= '$(slibname)' |
6、编译ffmpeg
在ffmpeg-2.5.3目录下创建文件build_android.sh。
注意前三行要按照自己的路径正确配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#!/bin/bash ndk=/usr/local/android-ndk-r10d sysroot=$ndk/platforms/android- 15 /arch-arm/ toolchain=$ndk/toolchains/arm-linux-androideabi- 4.9 /prebuilt/darwin-x86_64 function build_one { ./configure \ --prefix=$prefix \ --enable-shared \ --disable- static \ --disable-doc \ --disable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --disable-ffserver \ --disable-avdevice \ --disable-doc \ --disable-symver \ --cross-prefix=$toolchain/bin/arm-linux-androideabi- \ --target-os=linux \ --arch=arm \ --enable-cross-compile \ --sysroot=$sysroot \ --extra-cflags= "-os -fpic $addi_cflags" \ --extra-ldflags= "$addi_ldflags" \ $additional_configure_flag make clean make make install } cpu=arm prefix=$(pwd)/android/$cpu addi_cflags= "-marm" build_one |
保存后执行
1
2
|
sudo chmod +x build_android.sh ./build_android.sh |
编译会花上一段时间。
7、查看编译结果
编译完成后$ndk_dir/sources/ffmpeg-2.5.3下面会多出一个android目录,里面就是我们想要的编译好的库。
1
|
[cg @local ]# ls -r android/ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
arm android //arm: android.mk include lib android //arm/include: libavcodec libavfilter libavformat libavutil libswresample libswscale android //arm/include/libavcodec: avcodec.h avfft.h dv_profile.h dxva2.h old_codec_ids.h vaapi.h vda.h vdpau.h version.h vorbis_parser.h xvmc.h android //arm/include/libavfilter: asrc_abuffer.h avcodec.h avfilter.h avfiltergraph.h buffersink.h buffersrc.h version.h android //arm/include/libavformat: avformat.h avio.h version.h android //arm/include/libavutil: adler32.h avstring.h cast5.h downmix_info.h hash.h macros.h opt.h replaygain.h time.h aes.h avutil.h channel_layout.h error.h hmac.h mathematics.h parseutils.h ripemd.h timecode.h attributes.h base64.h common.h eval.h imgutils.h md5.h pixdesc.h samplefmt.h timestamp.h audio_fifo.h blowfish.h cpu.h ffversion.h intfloat.h mem.h pixelutils.h sha.h version.h audioconvert.h bprint.h crc.h fifo.h intreadwrite.h motion_vector.h pixfmt.h sha512.h xtea.h avassert.h bswap.h dict.h file.h lfg.h murmur3.h random_seed.h stereo3d.h avconfig.h buffer.h display.h frame.h log.h old_pix_fmts.h rational.h threadmessage.h android //arm/include/libswresample: swresample.h version.h android //arm/include/libswscale: swscale.h version.h android //arm/lib: libavcodec- 56 .so libavfilter- 5 .so libavformat- 56 .so libavutil- 54 .so libswresample- 1 .so libswscale- 3 .so pkgconfig libavcodec.so libavfilter.so libavformat.so libavutil.so libswresample.so libswscale.so android //arm/lib/pkgconfig: libavcodec.pc libavfilter.pc libavformat.pc libavutil.pc libswresample.pc libswscale.pc |
8、给ffmpeg库写android.mk使其可用
创建$ndk_dir/sources/ffmpeg-2.5.3/android/arm/android.mk文件,内容如下:
要注意其中.so前面的数字应该改成你的ffmpeg版本编译出来的数字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
local_path:= $(call my-dir) include $(clear_vars) local_module:= libavcodec local_src_files:= lib/libavcodec- 56 .so local_export_c_includes := $(local_path)/include include $(prebuilt_shared_library) include $(clear_vars) local_module:= libavformat local_src_files:= lib/libavformat- 56 .so local_export_c_includes := $(local_path)/include include $(prebuilt_shared_library) include $(clear_vars) local_module:= libswscale local_src_files:= lib/libswscale- 3 .so local_export_c_includes := $(local_path)/include include $(prebuilt_shared_library) include $(clear_vars) local_module:= libavutil local_src_files:= lib/libavutil- 54 .so local_export_c_includes := $(local_path)/include include $(prebuilt_shared_library) include $(clear_vars) local_module:= libavfilter local_src_files:= lib/libavfilter- 5 .so local_export_c_includes := $(local_path)/include include $(prebuilt_shared_library) include $(clear_vars) local_module:= libswresample local_src_files:= lib/libswresample- 1 .so local_export_c_includes := $(local_path)/include include $(prebuilt_shared_library) |
至此ndk配置完毕,后面是配置android studio的部分。
android studio部分
android studio和eclipse不太一样,它有一定的自动生成android.mk并自动搞定jni的能力。
但目前还并不足以让我们使用起来ffmpeg库。
因此我们的思路是禁用掉android studio自动ndk-build的功能,手动编译我们的c代码达到目的。
首先当然要新建一个android studio项目。
我们使用$root_dir指代项目根目录。
1、android studio配置ndk路径
$root_dir/local.properties原先只配置了sdk。
1
|
sdk.dir=/usr/local/bin/android-sdk-macosx |
给它增加一行ndk的配置
1
2
|
sdk.dir=/usr/local/bin/android-sdk-macosx ndk.dir=/usr/local/bin/android-ndk-r10d |
2、配置build.gradle
项目里面有两个build.gradle,一个在根目录下,一个在$root_dir/app/src下,我们要修改的是后者。
a>添加这一段以禁用自动ndk-build。
1
|
sourcesets.main.jni.srcdirs = [] |
b>添加这一段让它知道用库
1
2
3
4
5
|
ndk { abifilter "armeabi" modulename "ovsplayer" ldlibs "log" , "z" , "m" , "jnigraphics" , "android" } |
修改后的build.gradle是这样的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
android { compilesdkversion 21 buildtoolsversion "21.1.1" sourcesets.main.jni.srcdirs = [] // 禁用自动执行ndk-build defaultconfig { applicationid "com.example.chengang.myapplication" minsdkversion 15 targetsdkversion 21 versioncode 1 versionname "1.0" ndk { abifilter "armeabi" modulename "ovsplayer" // 这个是c文件的名字 ldlibs "log" , "z" , "m" , "jnigraphics" , "android" } } buildtypes { release { minifyenabled false proguardfiles getdefaultproguardfile( 'proguard-android.txt' ), 'proguard-rules.pro' } } } |
3、生成头文件
执行命令,注意路径要根据自己的情况更改。
javah -d jni -classpath ..\..\build\intermediates\classes\debug com.example.nativeapp.app.mainactivity
会生成这个文件$root_dir/app/src/main/jni/com_example_chengang_myapplication_mainactivity.h
4、编写c文件
$root_dir/app/src/main/jni/ovsplayer.c内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include <stdio.h> #include <stdlib.h> #include "com_example_chengang_myapplication_mainactivity.h" #include "libavutil/avutil.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" jniexport jstring jnicall java_com_example_chengang_myapplication_mainactivity_getstringfromnative (jnienv * env , jobject obj) { const char *url = "/mnt/sdcard/1.mp4" ; av_register_all(); avformatcontext *pformatctx = null ; int ret = avformat_open_input(&pformatctx, url, null , null ); ret = avformat_find_stream_info(input_context, null ); int streamnum = input_context->nb_streams; char wd[ 512 ]; sprintf(wd, "avcodec version %u\n, streamnum[%d]" , avcodec_version() , streamnum ); return (*env)->newstringutf(env, wd); } |
5、编写java文件
$root_dir/app/src/main/java/com/example/chengang/myapplication/mainactivity.java内容如下。
其中getstringfromnative()方法是我们实现的,打印了ffmpeg库的版本号(我编译的这个是3673444)和视频文件的信息出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package com.example.chengang.myapplication; import android.support.v7.app.actionbaractivity; import android.os.bundle; import android.view.menu; import android.view.menuitem; import android.widget.textview; public class mainactivity extends actionbaractivity { @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.activity_main); textview view = (textview) findviewbyid(r.id.mytext); view.settext( this .getstringfromnative()); } @override public boolean oncreateoptionsmenu(menu menu) { // inflate the menu; this adds items to the action bar if it is present. getmenuinflater().inflate(r.menu.menu_main, menu); return true ; } @override public boolean onoptionsitemselected(menuitem item) { // handle action bar item clicks here. the action bar will // automatically handle clicks on the home/up button, so long // as you specify a parent activity in androidmanifest.xml. int id = item.getitemid(); //noinspection simplifiableifstatement if (id == r.id.action_settings) { return true ; } return super .onoptionsitemselected(item); } public native string getstringfromnative(); static { system.loadlibrary( "swresample-1" ); system.loadlibrary( "avutil-54" ); system.loadlibrary( "avformat-56" ); system.loadlibrary( "avcodec-56" ); system.loadlibrary( "swscale-3" ); system.loadlibrary( "ovsplayer" ); } } |
附上布局文件是这样的。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" android:layout_width= "match_parent" android:layout_height= "match_parent" android:paddingleft= "@dimen/activity_horizontal_margin" android:paddingright= "@dimen/activity_horizontal_margin" android:paddingtop= "@dimen/activity_vertical_margin" android:paddingbottom= "@dimen/activity_vertical_margin" tools:context= ".mainactivity" > <textview android:id= "@+id/mytext" android:text= "@string/hello_world" android:layout_width= "wrap_content" android:layout_height= "wrap_content" /> </relativelayout> |
6、编写项目android.mk
android.mk放到$root_dir/app/build/intermediates/ndk/debug/下。
注意其中的绝对路径”/users/chengang/code/android/myapplication4″是咱们的$root_dir。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
local_path := $(call my-dir) include $(clear_vars) local_module := ovsplayer local_ldlibs := \ -lm \ -ljnigraphics \ -landroid \ -llog \ -lz \ local_shared_libraries := libavformat libavcodec libswscale libavutil local_src_files := \ /users/chengang/code/android/myapplication4/app/src/main/jni/ovsplayer.c \ local_c_includes += /users/chengang/code/android/myapplication4/app/src/main/jni local_c_includes += /users/chengang/code/android/myapplication4/app/src/debug/jni include $(build_shared_library) $(call import -module,ffmpeg- 2.5 . 3 /android/arm) |
简单说下local_ldlibs和local_shared_libraries的区别,前者链接系统库,后者链接第三方库。
并不能换着用。
7、编译c代码
在终端下执行这个命令编译c代码。
/usr/local/bin/android-ndk-r10d/ndk-build ndk_project_path=null app_build_script=/users/chengang/code/android/myapplication4/app/build/intermediates/ndk/debug/android.mk app_platform=android-21 ndk_out=/users/chengang/code/android/myapplication4/app/build/intermediates/ndk/debug/obj ndk_libs_out=/users/chengang/code/android/myapplication4/app/build/intermediates/ndk/debug/lib app_abi=armeabi
8、运行项目
回到android studio中ctrl+r运行项目。
会看到手机上打印出ffmpeg库的版本号(我编译的这个是3673444)和视频文件的信息出来。
ffmpeg库已经可以调用了,然后继续写自己的逻辑就好了。
9、另外
另外说两个c代码中可能遇见的错误:
a>如果你通过标准库想得到文件大小,又写错了文件名。安卓上,在不存在的文件的句柄上执行fseek会报如下错误:”could not disable core file generation.”;
b>如果avformat_open_input()返回-1330794744了。那有两种可能,一是你忘记调用av_register_all()了,二是编译ffmpeg的时候没有编译进对应的demuxer或者指定了disable-everything。