摘要:本文主要介绍将FFmpeg音视频编解码库移植到Android平台上的编译和基本测试过程。

环境准备:

ubuntu-12.04.5

android-ndk64-r10-linux-x86_64.tar.tar

ffmpeg-2.7.2_tmp.tar.bz2

adt-bundle-windows-x86_64-20131030.zip

第一步:源代码下载

到FFmpeg官方网站http://www.ffmpeg.org/上去下载源代码,这里下载的源代码是最权威的。进入官网之后,选择”Download”进入下载页面,截止2014年3月28日止,最新的发布的稳定版本为FFmpeg2.2,代号”Muybridge”。选择该下方的”Downloadgzip tarball”进行下载,下载后的文件名为ffmpeg-2.2.tar.gz,大约8.3M。

第二步:在Linux环境下编译FFmpeg

在Windows平台可以采用VMplayer虚拟机上安装ubuntu的方式,本人也是采用这种方式。

本文以/home/dennis为根目录进行操作和说明:

将ffmpeg-2.2.tar.gz拷贝至根目录,然后执行如下解压命令将其解压:

$tar zxf ffmpeg-2.2.tar.gz

解压后将得到/home/dennis/ffmpeg-2.2目录。

修改ffmpeg-2.2/configure文件

如果直接按照未修改的配置进行编译,结果编译出来的so文件类似libavcodec.so.55.39.101,版本号位于so之后,Android上似乎无法加载。因此需要按如下修改:

将该文件中的如下四行:

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)'

编写build_android.sh脚本文件

FFmpeg可以说是一个包络音视频编解码及格式的超级霸。因此在编译前通常都需要进行配置,设置相应的环境变量等。

所有的配置选项都在ffmpeg-2.2/configure这个脚本文件中,可以通过执行如下命令来查看所有的配置选项:

$ ./configure –help

配置选项很多,也较为复杂,这里先把我需要的搞出来,然后有时间再慢慢看。

我们将需要的配置项和环境变量设置写成一个sh脚本文件来运行以便编译出Android平台需要的so文件出来。

build_android.sh的内容如下:

  1. #!/bin/bash
  2. NDK=/home/dennis/android-ndk-r9d
  3. SYSROOT=$NDK/platforms/android-9/arch-arm/
  4. TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
  5. function build_one
  6. {
  7. ./configure \
  8. --prefix=$PREFIX \
  9. --enable-shared \
  10. --disable-static \
  11. --disable-doc \
  12. --disable-ffserver \
  13. --enable-cross-compile \
  14. --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
  15. --target-os=linux \
  16. --arch=arm \
  17. --sysroot=$SYSROOT \
  18. --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
  19. --extra-ldflags="$ADDI_LDFLAGS" \
  20. $ADDITIONAL_CONFIGURE_FLAG
  21. }
  22. CPU=arm
  23. PREFIX=$(pwd)/android/$CPU
  24. ADDI_CFLAGS="-marm"
  25. build_one
