0. 版本确定:
最近需要以NativeC方式编译OpenCV2.0. 其中用到ffmpeg. (OpenCV 目录树下,有interfaces/ffopencv). 但ffmpeg版本一直在升级,在不同的版本中,接口也有所变化,如何判断OpenCV2.0使用的哪个版本的ffmpeg是个关键。
首先观察interfaces/ffopencv/ffopencv.cpp. 其中包含:
#ifdef WIN32
#include //ffmpeg_/avformat.h
#include //ffmpeg_/avcodec.h
#include //ffmpeg_/imgconvert.h
#else
说明Windows编译时使用这几个ffmpeg头文件。
打开:3rdparty/include/ffmpeg_/avcodec.h
有如下代码:
#define LIBAVCODEC_VERSION_MAJOR 52
#define LIBAVCODEC_VERSION_MINOR 20
#define LIBAVCODEC_VERSION_MICRO 0
所以,确认应该找对应的ffmpeg 版本。
在ffmpeg官网(http://ffmpeg.org/) 分别下载了:
ffmpeg-2.2.4.tar.gz(当前最新版本)
ffmpeg-1.0.9.tar.gz,ffmpeg-0.6.7.tar.gz,ffmpeg-0.5.13.tar.gz
分别打开其avcodec.h,结果0.5.13最为接近:
#define LIBAVCODEC_VERSION_MAJOR 52
#define LIBAVCODEC_VERSION_MINOR 20
#define LIBAVCODEC_VERSION_MICRO 1
所以说明:OpenCV2.0所使用的FFMpeg库是0.5.13.
1. 开始编译ffMpeg-0.5.13:
以下编译方法,是借鉴网络上已有的ffmpeg NDK编译之方法。只是针对各个特定ffmpeg版本做了一点微调。在此感谢分享这些编译经历的朋友。
1.1:目录建立:
创建jni目录。将ffmpeg-0.5.13.tar.gz copy到其中并解压缩。
最终目录结构如下:
jni/ffmpeg-0.5.13
1.2: 建立config_android.sh并生成config.h文件:
在编译X86 Linux环境下ffmpeg, 是通过configure来创建config.h 和config.mk.
决定编译哪些模块,以及程序中用到的一些宏定义(关系到程序向哪方向走)。
我们也需要一个config.h. 所以要利用configure来生成它。
在jni/ffmpeg-0.5.13/目录内,创建config_android.sh
内容如下:
NDK_ROOT=/opt/android-ndk-r9d
PREBUILT=${NDK_ROOT}/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
PLATFORM=${NDK_ROOT}/platforms/android-8/arch-arm
./configure --target-os=linux \
--arch=arm \
--enable-small \
--enable-static \
--disable-yasm \
--enable-armv5te \
--enable-cross-compile \
--disable-stripping \
--disable-ffplay \
--disable-ffserver \
--disable-devices \
--disable-devices \
--enable-swscale \
--cc=$PREBUILT/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-linux-androideabi- \
--nm=$PREBUILT/bin/arm-linux-androideabi-nm \
--extra-cflags="-fPIC -DANDROID" \
--extra-ldflags='-L$PLATFORM/usr/lib -nostdlib' \
echo "#undef restrict" >> config.h
echo "#define restrict __restrict__" >> config.h
echo "#undef HAVE_LRINT" >> config.h
echo "#define HAVE_LRINT 1" >> config.h
echo "#undef HAVE_LRINTF" >> config.h
echo "#define HAVE_LRINTF 1" >> config.h
echo "#undef HAVE_ROUND" >> config.h
echo "#define HAVE_ROUND 1" >> config.h
echo "#undef HAVE_ROUNDF" >> config.h
echo "#define HAVE_ROUNDF 1" >> config.h
echo "#undef HAVE_TRUNCF" >> config.h
echo "#define HAVE_TRUNCF 1" >> config.h
注意:NDK_ROOT,PREBUILT 要按照本机实际地址配置。
之后修改其权限:
chmod 777 config_android.sh
运行:
./config_android.sh
生成了config.h
1.3: 微调config.h和几个文件:
1.3.1: config.h微调:
在config.h中,找到:
#define restrict restrict
#define restrict __restrict__
修改为:
#define restrict
#define restrict
因为restrict关键字是C99才引入的。
1.3.2:libavutil/internal.h微调:
#if 0
static av_always_inline av_const long long llrint(double x)
{
return rint(x);
}
#endif
如果不去掉,llrint会重定义。(llrint是C函数)
1.3.3:libavcodec/utils.c微调:
fd = open(*filename, O_RDWR | O_BINARY | O_CREAT, 0444);
修改为:
fd = open(*filename, O_RDWR | O_CREAT, 0444);
1.3.4: libavformat/rtsp.c微调:
增加:
#include // sys/time.h
#include // sys/types.h
#include // unistd.h
1.4: 修改Makefile:
分别修改libavcodec、libavfilter、libavformat、libavutil、libpostproc和libswscale下的Makefile,把每个Makefile中的下面两句删除或注释掉
include $(SUBDIR)../config.mak
include $(SRC_PATH)/subdir.mak
1.5:增加av.mk文件:
在jni/ffmpeg-0.5.13中增加av.mk文件,内容如下:
# LOCAL_PATH is one of libavutil, libavcodec, libavformat, or libswscale
#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)/*.S)
ALL_S_FILES := $(addprefix $(TARGET_ARCH)/, $(notdir $(ALL_S_FILES)))
ifneq ($(ALL_S_FILES),)
ALL_S_OBJS := $(patsubst %.S,%.o,$(ALL_S_FILES))
C_OBJS := $(filter-out $(ALL_S_OBJS),$(OBJS))
S_OBJS := $(filter $(ALL_S_OBJS),$(OBJS))
else
C_OBJS := $(OBJS)
S_OBJS :=
endif
C_FILES := $(patsubst %.o,%.c,$(C_OBJS))
S_FILES := $(patsubst %.o,%.S,$(S_OBJS))
FFFILES := $(sort $(S_FILES)) $(sort $(C_FILES))
1.6:在目标目录内添加Android.mk , Application.mk
1.6.1: 先添加Android.mk
jni/Android.mk 内容如下:
include $(all-subdir-makefiles)
jni/ffmpeg-0.5.13/Android.mk 内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libpostproc libswscale
LOCAL_MODULE := ffmpeg
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
注意:为了编译一个测试程序:ffmpeg. 可以将这个Android.mk修改为如下版本:
LOCAL_PATH := $(call my-dir)
L_PATH := $(PWD)
include $(CLEAR_VARS)
LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libpostproc libswscale
LOCAL_MODULE := ffmpeg
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
LOCAL_PATH := $(L_PATH)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_CXXFLAGS := -D_GLIBCXX_USE_WCHAR_T -I../ -I./
LOCAL_MODULE := ffmpeg_g
LOCAL_SRC_FILES := ffmpeg.c cmdutils.c
LOCAL_LDLIBS := -llog -L../../obj/local/armeabi-v7a/ -lavformat -lavcodec -lavutil -lswscale -lpostproc -lffmpeg
include $(BUILD_EXECUTABLE)
jni/ffmpeg-0.5.13/libavcodec/Android.mk内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
ARCH=arm
ARCH_ARM=yes
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS) -Wa,-mimplicit-it=thumb
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
请注意:一定要添加:-Wa, -mimplicit-it=thumb
否则会报错:
/tmp/ccYZnT0I.s: Assembler messages:
/tmp/ccYZnT0I.s:3268: Error: thumb conditional instruction should be in IT block -- `movgt fp,r3'
/tmp/ccYZnT0I.s:3269: Error: thumb conditional instruction should be in IT block -- `movgt r3,sl'
/tmp/ccYZnT0I.s:3271: Error: thumb conditional instruction should be in IT block -- `movle r3,lr'
/tmp/ccYZnT0I.s:3273: Error: thumb conditional instruction should be in IT block -- `movgt fp,r3'
jni/ffmpeg-0.5.13/libavformat/Android.mk 内容如下:
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_CFLAGS += -include "string.h" -Dipv6mr_interface=ipv6mr_ifindex
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
libavfilter、libavutil、libpostproc和libswscale下的Android.mk内容如下:
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_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
1.6.2: 添加Application.mk
所有对应的Application.mk可以自定义,Sam常用的是:
# Build both ARMv5TE and ARMv7-A machine code.
APP_PLATFORM = android-8
APP_ABI := armeabi-v7a
#APP_ABI := $(ARM_ARCH)
#Sam modify it to release
APP_OPTIM := release
#APP_OPTIM := debug
#APP_OPTIM = $(MY_OPTIM)
APP_CPPFLAGS += -fexceptions
APP_CPPFLAGS += -frtti
#sam modify it from gnustl_static to gnustl_shared
#APP_STL := gnustl_static
#APP_STL := gnustl_shared
APP_STL := gnustl_shared
#APP_CPPFLAGS += -fno-rtti
#
APP_CPPFLAGS += -Dlinux -fsigned-char
APP_CFLAGS += -fsigned-char
#APP_CPPFLAGS += $(MY_CPPFLAGS) -Dlinux
#STLPORT_FORCE_REBUILD := true
1.7: 编译:
在jni目录:
ndk-build -B V=1
编译结果可以在jni同级目录obj下看到编译出的结果:
obj/local/armeabi-v7a
1.8: 编译ffmpeg 应用程序:
如果上面在jni/ffmpeg-0.5.13目录下的Android.mk用的是第二项。则可以生成一个ffmpeg测试程序。
则在jni/ffmpeg-0.5.13/下,
运行:ndk-build -B V=1
则在jni/ffmpeg-0.5.13/下生成ffmpeg应用程序。
中间会需要version.h
可以创建之:内容如下:
#define FFMPEG_VERSION "0.5.13"
1.9: 测试:
将一段视频源转换为DVD格式:
./ffmpeg -i command_6.wmv -target pal-dvd -ps 2000000000 -aspect 16:9 a.mpeg
转换后一切正常,可以播放之。证明此版本ffmpeg交叉编译通过。
因为之前在寻找对应OpenCV2.0的ffmpeg时,下载了多份FFMpeg。
包括:
ffmpeg-2.2.4.tar.gz(当前最新版本)
ffmpeg-1.0.9.tar.gz,ffmpeg-0.6.7.tar.gz,ffmpeg-0.5.13.tar.gz
所以,干脆把他们都给编译了吧。
2. 编译ffmpeg-0.6.7:
2.1:目录建立:
创建jni目录。将ffmpeg-0.5.13.tar.gz copy到其中并解压缩。
最终目录结构如下:
jni/ffmpeg-0.6.7
2.2: 建立config_android.sh并生成config.h文件:
在jni/ffmpeg-0.6.7/目录内,创建config_android.sh
内容如下:
NDK_ROOT=/opt/android-ndk-r9d
PREBUILT=${NDK_ROOT}/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
PLATFORM=${NDK_ROOT}/platforms/android-8/arch-arm
./configure --target-os=linux \
--arch=arm \
--enable-small \
--enable-static \
--disable-yasm \
--enable-armv5te \
--enable-cross-compile \
--disable-stripping \
--disable-ffplay \
--disable-devices \
--disable-devices \
--enable-swscale \
--disable-asm \
--cc=$PREBUILT/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-linux-androideabi- \
--nm=$PREBUILT/bin/arm-linux-androideabi-nm \
--extra-cflags="-fPIC -DANDROID" \
--extra-ldflags='-L$PLATFORM/usr/lib -nostdlib' \
echo "#undef restrict" >> config.h
echo "#define restrict __restrict__" >> config.h
echo "#undef HAVE_LRINT" >> config.h
echo "#define HAVE_LRINT 1" >> config.h
echo "#undef HAVE_LRINTF" >> config.h
echo "#define HAVE_LRINTF 1" >> config.h
echo "#undef HAVE_ROUND" >> config.h
echo "#define HAVE_ROUND 1" >> config.h
echo "#undef HAVE_ROUNDF" >> config.h
echo "#define HAVE_ROUNDF 1" >> config.h
echo "#undef HAVE_TRUNCF" >> config.h
echo "#define HAVE_TRUNCF 1" >> config.h
2.3: 微调config.h和几个文件:
2.3.1: config.h微调:
在config.h中,找到:
#define restrict restrict
#define restrict __restrict__
修改为:
#define restrict
#define restrict
2.3.2:libavcodec/utils.c微调:
fd = open(*filename, O_RDWR | O_BINARY | O_CREAT, 0444);
修改为:
fd = open(*filename, O_RDWR | O_CREAT, 0444);
2.4: 修改Makefile:
分别修改libavcodec、libavfilter、libavformat、libavutil、libpostproc和libswscale下的Makefile,把每个Makefile中的下面两句删除或注释掉
include $(SUBDIR)../config.mak
include $(SRC_PATH)/subdir.mak
2.5:增加av.mk文件:
在jni/ffmpeg-0.5.13中增加av.mk文件,内容如下:
# LOCAL_PATH is one of libavutil, libavcodec, libavformat, or libswscale
#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)/*.S)
ALL_S_FILES := $(addprefix $(TARGET_ARCH)/, $(notdir $(ALL_S_FILES)))
ifneq ($(ALL_S_FILES),)
ALL_S_OBJS := $(patsubst %.S,%.o,$(ALL_S_FILES))
C_OBJS := $(filter-out $(ALL_S_OBJS),$(OBJS))
S_OBJS := $(filter $(ALL_S_OBJS),$(OBJS))
else
C_OBJS := $(OBJS)
S_OBJS :=
endif
C_FILES := $(patsubst %.o,%.c,$(C_OBJS))
S_FILES := $(patsubst %.o,%.S,$(S_OBJS))
FFFILES := $(sort $(S_FILES)) $(sort $(C_FILES))
2.6:在目标目录内添加Android.mk , Application.mk
2.6.1: 先添加Android.mk
jni/Android.mk 内容如下:
include $(all-subdir-makefiles)
jni/ffmpeg-0.6.7/Android.mk 内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libpostproc libswscale
LOCAL_MODULE := ffmpeg
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
注意:为了编译一个测试程序:ffmpeg. 可以将这个Android.mk修改为如下版本:
LOCAL_PATH := $(call my-dir)
L_PATH := $(PWD)
include $(CLEAR_VARS)
LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libpostproc libswscale
LOCAL_MODULE := ffmpeg
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
LOCAL_PATH := $(L_PATH)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_CXXFLAGS := -D_GLIBCXX_USE_WCHAR_T -I../ -I./
LOCAL_MODULE := ffmpeg_g
LOCAL_SRC_FILES := ffmpeg.c cmdutils.c
LOCAL_LDLIBS := -llog -L../../obj/local/armeabi-v7a/ -lavformat -lavcodec -lavutil -lswscale -lpostproc -lffmpeg
include $(BUILD_EXECUTABLE)
jni/ffmpeg-0.6.7/libavcodec/Android.mk内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
ARCH=arm
ARCH_ARM=yes
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
jni/ffmpeg-0.6.7/libavformat/Android.mk 内容如下:
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_CFLAGS += -include "string.h" -Dipv6mr_interface=ipv6mr_ifindex
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
libavfilter、libavutil、libpostproc和libswscale下的Android.mk内容如下:
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_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)
2.6.2: 添加Application.mk
所有对应的Application.mk可以自定义,Sam常用的是:
# Build both ARMv5TE and ARMv7-A machine code.
APP_PLATFORM = android-8
APP_ABI := armeabi-v7a
#APP_ABI := $(ARM_ARCH)
#Sam modify it to release
APP_OPTIM := release
#APP_OPTIM := debug
#APP_OPTIM = $(MY_OPTIM)
APP_CPPFLAGS += -fexceptions
APP_CPPFLAGS += -frtti
#sam modify it from gnustl_static to gnustl_shared
#APP_STL := gnustl_static
#APP_STL := gnustl_shared
APP_STL := gnustl_shared
#APP_CPPFLAGS += -fno-rtti
#
APP_CPPFLAGS += -Dlinux -fsigned-char
APP_CFLAGS += -fsigned-char
#APP_CPPFLAGS += $(MY_CPPFLAGS) -Dlinux
#STLPORT_FORCE_REBUILD := true
2.7: 编译:
在jni目录:
ndk-build -B V=1
cmdutils.c 中有几处要屏蔽.
编译结果可以在jni同级目录obj下看到编译出的结果:
obj/local/armeabi-v7a
2.8: 编译ffmpeg 应用程序:
如果上面在jni/ffmpeg-0.5.13目录下的Android.mk用的是第二项。则可以生成一个ffmpeg测试程序。
则在jni/ffmpeg-0.6.7/下,
运行:ndk-build -B V=1
则在jni/ffmpeg-0.6.7/下生成ffmpeg应用程序。
中间会需要version.h
可以创建之:内容如下:
#define FFMPEG_VERSION "0.6.7"
2.9: 测试:
将一段视频源转换为DVD格式:
./ffmpeg -i command_6.wmv -target pal-dvd -ps 2000000000 -aspect 16:9 a.mpeg
转换后一切正常,可以播放之。证明此版本ffmpeg交叉编译通过。
其它版本大同小异,就不再继续抄录了.
记录:
OpenCV-2.4.10的FFMPEG版本是:ffmpeg-2.0.7
#define LIBAVCODEC_VERSION_MAJOR 55
#define LIBAVCODEC_VERSION_MINOR 18
#define LIBAVCODEC_VERSION_MICRO 102
附录:
出错信息1:
/opt/android-ndk-r9/build/core/build-binary.mk:109: *** target file `clean' has both : and :: entries. Stop.
通常是在Makefile中,有clean段,请将clean 段注释掉。
libavfilter/Makefile 中就有。
出错信息2:
jni/ffmpeg-2.0.7/libavcodec/../libavutil/libm.h:52:32: error: static declaration of 'cbrt' follows non-static declaration
去掉libm.h中的static