在Ubuntu中用Android NDK编译FFmpeg

原文:http://www.cnblogs.com/scottwong/archive/2010/12/17/1909455.html

最近在做 Android 上的项目,我被恶心的一塌糊涂。本以为 Java 是 Android 上的一等公民,结果深入学习之后才发现,Java 在 Android 上 就是个做 UI 的,除此之外无论想干什都得用 C 语言去实现。Android 一个非常糟糕差劲的操作系统,甚至连 Windows Mobile 都不如。Android 能取得今天的市场占有率只是因为当年微软的 Window Phone 7 还在开发中,而 iOS 又只给 iPhone用,所以手机生产商没得选,只能被迫采用 Android 这个连 Linux 内核开发团队都不承认的 Linux 操作系统。而基于 Linux 内核就是 Android 唯一的优点了,正是因为如此我们才想办法能把那些 Linux 上的伟大开源项目移植到 Android 上以弥补 Android 的不足。

Android 的多媒体功能是如此之弱,限制是如此之多,逼着我只能想办法去把 FFmpeg 移植到 Android 上。 感谢 havlenapetr 给出的示例代码,感谢 ABitNo 整理的说明文档,没有他们的贡献,我不可能把 FFmpeg 成功移植到 Android 上。下面我将说明将 FFmpeg 移植到 Android 上的详细步骤,希望能对正在进行同样工作的朋友有所帮助。

一、下载必要软件

Oracle VM VirtualBox 3.2.12

Ubuntu Desktop Edition 10.10 32-bit

Android NDK r4b(需要翻墙访问)

Android NDK r5(需要翻墙访问)

FFmpeg 0.6.1

二、配置编译环境

  1. 在 VirtualBox 中创建一个 Ubuntu 虚拟机
  2. 在 Ubuntu 虚拟机中使用 sudo passwd root 命令启动 root 账户
  3. 用 root 账户登录进入 Ubuntu
  4. 将 android-ndk-r4b-linux-x86.zip 中的内容解压缩到 /root 目录下
  5. 将 android-sdk_r07-linux_x86.tgz 中的内容解压缩到 /root 目录下
  6. 将 ffmpeg-0.6.1.tar.bz2 中的内容解压缩到 /root/ffmpeg/jni 目录下