#!/bin/bash
NDK=/home/dennis/android-ndk-r9d
SYSROOT=$NDK/platforms/android-9/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64 function build_one
{
./configure \
--prefix=$PREFIX \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffserver \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one

这个脚本文件有几个地方需要注意:

(1)    NDK,SYSROOT和TOOLCHAIN这三个环境变量一定要换成你自己机器里的。

(2)    确保cross-prefix变量所指向的路径是存在的。

给build_android.sh增加可执行权限:

  1. $chmod+x build_android.sh
$chmod+x build_android.sh

执行build_android.sh

  1. $./build_android.sh
$./build_android.sh

配置该脚本完成对ffmpeg的配置,会生成config.h等配置文件,后面的编译会用到。如果未经过配置直接进行编译会提示无法找到config.h文件等错误。

  1. $make
  2. $make install
$make
$make install

至此,会在/home/dennis/ffmpeg-2.2目录下生成一个android目录,其中/home/dennis/ffmpeg-2.2/android/arm/lib目录下的so库文件如下:

  1. -rwxr-xr-x 1 dennisdennis   55208 Mar 29 16:26libavdevice-55.so
  2. -rwxr-xr-x 1 dennisdennis  632476 Mar 29 16:26 libavfilter-4.so
  3. -rwxr-xr-x 1 dennisdennis 1442948 Mar 29 16:26 libavformat-55.so
  4. -rwxr-xr-x 1 dennisdennis 7985396 Mar 29 16:26 libavcodec-55.so
  5. -rwxr-xr-x 1 dennisdennis   83356 Mar 29 16:26libswresample-0.so
  6. -rwxr-xr-x 1 dennisdennis  308636 Mar 29 16:26 libswscale-2.so
  7. -rwxr-xr-x 1 dennisdennis  300580 Mar 29 16:26libavutil-52.so
-rwxr-xr-x 1 dennisdennis   55208 Mar 29 16:26libavdevice-55.so
-rwxr-xr-x 1 dennisdennis 632476 Mar 29 16:26 libavfilter-4.so
-rwxr-xr-x 1 dennisdennis 1442948 Mar 29 16:26 libavformat-55.so
-rwxr-xr-x 1 dennisdennis 7985396 Mar 29 16:26 libavcodec-55.so
-rwxr-xr-x 1 dennisdennis 83356 Mar 29 16:26libswresample-0.so
-rwxr-xr-x 1 dennisdennis 308636 Mar 29 16:26 libswscale-2.so
-rwxr-xr-x 1 dennisdennis 300580 Mar 29 16:26libavutil-52.so

注:以上列表去掉了符号链接文件和pkgconfig目录。

第三步:创建一个普通的Android工程

  1. 创建一个新的Android工程FFmpeg4Android
  2. 在工程根目录下创建jni文件夹
  3. 在jni下创建prebuilt目录,然后:

(1)     将上面编译成功的7个so文件放入到该目录下;

(2)     将/home/dennis/ffmpeg-2.2/android/arm/include下的所有头文件夹拷贝到该目录下.

  1. 创建包含native方法的类,先在src下创建cn.dennishucd包,然后创建FFmpegNative.java类文件。主要包括加载so库文件和一个native测试方法两部分,其内容如下:
  1. package cn.dennishucd;
  2. public class FFmpegNative {
  3. static{
  4. System.loadLibrary("avutil-52");
  5. System.loadLibrary("avcodec-55");
  6. System.loadLibrary("swresample-0");
  7. System.loadLibrary("avformat-55");
  8. System.loadLibrary("swscale-2");
  9. System.loadLibrary("avfilter-3");
  10. System.loadLibrary("ffmpeg_codec");
  11. }
  12. publicnative int avcodec_find_decoder(int codecID);
  13. }
package cn.dennishucd;
public class FFmpegNative {
static{
System.loadLibrary("avutil-52");
System.loadLibrary("avcodec-55");
System.loadLibrary("swresample-0");
System.loadLibrary("avformat-55");
System.loadLibrary("swscale-2");
System.loadLibrary("avfilter-3");
System.loadLibrary("ffmpeg_codec");
}
publicnative int avcodec_find_decoder(int codecID);
}
  1. 用javah创建.头文件:

进入bin/classes目录,执行:javah-jni cn.dennishucd.FFmpegNative

会在当前目录产生cn_dennishucd_FFmpegNative.h的C头文件;

  1. 根据头文件名,建立相同名字才C源文件cn_dennishucd_FFmpegNative.c

在这个源文件中实现头文件中定义的方法,核心部分代码如下:

  1. JNIEXPORT jint JNICALLJava_cn_dennishucd_FFmpegNative_avcodec_1find_1decoder
  2. (JNIEnv *env, jobject obj, jint codecID)
  3. {
  4. AVCodec*codec = NULL;
  5. /*register all formats and codecs */
  6. av_register_all();
  7. codec= avcodec_find_decoder(codecID);
  8. if(codec != NULL)
  9. {
  10. return0;
  11. }
  12. else
  13. {
  14. return-1;
  15. }
  16. }<span style="font-family: Arial, Helvetica, sans-serif; "> </span>
JNIEXPORT jint JNICALLJava_cn_dennishucd_FFmpegNative_avcodec_1find_1decoder
(JNIEnv *env, jobject obj, jint codecID)
{
AVCodec*codec = NULL; /*register all formats and codecs */
av_register_all(); codec= avcodec_find_decoder(codecID); if(codec != NULL)
{
return0;
}
else
{
return-1;
}
}
  1. 编写Android.mk,内容如下:
  1. LOCAL_PATH := $(callmy-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE :=avcodec-55-prebuilt
  4. LOCAL_SRC_FILES :=prebuilt/libavcodec-55.so
  5. include$(PREBUILT_SHARED_LIBRARY)
  6. include $(CLEAR_VARS)
  7. LOCAL_MODULE :=avdevice-55-prebuilt
  8. LOCAL_SRC_FILES :=prebuilt/libavdevice-55.so
  9. include$(PREBUILT_SHARED_LIBRARY)
  10. include $(CLEAR_VARS)
  11. LOCAL_MODULE :=avfilter-4-prebuilt
  12. LOCAL_SRC_FILES :=prebuilt/libavfilter-4.so
  13. include$(PREBUILT_SHARED_LIBRARY)
  14. include $(CLEAR_VARS)
  15. LOCAL_MODULE :=avformat-55-prebuilt
  16. LOCAL_SRC_FILES :=prebuilt/libavformat-55.so
  17. include$(PREBUILT_SHARED_LIBRARY)
  18. include $(CLEAR_VARS)
  19. LOCAL_MODULE :=  avutil-52-prebuilt
  20. LOCAL_SRC_FILES :=prebuilt/libavutil-52.so
  21. include$(PREBUILT_SHARED_LIBRARY)
  22. include $(CLEAR_VARS)
  23. LOCAL_MODULE :=  avswresample-0-prebuilt
  24. LOCAL_SRC_FILES :=prebuilt/libswresample-0.so
  25. include $(PREBUILT_SHARED_LIBRARY)
  26. include $(CLEAR_VARS)
  27. LOCAL_MODULE :=  swscale-2-prebuilt
  28. LOCAL_SRC_FILES :=prebuilt/libswscale-2.so
  29. include$(PREBUILT_SHARED_LIBRARY)
  30. include $(CLEAR_VARS)
  31. LOCAL_MODULE :=ffmpeg_codec
  32. LOCAL_SRC_FILES :=cn_dennishucd_FFmpegNative.c
  33. LOCAL_LDLIBS := -llog-ljnigraphics -lz -landroid
  34. LOCAL_SHARED_LIBRARIES:= avcodec-55-prebuilt avdevice-55-prebuilt avfilter-4-prebuiltavformat-55-prebuilt avutil-52-prebuilt
  35. include$(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(callmy-dir)

include $(CLEAR_VARS)
LOCAL_MODULE :=avcodec-55-prebuilt
LOCAL_SRC_FILES :=prebuilt/libavcodec-55.so
include$(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE :=avdevice-55-prebuilt
LOCAL_SRC_FILES :=prebuilt/libavdevice-55.so
include$(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE :=avfilter-4-prebuilt
LOCAL_SRC_FILES :=prebuilt/libavfilter-4.so
include$(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE :=avformat-55-prebuilt
LOCAL_SRC_FILES :=prebuilt/libavformat-55.so
include$(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := avutil-52-prebuilt
LOCAL_SRC_FILES :=prebuilt/libavutil-52.so
include$(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := avswresample-0-prebuilt
LOCAL_SRC_FILES :=prebuilt/libswresample-0.so
include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := swscale-2-prebuilt
LOCAL_SRC_FILES :=prebuilt/libswscale-2.so
include$(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE :=ffmpeg_codec
LOCAL_SRC_FILES :=cn_dennishucd_FFmpegNative.c LOCAL_LDLIBS := -llog-ljnigraphics -lz -landroid
LOCAL_SHARED_LIBRARIES:= avcodec-55-prebuilt avdevice-55-prebuilt avfilter-4-prebuiltavformat-55-prebuilt avutil-52-prebuilt include$(BUILD_SHARED_LIBRARY)
  1. 编写Application.mk[可省略]
  2. 编译so文件

打开cmd命令行,进入FFmpeg4Android\jni目录下,执行如下命令:

  1. $ndk-build
$ndk-build

截止本步骤完成,将在FFmpeg4Android根目录下生成libs\armeabi目录,该目录除了包含上面的7个so之外,另外还生成了libffmpeg_codec.so文件。

  1. 添加库的加载方法

在FFmpegNative类中增加如下加载so库的代码:

  1. static {
  2. System.loadLibrary("avutil-52");
  3. System.loadLibrary("avcodec-55");
  4. System.loadLibrary("swresample-0");
  5. System.loadLibrary("avformat-55");
  6. System.loadLibrary("swscale-2");
  7. System.loadLibrary("avfilter-3");
  8. System.loadLibrary("avdevice-55");
  9. System.loadLibrary("ffmpeg_codec");
  10. }
static {
System.loadLibrary("avutil-52");
System.loadLibrary("avcodec-55");
System.loadLibrary("swresample-0");
System.loadLibrary("avformat-55");
System.loadLibrary("swscale-2");
System.loadLibrary("avfilter-3");
System.loadLibrary("avdevice-55");
System.loadLibrary("ffmpeg_codec");
}
  1. 修改layout/main.xml,给TextView增加id,以便在代码中操作它。

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:orientation="horizontal"
    4. android:layout_width="fill_parent"
    5. android:layout_height="fill_parent"
    6. >
    7. <TextView
    8. android:id="@+id/textview_hello"
    9. android:text="@string/hello"
    10. android:layout_width="wrap_content"
    11. android:layout_height="wrap_content"
    12. android:layout_gravity="center"
    13. />
    14. </LinearLayout>
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
    android:id="@+id/textview_hello"
    android:text="@string/hello"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
    </LinearLayout>
  2. 增加一个Activity实现类FFmpeg4AndroidActivity,在OnCreate方法中调用native函数将值传给TextView控件,打包运行即可。FFmpeg4AndroidActivity代码如下:
  1. package cn.dennishucd;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.widget.TextView;
  5. public class FFmpeg4AndroidActivity extends Activity {
  6. @Override
  7. protectedvoid onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10. TextViewtv = (TextView)this.findViewById(R.id.textview_hello);
  11. FFmpegNativeffmpeg = new FFmpegNative();
  12. intcodecID = 28; //28 is the H264 Codec ID
  13. intres = ffmpeg.avcodec_find_decoder(codecID);
  14. if(res ==0) {
  15. tv.setText("Success!");
  16. }
  17. else{
  18. tv.setText("Failed!");
  19. }
  20. }
  21. }
package cn.dennishucd;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class FFmpeg4AndroidActivity extends Activity {
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.main); TextViewtv = (TextView)this.findViewById(R.id.textview_hello); FFmpegNativeffmpeg = new FFmpegNative();
intcodecID = 28; //28 is the H264 Codec ID intres = ffmpeg.avcodec_find_decoder(codecID); if(res ==0) {
tv.setText("Success!");
}
else{
tv.setText("Failed!");
}
}
}

代码中的28是H264的编解码ID,可以在ffmpeg的源代码中找到,它是枚举类型定义的。在C语言中,可以换算为整型值。这里测试能否找到H264编解码,如果能找到,说明调用ffmpeg的库函数是成功的,这也表明我们编译的so文件是基本可用。

作者注:

[1] 本文编译的方法主要参考了参考资料 [1] 中的思路,这里要感谢作者的贡献;

[2] 后面的测试过程是参考了ffmpeg-2.1.4中的decoding_encoding.c例子;

[3] 关于如何使用pre-built参考了参考资料 [2] 中的思路;

[4] 这只是移植过程第一步,后面还会进一步分析ffmpeg的接口来调用其编解码库.

[5]Android.mk文件应该还可以优化,这是后面的工作.

[6] 整个过程完成还是耗费了我不少精力,如有转载请注明出处,多谢。本文的完整源代码可以在我的CSDN资源(http://download.csdn.net/detail/gobitan/7132037)下载,最新版本可跟踪我的github(https://github.com/dennishucd/FFmpeg4Android)。

参考资料:

  1. http://www.roman10.net/how-to-build-ffmpeg-with-ndk-r9/
  2. http://www.ciaranmccormack.com/creating-a-prebuilt-shared-library-on-android-using-the-ndk/

FFmpeg的Android平台移植—编译篇的更多相关文章

  1. Cocos2d-x v3.0正式版尝鲜体验【2】 Android平台移植

    今天没事又尝试了下3.0正式版关于Android平台的移植,把新建的项目移植了下.过程仅用了十分钟左右,什么概念?! 好吧,事实上我想说,这个版本号真的移植非常轻松啊,只是还没加上其它东西,只是就眼下 ...

  2. libx264开发笔记(一):libx264介绍、海思平台移植编译

    前言   在编译ffmpeg时,使用到h264编码时是需要依赖libx264的,本文章是将将libx264作为静态库移植到海思上.   相关博客 <Qt开发笔记之编码x264码流并封装mp4(一 ...

  3. cocos2d-x 2.2.2 android平台移植

    1.完成以上工具的下载安装--cocos2d-x 2.2.2 --eclipse+adt+sdk --ndk 2.创建cocos2d-x工程 在"cocos2d-x-2.2.2\tools\ ...

  4. cocos2dx-3.0(21) 移植android平台 说多了都是泪

    ----我的生活,我的点点滴滴! ! 网上3.0的教程真心少.能够说没有吧,大多都是2.x 或者 3.0測试版之类的,因为我心大,没有照着2.x去搞,后来搞完后总结了一下,发觉事实上3.0的移植and ...

  5. 【winows7+android-ndk-r9+Cygwin 】cocos2dx 2.*游戏移植Android平台完全手册

    为了有不少走的弯路.这里简要记录cocos2d开发环境的搭建.android开发环境搭建.android NDK环境搭建.终于实现cocos2dx 2.* 游戏Android平台移植. 转载请注明出处 ...

  6. 将 FFmpeg 移植到 Android平台 (完整版)

    首先需要去FFmpeg的官网http://www.ffmpeg.org/去下载FFmpeg的源码,目前的版本号为FFmpeg3.3(Hilbert). 下载的文件为压缩包,解压后得到ffmpeg-3. ...

  7. FFmpeg 学习(三):将 FFmpeg 移植到 Android平台

    首先需要去FFmpeg的官网http://www.ffmpeg.org/去下载FFmpeg的源码,目前的版本号为FFmpeg3.3(Hilbert). 下载的文件为压缩包,解压后得到ffmpeg-3. ...

  8. 深入浅出-Android系统移植与平台开发(一)- Android4.0系统的下载与编译

    作者:唐老师,华清远见嵌入式学院讲师. 一.Android4.0系统的下载与编译 Android系统的下载与编译,Google的官方网站上已经给出了详细的说明,请参照Android的官方网址: htt ...

  9. 深入浅出 - Android系统移植与平台开发(五)- 编译Android源码(转)

    2.3编译Android源码 Android源码体积非常庞大,由Dalvik虚拟机.Linux内核.编译系统.框架代码.Android定制C库.测试套件.系统应用程序等部分组成,在编译Android源 ...

随机推荐

  1. GDB: advanced usages

    Sometimes running program in Unix will fail without any debugging info or warnings because of the la ...

  2. First blogs start

    这是我第一次写博客笔记,为了更好的成长,从现在开始我的博客记录. 我是从事linux运维的,到目前为止,已经有2年的工作经验了. 希望从此以后我能够坚持每天记录下点点滴滴.

  3. jQuery实现的简单分页功能的详细解析

    分页功能在项目开发中不可或缺,老司机操作起来就和呼吸一样简单,新手恐怕就会吃力一些. 今天我回顾了一下具体的操作步骤,决定详细的分析一下每一步的实现目的及原理. 我们会创建一个简单的json文件来模拟 ...

  4. Primes on Interval

    AC代码: #include <cstdio> #include <cstring> #include <iostream> #include <algori ...

  5. openwrt默认不开启wifi

    Openwrt默认不开启wifi,要开启的话, 修改这个文件: openwrt/trunk/package/kernel/mac80211/files/lib/wifi/mac80211.sh. 滚到 ...

  6. Lambda表达式介绍

    Lambda表达式实际上是一个匿名函数.它包含表达式和语句,常用于创建委托或表达式目录树类型.所有Lambda表达式都是用Lambda运算符----------  =>,该运算符读为" ...

  7. js纯ajax

    var XMLHttpReq; function createXMLHttpRequest() { try { XMLHttpReq = new ActiveXObject("Msxml2. ...

  8. mongo细节

    mongo创建表db.createCollection(name, {capped: <Boolean>, autoIndexId: <Boolean>, size: < ...

  9. SDCycleScrollView 添加初始滚动页码

    最近在做一个无限轮播界面,由于自己写的有些卡顿,所以我使用了第三方框架SDCycleScrollView,但是我的初始滚动页不是从第一页开始怎么办,我想从第二页,第三页,或者最后一页,关于这个SDCy ...

  10. ASP.NET 会话状态的模式

    ASP.NET 会话状态为会话数据提供了几个不同的存储选项.每个选项都通过一个 SessionStateMode 枚举值进行识别.如下列表中描述了可用的会话状态模式: InProc 模式:把会话状态存 ...