Android ffmpeg编译
首先,被这部分烦的不行的可以去github里clone一些别人的成品轻松拿到适合Android的编译结果
说下自己的环境
win10 x64
VMware® Workstation 12 Pro
ubuntu 14.04 LTS
android-ndk-r10b
ffmpeg3.0.2
Step1
android-ndk-r10b的安装,超级简单,去官网按照自己的配置下载一个,然后放到你习惯的目录下(我直接放/目录下了)。最后export配置下就好了
export NDK_HOME=/android-ndk-r10b
export PATH=$NDK_HOME:$PATH
Step2
到ffmpeg的官网下载最新的源码包http://www.ffmpeg.org/。并解压到你习惯的位置(我这里依然解压在/根目录下)然后对他的配置文件稍作修改,原因是为了让编译出来的so库适合Android。
su gedit /ffmpeg-3.0.2/configure
将
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)'
改成
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
这里无脑改即可,不需要做其它修改。
Step3
这步卡死了枭雄无数,关键在于网上很多说明不是很详细;其实就是一个configure的参数(至于参数的意思自己–help查看哈),ffmpeg的参数比较多根据个人的不同要求可以有不同的配置,网上很多写成*.sh的文件都是可以用的,只不过不清楚里面几个变量的意思含义的话很容易报错卡这里。我也是在这里卡了好一会,感谢github上大神的详解,这里抄袭下:
1.指定临时目录
export TMPDIR=/home/djia/tmpdir
指定一个临时目录,可以是任何路径,但必须保证存在,ffmpeg编译要用;
2.指定NDK路径
NDK=/home/djia/android/android-ndk-r10
3.指定使用NDK Platform版本
SYSROOT=$NDK/platforms/android-16/arch-arm/
这里指定的ndk platform的路径,一定要选择比你的目标机器使用的版本低的,比如你的手机是android-15版本,那么就选择低于15的
4.指定编译工具链
TOOLCHAIN=/home/djia/android/android-ndk-r10/toolchains/arm-Linux-androideabi-4.9/prebuilt/linux-x86_64
5.指定编译后的安装目录
PREFIX=/root/workspace/ffmpeg_shared_compile/dxjia_ffmpeg_install
这个目录是ffmpeg编译后的so的输出目录,会有一个include和lib文件夹生成在这里,这也是我们之后要在android apk中使用的.
网上很多种.sh我这里也贴一个,顺便把它的github项目地址贴出来
https://github.com/dxjia/ffmpeg-compile-shared-library-for-android
#!/bin/bash
export TMPDIR=/home/djia/tmpdir
NDK=/home/djia/android/android-ndk-r10
SYSROOT=$NDK/platforms/android-16/arch-arm/
TOOLCHAIN=/home/djia/android/android-ndk-r10/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
CPU=arm
PREFIX=/root/workspace/ffmpeg_shared_compile/dxjia_ffmpeg_install/arm/
ADDI_CFLAGS="-marm"
function build_one
{
./configure \
--prefix=$PREFIX \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-doc \
--disable-symver \
--enable-small \
--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
}
build_one
Step4
给sh文件设置可执行权限并执行下sh文件,然后休息几分钟。。。幸运的话 就可以看到结果了。。。顺便提下上面有需要的目录都是需要你事先手动建好顺便把权限设置下。。。
ok,假设你一步步走到这里了。打开你sh文件里指定的放编译结果的目录, $PREFIX 目录下生成 include和lib两个文件夹,将lib文件夹中的 pkgconfig 目录和so的链接文件删除,只保留so文件,然后include 和lib两个目录拷贝出来这就是我们想要的了
华丽的分割下
最后把拿到的库文件测试下效果,写一个jni的程序调用下。
过程我就不写了,按照jni的流程走下。有几点说明下,一个Android.mk
ifeq ($(APP_ABI), x86)
LIB_NAME_PLUS := x86
else
LIB_NAME_PLUS := armeabi
endif
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= avcodec-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavcodec-57.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= avdevice-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavdevice-57.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= avfilter-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavfilter-6.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= avformat-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavformat-57.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES := prebuilt/$(LIB_NAME_PLUS)/libavutil-55.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES := prebuilt/$(LIB_NAME_PLUS)/libswresample-2.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES := prebuilt/$(LIB_NAME_PLUS)/libswscale-4.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libffmpegjni
ifeq ($(APP_ABI), x86)
TARGET_ARCH:=x86
TARGET_ARCH_ABI:=x86
else
LOCAL_ARM_MODE := arm
endif
LOCAL_SRC_FILES := com_hsb_ffmpeg_FFmpegNative.c
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
LOCAL_SHARED_LIBRARIES:= avcodec-prebuilt-$(LIB_NAME_PLUS) \
avdevice-prebuilt-$(LIB_NAME_PLUS) \
avfilter-prebuilt-$(LIB_NAME_PLUS) \
avformat-prebuilt-$(LIB_NAME_PLUS) \
avutil-prebuilt-$(LIB_NAME_PLUS) \
swresample-prebuilt-$(LIB_NAME_PLUS) \
swscale-prebuilt-$(LIB_NAME_PLUS)
LOCAL_C_INCLUDES += -L$(SYSROOT)/usr/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
ifeq ($(APP_ABI), x86)
LOCAL_CFLAGS := -DUSE_X86_CONFIG
else
LOCAL_CFLAGS := -DUSE_ARM_CONFIG
endif
include $(BUILD_SHARED_LIBRARY)
另外一个是c里的几行代码
JNIEXPORT jint JNICALL Java_com_hsb_ffmpeg_FFmpegNative_test_1h264
(JNIEnv* env, jobject thiz,jint codecID)
{
AVCodec* codec = NULL;
av_register_all();//该函数在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。只有调用了该函数,才能使用复用器,编码器等
codec = avcodec_find_decoder(codecID);//通过code ID查找一个已经注册的音视频编码器。H264的codecID是28,所以我们java那边传28下来如果检测到H264注册过了这边codec就不为空,返回0
if(codec != NULL)
{
return 0;
}else{
return -1;
}
}
最后一点,其实jni的过程也是浪费了些时间,大部分的错在于细节。。。大部分错误都是你漏了啥。。。如果你也报错了又没有思路那就把jni的调用过程在复习下。。。maybe 你会拍大腿的。。。。
last but not least,把走到这里我用到的打包发资源下(一个result是ubuntu那边编译的结果,一个FFmpeg是android的项目)
http://download.csdn.net/detail/xiaoru5127/9533849