三、准备编译 FFmpeg

  1. 编写 mk 文件
    1. 在 /root/ffmpeg/jni 目录中创建一个 Android.mk 文件,内容如下

      include $(all-subdir-makefiles)
    2. 在 /root/ffmpeg/jni/ffmpeg-0.6.1 目录中创建一个 Android.mk 文件,内容如下

      LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_WHOLE_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscaleLOCAL_MODULE := ffmpeginclude $(BUILD_SHARED_LIBRARY)include $(call all-makefiles-under,$(LOCAL_PATH))
    3. 在 /root/ffmpeg/jni/ffmpeg-0.6.1 目录中创建一个 av.mk 文件,内容如下

      # LOCAL_PATH is one of libavutil, libavcodec, libavformat, or libswscale
      
      #include $(LOCAL_PATH)/../config-$(TARGET_ARCH).makinclude $(LOCAL_PATH)/../config.mak
      
      OBJS :=OBJS-yes :=MMX-OBJS-yes :=include $(LOCAL_PATH)/Makefile
      
      # collect objectsOBJS-$(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-signFFCFLAGS += -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))elseC_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))
    4. 在 /root/ffmpeg/jni/ffmpeg-0.6.1/libavcodec 目录中创建一个 Android.mk 文件,内容如下

      LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)include $(LOCAL_PATH)/../av.mkLOCAL_SRC_FILES := $(FFFILES)LOCAL_C_INCLUDES :=        /    $(LOCAL_PATH)        /    $(LOCAL_PATH)/..LOCAL_CFLAGS += $(FFCFLAGS)LOCAL_LDLIBS := -lzLOCAL_STATIC_LIBRARIES := $(FFLIBS)LOCAL_MODULE := $(FFNAME)include $(BUILD_STATIC_LIBRARY)
    5. 在 /root/ffmpeg/jni/ffmpeg-0.6.1/libavformat 目录中创建一个 Android.mk 文件,内容如下

      LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)include $(LOCAL_PATH)/../av.mkLOCAL_SRC_FILES := $(FFFILES)LOCAL_C_INCLUDES :=        /    $(LOCAL_PATH)        /    $(LOCAL_PATH)/..LOCAL_CFLAGS += $(FFCFLAGS)LOCAL_CFLAGS += -include "string.h" -Dipv6mr_interface=ipv6mr_ifindexLOCAL_LDLIBS := -lzLOCAL_STATIC_LIBRARIES := $(FFLIBS)LOCAL_MODULE := $(FFNAME)include $(BUILD_STATIC_LIBRARY)
    6. 在 libavfilter、libavutil、libpostproc 和 libswscale 目录中各创建一个 Android.mk 文件,内容如下

      LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)include $(LOCAL_PATH)/../av.mkLOCAL_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. 修改 libm.h 文件和 Makefile 文件
    1. 编辑 /root/ffmpeg/jni/ffmpeg-0.6.1/libavutil 目录中的 libm.h 文件,删除以下 static 方法

      #if !HAVE_LRINTstatic av_always_inline av_const long int lrint(double x){return rint(x);}#endif /* HAVE_LRINT */
      
      #if !HAVE_LRINTFstatic av_always_inline av_const long int lrintf(float x){return (int)(rint(x));}#endif /* HAVE_LRINTF */
      
      #if !HAVE_ROUNDstatic av_always_inline av_const double round(double x){return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);}#endif /* HAVE_ROUND */
      
      #if !HAVE_ROUNDFstatic av_always_inline av_const float roundf(float x){return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);}#endif /* HAVE_ROUNDF */
      
      #if !HAVE_TRUNCFstatic av_always_inline av_const float truncf(float x){return (x > 0) ? floor(x) : ceil(x);}#endif /* HAVE_TRUNCF */
    2. 编辑 libavcodec、libavfilter、libavformat、libavutil、libpostproc 和 libswscale 目录中的 Makefile 文件,删除

      include $(SUBDIR)../subdir.mak

      include $(SUBDIR)../config.mak
  3. 生成 config.h 文件
    1. 在 /root/ffmpeg/jni/ffmpeg-0.6.1 目录中创建一个 config.sh 文件,使用 Android NDK r4b 编译时内容如下

      PREBUILT=/root/android-ndk-r4b/build/prebuilt/linux-x86/arm-eabi-4.4.0PLATFORM=/root/android-ndk-r4b/build/platforms/android-8/arch-arm
      
      ./configure --target-os=linux /    --arch=arm /    --enable-version3 /    --enable-gpl /    --enable-nonfree /    --disable-stripping /    --disable-ffmpeg /    --disable-ffplay /    --disable-ffserver /    --disable-ffprobe /    --disable-encoders /    --disable-muxers /    --disable-devices /    --disable-protocols /    --enable-protocol=file /    --enable-avfilter /    --disable-network /    --disable-mpegaudio-hp /    --disable-avdevice /    --enable-cross-compile /    --cc=$PREBUILT/bin/arm-eabi-gcc /    --cross-prefix=$PREBUILT/bin/arm-eabi- /    --nm=$PREBUILT/bin/arm-eabi-nm /    --extra-cflags="-fPIC -DANDROID" /    --disable-asm /    --enable-neon /    --enable-armv5te /    --extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"

      使用 Android NDK r5 编译时内容如下

      #!/bin/bash
      
      PREBUILT=/root/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86PLATFORM=/root/android-ndk-r5/platforms/android-8/arch-arm
      
      ./configure --target-os=linux /    --arch=arm /    --enable-version3 /    --enable-gpl /    --enable-nonfree /    --disable-stripping /    --disable-ffmpeg /    --disable-ffplay /    --disable-ffserver /    --disable-ffprobe /    --disable-encoders /    --disable-muxers /    --disable-devices /    --disable-protocols /    --enable-protocol=file /    --enable-avfilter /    --disable-network /    --disable-mpegaudio-hp /    --disable-avdevice /    --enable-cross-compile /    --cc=$PREBUILT/bin/arm-eabi-gcc /    --cross-prefix=$PREBUILT/bin/arm-eabi- /    --nm=$PREBUILT/bin/arm-eabi-nm /    --extra-cflags="-fPIC -DANDROID" /    --disable-asm /    --enable-neon /    --enable-armv5te /    --extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"
    2. 打开终端,进入 /root/ffmpeg/jni/ffmpeg-0.6.1 目录,运行下面的命令

      chmod +x config.sh./config.sh

    3. 编辑 /root/ffmpeg/jni/ffmpeg-0.6.1 目录中的 config.h 文件,将

      #define restrict restrict

      修改为

      #define restrict

