在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 libswscale
      LOCAL_MODULE := ffmpeg
      include $(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).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))
    4. 在 /root/ffmpeg/jni/ffmpeg-0.6.1/libavcodec 目录中创建一个 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_LDLIBS := -lz
      LOCAL_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.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)
    6. 在 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. 修改 libm.h 文件和 Makefile 文件
    1. 编辑 /root/ffmpeg/jni/ffmpeg-0.6.1/libavutil 目录中的 libm.h 文件,删除以下 static 方法
      #if
       !HAVE_LRINT
      static av_always_inline av_const long int lrint(double x)
      {
      return rint(x);
      }
      #endif /* HAVE_LRINT */

      #if !HAVE_LRINTF
      static av_always_inline av_const long int lrintf(float x)
      {
      return (int )(rint(x));
      }
      #endif /* HAVE_LRINTF */

      #if !HAVE_ROUND
      static 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_ROUNDF
      static 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_TRUNCF
      static 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.0
      PLATFORM=/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-x86
      PLATFORM=/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 文件 
    image

    修改后的 build-binary.mk 文件 
    image

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

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

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


    image

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

    image

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

    image
  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 (需要翻墙访问)

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
要在Windows上使用NDK编译FFmpeg,你需要按照以下步骤进行操作: 1. 首先,确保你已经安装了NDK和CMake。你可以从Android开发者网站下载并安装Android Studio,它会自动安装NDK和CMake。 2. 下载FFmpeg源代码。你可以从FFmpeg的官方网站或GitHub上获取最新的源代码。 3. 创建一个用于编译FFmpeg的CMakeLists.txt文件。这个文件描述了编译过程中所需的配置和依赖项。你可以使用以下示例作为起点: ```cmakecmake_minimum_required(VERSION3.4.1) # 设置FFmpeg源代码的路径set(FFMPEG_SOURCE_DIR /path/to/ffmpeg/source) # 设置编译输出的路径set(FFMPEG_OUTPUT_DIR /path/to/ffmpeg/output) # 添加FFmpeg源代码的子目录add_subdirectory(${FFMPEG_SOURCE_DIR} ${FFMPEG_OUTPUT_DIR}) # 添加FFmpeg库到你的项目中add_library(ffmpeg SHARED IMPORTED) set_target_properties(ffmpeg PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libffmpeg.so) # 链接FFmpeg库到你的项目中target_link_libraries(your_project ffmpeg) ``` 在上面的示例中,你需要将`/path/to/ffmpeg/source`替换为你下载的FFmpeg源代码的路径,将`/path/to/ffmpeg/output`替换为你想要生成输出文件的路径。 4. 打开命令提示符或终端,并导航到包含CMakeLists.txt文件的目录。 5. 运行以下命令来生成构建文件: ```shellcmake -DCMAKE_TOOLCHAIN_FILE=/path/to/your/ndk/build/cmake/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NATIVE_API_LEVEL=21 . ``` 在上面的命令中,将`/path/to/your/ndk/build/cmake/android.toolchain.cmake`替换为你的NDK安装目录中的相应路径。`ANDROID_ABI`和`ANDROID_NATIVE_API_LEVEL`参数可以根据你的需求进行调整。 6. 运行以下命令来编译FFmpeg: ```shellcmake --build . ``` 这将使用CMake生成的构建文件来编译FFmpeg。 请注意,编译FFmpeg可能需要一些时间,具体取决于你的系统配置和源代码的大小。完成后,你将在指定的输出路径中找到编译好的FFmpeg库文件。 这只是一个简单的指导,实际中可能会遇到其他问题。在遇到困难时,你可以参考FFmpeg官方文档或在相关的开发者社区中寻求帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值