原:https://righere.github.io/2016/10/10/build-ffmpeg4Android/
本文主要实现了FFmpeg的编译和移植,首先在linux下将官网下载的源码编译成.so文件,然后使用android-studio配合NDK工具,将.so文件移植到android项目当中,简单地介绍了如何一步步完成FFmpeg的编译流程
参考文章:手把手图文并茂教你用Android Studio编译FFmpeg库并移植
下面是我自己在ubuntu下编译
Git,NDK
安装git,检查本地git,git --version
直接用命令符安装: sudo apt install git
也可以去官网下载,git官网下载
如果已经有了git,想更新到最新版,可以输入 git clone https://github.com/git/git,再进行编译安装
然后下载NDK(现在已经有13的版本了),推荐使用android studio安装ndk,下载的ndk路径默认在AndroidSDK的ndk-boudle文件中
使用terminal配置电脑的环境,(个人电脑,我直接以管理员权限配置的系统环境变量)
sudo gedit /etc/profile
打开之后,把我们的ndk路径配置进来,
1 2 3 #Android NDK export ANDROID_NDK=/path/to/ndk(你的ndk解压路径) export PATH=$PATH:$ANDROID_NDK
新建一个ffempeg的工作文件夹,例如 mkdir workplace
git clone https://git.ffmpeg.org/ffmpeg.git
也可以直接去官网下载:ffmpeg官网下载
为了保证我们编译出的文件是以.so的后缀名 将文本中的3209-3212这4行:
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)'
写脚本的直观上的好处就是省去了一步步的执行编译命令,通常的在编译之前都需要进行配置,设置相应的环境变量,比如指定编译工具、编译平台等等,查看所有的配置选项可以在ffmpeg目录执行如下命令:
./configure –help
当然此脚本中,我们只需要设置几个我们比较关心的配置。
1. NDK的路径: NDK=/path/to/ndk 2. 编译的ndk plaform版本: SYSROOT=$NDK/platforms/android-23/arch-arm/ 3. 交叉编译工具: TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 4. CPU平台: CPU=arm 5. 编译完成后安装目录(当前目录下的android文件夹): PREFIX=./android/$CPU确定我们需要的配置之后,在ffmpeg的目录下面新建一个脚本 build4android.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/sh NDK=/path/to/ndk SYSROOT=$NDK/platforms/android-23/arch-arm/ TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 CPU=arm PREFIX=./android/$CPU ADDI_CFLAGS="-marm" config_para() { ./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 } config_para这里增加一个方便的脚本技巧,便于修改自己的配置,我们将这些configure配置参数写进一个脚本function,建议如下语法:
1 2 3 4 5 func_name() { ./configure \ <---我们的参数配置---/> }
另外一种一种语法(这种可能会报找不到function的错误,不推荐使用):
1 2 3 4 5 function func_name { ./configure \ <---我们的参数配置---/> }
还有就是脚本编辑推荐使用vim非常方便,变量参数一目了然
进入我们的FFmpeg的源码目录,执行刚才的脚本: (先增加执行权限 sudo chmod +x build4android.sh,以防权限不够)
./build4anroid.sh
脚本执行中:
脚本执行完成:
生成的android/arm/目录中的文件,图中标出的为链接文件可以删除:
在android-studio中切换成project工程目录模式,在app/src/main目录下面新建一个jni文件夹,将上面第6步中的include文件夹拷贝到此文件夹中,再在jni文件夹中新建一个lib文件夹,将.so文件拷贝进来,新建Android.mk,Application.mk和c文件目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ├── Android.mk ├── Application.mk ├── ffmpeg-jni.c ├── include │ ├── libavcodec │ ├── libavfilter │ ├── libavformat │ ├── libavutil │ ├── libswresample │ └── libswscale └── lib ├── libavcodec-57.so ├── libavfilter-6.so ├── libavformat-57.so ├── libavutil-55.so ├── libswresample-2.so └── libswscale-4.so在上面编写的Android.mk中,我们制定了c文件的名字为ffmpeg-jni.c,所以c文件的与此保持一致,
函数申明语法:JNIEXPORT jstring Java_包名activity名函数名,包名中间的点号.全部变成下划线_
例如:我在包名com.righere.ffmpegndkbuild下面的MainActivity中要使用avcodecInfo这个函数,
JNIEXPORT jstring Java_com_righere_ffmpegndkbuild_MainActivity_avcodecInfo
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 #include <stdio.h> #include "libavformat/avformat.h" #include <libavfilter/avfilter.h> #include <jni.h> JNIEXPORT jstring Java_com_righere_ffmpegndkbuild_MainActivity_avcodecInfo(JNIEnv* env, jobject obj) { char info[4000] = { 0 }; int count = 100; //输出前100个codec名字 av_register_all();//初始化所有decoder和encoder,注册所有容器类型和codec AVCodec *c_temp = av_codec_next(NULL); while (c_temp != NULL && count > 0){ //输出解码器和编码器 if(c_temp->decode != NULL){ sprintf(info,"%s[Dec]",info); } else{ sprintf(info,"%s[Enc]",info); } sprintf(info,"%s[s]\n",info,c_temp->name); c_temp = c_temp->next; count--; } return (*env)->NewStringUTF(env, info); }这里我编译的armeabi版本的生成的.so文件自动会放在了’src/main/libs/armeabi’这个目录下面,其他的版本只需要把armeabi名字变一下就行了
1 2 3 4 sourceSets.main { jni.srcDirs = [] //disable automatic ndk-build jniLibs.srcDirs = ['src/main/libs/armeabi'] }告诉gradle,ndk-build的时候还要执行哪些附加选项(在defaultConfig{}里面添加):
这一步其实可以看做是对.mk文件的补充
1 2 3 4 5 6 ndk { abiFilter "armeabi" //在Application.mk里面设置保持一致,如果是编译了多个平台的,可以指定编译多平台的或者 moduleName "FFmpegCodec" //jni模块的名字,与android.mk文件保持一致 ldLibs "log", "z", "m", "jnigraphics", "android" //这里可以设置ldLibs选项,比如你想调试jni的时候必须添加"log"参数 }编译完成main文件夹下会增加libs和obj两个文件夹,其中libs/armeabi下就是我们需要的.so文件
1 2 3 4 5 6 7 8 9 10 11 ├── jni │ ├── Android.mk │ ├── Application.mk │ ├── ffmpeg-jni.c │ ├── include │ └── lib ├── libs │ └── armeabi ├── obj │ └── local └── res
切换回Android结构的工程试图,系统会自动把生成的.so文件放入jniLibs中,看到这个FFmpeg的移植基本就成功了
在MainActivity中添加测试代码,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class MainActivity extends AppCompatActivity { //jni public native String avcodecInfo(); static { System.loadLibrary("FFmpegCodec"); System.loadLibrary("avcodec-57"); System.loadLibrary("avfilter-6"); System.loadLibrary("avformat-57"); System.loadLibrary("avutil-55"); System.loadLibrary("swresample-2"); System.loadLibrary("swscale-4"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView Codec_info = (TextView) findViewById(R.id.TextView_codec_info); Codec_info.setMovementMethod(ScrollingMovementMethod.getInstance()); Codec_info.setText(avcodecInfo()); } }