四、开始编译 FFmpeg

很多人在用 havlenapetr 的方法编译 FFmpeg 时只得到一个 1599 字节 1.6KB 大小的 libffmpeg.so 文件,无论是用 Android NDK r4b 编译还是用 Android NDK r5 编译结果都是如此,很让人抓狂。我也很郁闷,最后花时间研究了一下 NDK,终于发现了解决方法,而且 Android NDK r4b 和 Android NDK r5  的情况还是完全不同的,请继续往下读。

  1. 使用 Android NDK r4b 编译

    打开 android-ndk-r4b/build/toolchains/arm-eabi-4.4.0 目录中的 setup.mk 文件,你会发现 Google 在里面定义了一个用于编译动态库的 cmd-build-shared-library 函数。在cmd-build-shared-library 函数中 Google 使用了 PRIVATE_WHOLE_STATIC_LIBRARIES 函数。但是你在 android-ndk-r4b/build/core 目录中的 build-binary.mk 文件里却找不到 PRIVATE_WHOLE_STATIC_LIBRARIES 函数…… 外?WHY?终于搞清楚了,原来得不到正确的 libffmpeg.so 文件不是我的错,而是 Android NDK r4b 的 BUG!你妹啊!你大爷啊!坑爹呢这是!发布前不做测试吗!居然漏掉一个函数!!!(我敢说这是个 BUG 是因为 Google 在 Android NDK r5 中修复了这个 BUG)

    木办法,只好手动替 Google 修补这个 BUG。好在修改方法很简单,只需要照 build-binary.mk 文件里的 PRIVATE_STATIC_LIBRARIES 增加一个 PRIVATE_WHOLE_STATIC_LIBRARIES 就行了。具体方法见下图

    修改前的 build-binary.mk 文件 

    修改后的 build-binary.mk 文件 

    保存 build-binary.mk 文件之后,运行下面的命令编译

    /root/android-ndk-r4b/ndk-build NDK_PROJECT_PATH=/root/ffmpeg

    接着你会看到 warning 不停的出现在屏幕上,熬过这段心惊肉跳的时间之后,你会看到 libffmpeg.so 文件已经被编译生成了。


    看看 /root/ffmpeg/obj/local/armeabi 目录中的 libffmpeg.so 文件,文件大小是 12.2MB 


    再看看 /root/ffmpeg/libs/local/armeabi 目录中的 libffmpeg.so 文件,文件大小是 3.2MB

  2. 使用 Android NDK r5 编译

    打开 android-ndk-r5/build/core 目录中的 build-binary.mk 文件,发现 Google 这次没有忘记 PRIVATE_WHOLE_STATIC_LIBRARIES,但还最后编译得到的 libffmpeg.so 文件大小还是不正确。 这次的问题是,android-ndk-r5 默认是使用 arm-linux-androideabi-4.4.3 编译,而不是 arm-eabi-4.4.0。但 android-ndk-r5/toolchains/arm-linux-androideabi-4.4.3 目录中的 setup.mk 文件里定义的  cmd-build-shared-library 函数并没有将静态库文件链接在一起生成动态库文件。所以解决的办法就是在执行 ndk-build 时加上 NDK_TOOLCHAIN 参数,指定使用 arm-eabi-4.4.0 来编译。完整命令如下

    /root/android-ndk-r5/ndk-build NDK_PROJECT_PATH=/root/ffmpeg NDK_TOOLCHAIN=arm-eabi-4.4.0 NDK_PLATFORM=android-8

