最近项目中要用到FFmpeg,前期的环境搭建用了很长时间,正所谓万事开头难,又一次深刻的领悟到了这个道理~
写篇文章,再次将自己的思路梳理一下,希望也能帮助到有需要的人~
本文参考:
https://juejin.im/entry/5af8384a6fb9a07ac23ad590#comment
https://www.jianshu.com/p/dfe9404e001a
http://www.ihubin.com/blog/android-ffmpeg-demo-3/
前提:本文演示的是将f'fmpeg编译成一个SO库
一、系统环境(与本文环境不同很有可能会编译失败)
- Mac OS High Sierra 10.13.6
- NDK-r14b(64bit)
- FFmpeg 3.3
二、下载NDK、ffmpeg
1.下载ndk(国内网站),也可以直接去官网,我下载的是android-ndk-r14b-darwin-x86_64.zip;
2.配置ndk环境
- 启动终端,进入当前用户的home目录下
- 创建.bash_profile文件(如果之前已经创建过该文件,则直接打开):touch .bash_profile
- 打开.bash_profile文件:open -e .bash.profile
进入到编辑文件的页面,配置如下环境 :
上图中NDK_ROO为你下载的ndk所在位置。
- 保存文件,关闭
- 更新刚才的配置:source .bash_profile
- 查看刚才的配置是否成功:
在终端输入$PATH,并按enter键,查看结果:
配置成功!
3.下载并编译ffmpeg
- 在终端中进入某一目录下,例如Downloads下,输入:git clone https://git.ffmpeg.org/ffmpeg.git
- 下载完成后,进入到ffmpeg目录下:cd ffmpeg
- 切换到3.3分支: git checkout -b 3.3 remotes/origin/release/3.3
- 在ffmpeg目录下找到configure文件,用sublime(或者其他工具)打开,将其中的:
修改成:
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文件夹下,创建一个ffmpegtemp文件夹和一个ffmpeg_build.sh文件,对于fmpeg_build.sh我是将version.sh文件复制重新命名得到的,具体内容如下:
#!/bin/bash
export TMPDIR=/Users/UserName/Downloads/ffmpeg/ffmpegtemp #这句很重要,不然会报错 unable to create temporary file in
# NDK的路径,根据自己的安装位置进行设置
NDK=/Users/UserName/Downloads/android-ndk-r14b
# 编译针对的平台,可以根据自己的需求进行设置
# 这里选择最低支持android-14, arm架构,生成的so库是放在
# libs/armeabi文件夹下的,若针对x86架构,要选择arch-x86
PLATFORM=$NDK/platforms/android-21/arch-arm
# 工具链的路径,根据编译的平台不同而不同
# arm-linux-androideabi-4.9与上面设置的PLATFORM对应,4.9为工具的版本号,
# 根据自己安装的NDK版本来确定,一般使用最新的版本
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build_one
{
./configure \
--prefix=$PREFIX \
--target-os=linux \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include" \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--disable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--disable-debug \
--enable-static \
--disable-doc \
--disable-asm \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping \
$ADDITIONAL_CONFIGURE_FLAG
sed -i '' 's/HAVE_LRINT 0/HAVE_LRINT 1/g' config.h
sed -i '' 's/HAVE_LRINTF 0/HAVE_LRINTF 1/g' config.h
sed -i '' 's/HAVE_ROUND 0/HAVE_ROUND 1/g' config.h
sed -i '' 's/HAVE_ROUNDF 0/HAVE_ROUNDF 1/g' config.h
sed -i '' 's/HAVE_TRUNC 0/HAVE_TRUNC 1/g' config.h
sed -i '' 's/HAVE_TRUNCF 0/HAVE_TRUNCF 1/g' config.h
sed -i '' 's/HAVE_CBRT 0/HAVE_CBRT 1/g' config.h
sed -i '' 's/HAVE_RINT 0/HAVE_RINT 1/g' config.h
make clean
make -j4
make install
$TOOLCHAIN/bin/arm-linux-androideabi-ld \
-rpath-link=$PLATFORM/usr/lib \
-L$PLATFORM/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$PREFIX/libffmpeg.so \
libavcodec/libavcodec.a \
libavfilter/libavfilter.a \
libswresample/libswresample.a \
libavformat/libavformat.a \
libavutil/libavutil.a \
libswscale/libswscale.a \
-lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
$TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a
}
# arm v7vfp
CPU=armv7-a
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
PREFIX=./android/$CPU-vfp
ADDITIONAL_CONFIGURE_FLAG=
build_one
这里一定要注意修改NDK的路径,下载的ndk版本,以及对应的系统架构(是X86,还是arm)
最后一个位置 ,我选择的是 CPU 类型是 armv7-a(想进一步了解的点这里)
脚本准备好了,下面开始编译:
- 打开终端切换到ffmpeg目录下,执行:chmod +x ffmpeg_build.sh
- 然后:./ffmpeg_build.sh
此刻耐心等待一下,等等过程中出现下图,证明正在编译~
编译完成后,ffmpeg中会出现一个android的文件夹:
include文件夹中是一些要引用的头文件,红色箭头所指,就是得到的so文件。
三、在Android项目中引入ffmpeg的so库
- 打开一个android项目,新建在main下新建一个jni文件夹,并将include文件夹和libffmpeg.so文件复制到jni目录下:
然后将ffmpeg文件下下的ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到jni目录下,这里要注意的是,需要对ffmpeg.c和ffmpeg.h两个文件进行修改,(参考http://www.ihubin.com/blog/android-ffmpeg-demo-4/ ,此文中的step3)
准备工作基本完成,下面开始调用libffmpeg.so
四、搭建开发流程
1.新建一个FFmpegRun.java,用来调用ffmpeg的类:
public class FFmpegRun {
public native static int run(String[] commands);
}
2.获取c语言借口的函数声明
点击android studio下面的terminal窗口,切换到项目下的java目录下,执行下面的命定(根据自己的文件名,适当修改)
javah com.example.ffmpegdemo.FFmpegRun
3. 执行完成以后,刷新一下,会在src/main/java目录下生成一个com_example_ffmpegdemo_FFmpegRun.h的文件,
把这个文件移动到jni目录下。
4. 在jni目录下手动创建三个文件,分别是Android.mk,Application.mk,com_example_ffmpegdemo_FFmpegRun.c
Android.mk文件内容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeginvoke
LOCAL_SRC_FILES := com_tangyx_video_ffmpeg_FFmpegRun.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
LOCAL_C_INCLUDES := /Users/UserName/Downloads/ffmpeg #这里修改成你的ffmpeg文件夹路径
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)
LOCAL_C_INCLUDES的路径记得修改为你当前ffmepg文件夹的路径。
LOCAL_SRC_FILES第一个c文件的引用记得改为你当前jni下生成的c文件。
Application.mk的内容:
APP_ABI := armeabi-v7a
APP_BUILD_SCRIPT := Android.mk
APP_PLATFORM := android-15
这里要注意:APP_ABI要和包含前面所设置的cpu类型。
com_example_ffmpegdemo_FFmpegRun.c文件的内容:
#include "com_example_ffmpegdemo_FFmpegRun.h"
#include "ffmpeg.h"
#include <string.h>
/*
* Method: run
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_example_ffmpegdemo_FFmpegRun_run(JNIEnv *env,
jclass obj, jobjectArray commands) {
int argc = (*env)->GetArrayLength(env, commands);
char *argv[argc];
int i;
for (i = 0; i < argc; i++) {
jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
}
return run(argc, argv);
}
第一行的include引用你当前jni下生成的.h文件.
5.开始编译。
打开terminal进入到jni目录下,执行命令:ndk-build
等待编译完成:
编译成功,刷新main项目,会多出2个文件夹,分别是lib和obj
lib下的生成了对应框架下需要用到的so文件,把armeabi和armeabi-v7a两个文件夹考到jniLib或者main下面自己创建的libs文件夹下
我这里是自己创建的lib文件夹,所以在app module下build.gradle文件中新
sourceSets {
main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
}
到这里,基本把ffmpeg集成到了android项目中,下面就是根据业务场景,去使用ffmpeg啦!