五、结语

关于如何编写在 Android 上运行的 FFmpeg 播放器,请看 havlenapetr/FFMpegtewilove/faplayerNicoRo 和 android / ffmpeg dynamic module, JNI simple wrapper(需要翻墙访问)

希望我的这篇随笔能对读到这里你有帮助。

 

 

时间: 2025-01-29 08:09:03

在Ubuntu中用Android NDK编译FFmpeg的相关文章

linux-在ubuntu下用ndk编译ffmpeg的问题

问题描述 在ubuntu下用ndk编译ffmpeg的问题 在ubuntu下用ndk编译ffmpeg的时候出现了这个错误: 这个是我的sh文件: make clean export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt export PLATFORM=$NDK/platforms/android-8/arch-arm export PREFIX=../ff-pure-onelib build_one(){ ./con

Mac OS下为Android Studio编译FFmpeg解码库的详细教程_Android

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文件中的这几行,目的是

Mac OS下为Android Studio编译FFmpeg解码库的详细教程

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文件中的这几

android ndk-Android NDK 编译64位so

问题描述 Android NDK 编译64位so RT 请问如何编译64位的so文件呢?请详细点,菜鸟并不懂,谢谢 解决方案 APP_ABI := arm64-v8a http://developer.android.com/ndk/guides/ndk-build.html

android ndk-Android NDK编译已有的C++实现的协议库文件--求助

问题描述 Android NDK编译已有的C++实现的协议库文件--求助 RT,,Android.mk已经写好,NDK也已经配置好.自己写的代码可以用JNI 调用本地方法,但是调用一个用C/C++ 实现的协议库的时候却没有NDK的编译信息,当然也没有产生预期的.so文件.有没有遇到同样问题的大神呢?求助 >>>>> 解决方案 android c++ ndk 的编译环境搭建 解决方案二: http://blog.csdn.net/yangchang999/article/det

Android NDK开发详细介绍_Android

Android之NDK开发  一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言.但这并不等同于"第三方应用只能使用Java".在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库,即在Android平台上,"Java+C"

Android NDK 开发教程_Android

Android NDK 是在SDK前面又加上了"原生"二字,即Native Development Kit,因此又被Google称为"NDK". 众所周知,Android程序运行在Dalvik虚拟机中,NDK允许用户使用类似C / C++之类的原生代码语言执行部分程序. NDK包括了: 从C / C++生成原生代码库所需要的工具和build files. 将一致的原生库嵌入可以在Android设备上部署的应用程序包文件(application packages fi

Android: NDK编程入门笔记

为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大. 2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C++代码编写的. 3. 便于移植,用C/C++写得库可以方便在其他的嵌入式平台上再次使用. 下面就介绍下Android NDK的入门学习过程: 入门的最好办法就是学习Android自带的例子, 这里就通过学习Android的NDK自带的demo程序:hello-jni来达到这个目的. 一

Android NDK 开发教程

Android NDK 是在SDK前面又加上了"原生"二字,即Native Development Kit,因此又被Google称为"NDK". 众所周知,Android程序运行在Dalvik虚拟机中,NDK允许用户使用类似C / C++之类的原生代码语言执行部分程序. NDK包括了: 从C / C++生成原生代码库所需要的工具和build files. 将一致的原生库嵌入可以在Android设备上部署的应用程序包文件(application packages fi