编译Android平台使用的FFmpeg库

目录

  • 编译生成多个.so
  • 打包成一个.so
  • 在安卓上测试使用

编译FFmpeg

-编译环境:

Mac OS
NDK-r10e
FFmpeg 3.3.3

-编译过程

-修改configure文件

下载FFmpeg源代码之后,首先需要对源代码中的configure文件进行修改。由于编译出来的动态库文件名的版本号在.so之后(例如“libavcodec.so.5.100.1”),而android平台不能识别这样文件名,所以需要修改这种文件名。

找到 ffmpeg-3.3/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)'

-编写脚本文件

新建脚本文件 ffmpeg-3.3/build_android.sh,保存下面脚本。

需要注意的是,这个脚本文件最好不要用记事本去创建改文件后缀的方式,记事本会添加一些奇怪的结束符(Line ending),我是直接拷贝ffmpeg解压后文件夹里的version.sh改的build_android.sh

#!/bin/bash

# NDK的路径,根据自己的安装位置进行设置
NDK=~/Library/Android/sdk/ndk-bundle

# 编译针对的平台,可以根据自己的需求进行设置
# 这里选择最低支持android-14, arm架构,生成的so库是放在
# libs/armeabi文件夹下的,若针对x86架构,要选择arch-x86
PLATFORM=$NDK/platforms/android-14/arch-arm


# 工具链的路径,根据编译的平台不同而不同
# arm-linux-androideabi-4.9与上面设置的PLATFORM对应,4.9为工具的版本号,
# 根据自己安装的NDK版本来确定,一般使用最新的版本
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64

ARCH=arm
TARGETOS=android
PREFIX=$(pwd)/$TARGETOS/$ARCH
PREFIX1=$(pwd)
ADDITIONAL_CONFIGURE_FLAG=

./configure \
    --prefix=$PREFIX \
    --enable-shared \
    --disable-static \
    --disable-doc \
    --disable-programs \
    --enable-small \
    --disable-avdevice \
    --disable-devices \
    --disable-protocols \
    --enable-protocol=file \
    --enable-cross-compile \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$PLATFORM \
    --extra-cflags="-Os -fpic" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    --arch="$ARCH" \
    --target-os="$TARGETOS"

make clean
make
make install

写完脚本文件后,需要将该脚本文件变为可执行文件

$ chmod +x build_android.sh

然后运行

$ ./build_android.sh

等待几分钟,就能在android/arm下生成一个所需的头文件和动态库。
这里写图片描述

编译成一个.so

这里看别人博客遇到很多坑,语法都写不对。。。鬼知道哪些人怎么编译成功的。。。(其实主要应该是FFmpeg的版本不同,导致编译出的东西,或者路径都少有差异,而博客作者又没有说明,给后来者早晨困扰,这里也希望读者能根据自己的FFmpeg版本,去灵活改写编译脚本,比如我下面遇到的问题)
下面这个是成功编译的。有几个重要选项参数说明一下。
与前文记录的不同,这个脚本不需要改congfigure。
但需要将前面那个脚本文件里configure命令里:

–enable-shared \
–disable-static\
改成:\
–enable-static\
–disable-shared

这个意思是编译成静态库(.a),而非之前的动态库(.so);

编译完成是:
- libavcodec.a
- libavfilter.a
- libswresample.a
- libavformat.a
- libavutil.a
- libswscale.a

然后脚本编译生成.a文件之后,会继续运行后面代码(代码区最后一段注释有),将多个.a文件打包生成一个.so文件.

打包.a生成.so的代码部分如下:

需要注意里面的参数:

-o $PREFIX/libffmpeg.so :
指的是输出的文件,即我们所需要打包出的一个.so文件

$PREFIX/lib/libavcodec.a \
$PREFIX/lib/libavfilter.a \
$PREFIX/lib/libswresample.a \
$PREFIX/lib/libavformat.a \
$PREFIX/lib/libavutil.a \
$PREFIX/lib/libswscale.a \
这些指需要链接的.a文件\

一开始总是打包失败就是这里出的问题,.a文件的路径不对,导致脚本执行时找不到,这里我是给出了.a文件的绝对路径,通过定义的一个变量:

PREFIX=$(pwd)/$TARGETOS/$ARCH
PREFIX其实就是指向  当前目录(ffmpeg3.3)/android/arm/

我们的.a生成之后的结构如下:
这里写图片描述

这里是合并生成静态库的代码:

# 合并生成的静态库

$TOOLCHAIN/bin/arm-linux-androideabi-ld \
    -rpath-link=$PLATFORM/usr/lib \
    -L$PLATFORM/usr/lib \
    -soname libffmpeg.so \
    -shared -nostdlib  \
    -Bsymbolic \
    --whole-archive --no-undefined \
    -o $PREFIX/libffmpeg.so \
    $PREFIX/lib/libavcodec.a \
    $PREFIX/lib/libavfilter.a \
    $PREFIX/lib/libswresample.a \
    $PREFIX/lib/libavformat.a \
    $PREFIX/lib/libavutil.a \
    $PREFIX/lib/libswscale.a \
    -lc -lm -lz -ldl -llog \
    $TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a

这是总的脚本文件:

前面是先生成多个.a静态文件,后面是将.a打包成一个.so文件,所以这两个可以分开运行,不用写在一个文件里,如果实现编译出了.a文件,那么可以在脚本里直接只运行后面的“合并生成静态库”的代码。

#!/bin/bash

# NDK的路径,根据自己的安装位置进行设置
NDK=~/Library/Android/sdk/ndk-bundle

# 编译针对的平台,可以根据自己的需求进行设置
# 这里选择最低支持android-14, arm架构,生成的so库是放在
# libs/armeabi文件夹下的,若针对x86架构,要选择arch-x86
PLATFORM=$NDK/platforms/android-14/arch-arm


# 工具链的路径,根据编译的平台不同而不同
# arm-linux-androideabi-4.9与上面设置的PLATFORM对应,4.9为工具的版本号,
# 根据自己安装的NDK版本来确定,一般使用最新的版本
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64

ARCH=arm
TARGETOS=android
PREFIX=$(pwd)/$TARGETOS/$ARCH
PREFIX1=$(pwd)
ADDITIONAL_CONFIGURE_FLAG=

./configure \
    --prefix=$PREFIX \
    --disable-shared \
    --enable-static \
    --disable-doc \
    --disable-programs \
    --enable-small \
    --disable-avdevice \
    --disable-devices \
    --disable-protocols \
    --enable-protocol=file \
    --enable-cross-compile \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$PLATFORM \
    --extra-cflags="-Os -fpic" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    --arch="$ARCH" \
    --target-os="$TARGETOS"

make clean
make
make install

# 合并生成的静态库

$TOOLCHAIN/bin/arm-linux-androideabi-ld \
    -rpath-link=$PLATFORM/usr/lib \
    -L$PLATFORM/usr/lib \
    -soname libffmpeg.so \
    -shared -nostdlib  \
    -Bsymbolic \
    --whole-archive --no-undefined \
    -o $PREFIX/libffmpeg.so \
    $PREFIX/lib/libavcodec.a \
    $PREFIX/lib/libavfilter.a \
    $PREFIX/lib/libswresample.a \
    $PREFIX/lib/libavformat.a \
    $PREFIX/lib/libavutil.a \
    $PREFIX/lib/libswscale.a \
    -lc -lm -lz -ldl -llog \
    $TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a

生成结果如图:

一个.so
这里参考了以下博客,虽然里面有不少错误,但是整体流程还是对的;

这个介绍的比较详细:
http://www.ihubin.com/blog/android-ffmpeg-demo-1/
http://www.ihubin.com/blog/android-ffmpeg-demo-3/
这一篇语法错误最少:
https://blog.crazyman.top/2015/05/06/%E7%BC%96%E8%AF%91ffmpeg/

在安卓平台测试使用

以命令行的方式来使用FFmpeg

参考下面这篇博客的话,我们已经能够使用上面编译出的.so动态库了,如果我们知道自己要调用的函数是什么,在Java中定义好native方法,用javah命令生成.h头文件,然后再撰写对应的.c文件,在用ndk-build命令将写好的.c文件生成.so使用即可

http://www.ihubin.com/blog/android-ffmpeg-demo-3/


但是这样用太麻烦,FFmpeg提供了强大的命令行的功能,如果我们能直接调用FFmpeg的命令行,只需要java中传递命令,然后交由jni调用c执行,就太方便了,参考一下博客:

http://www.ihubin.com/blog/android-ffmpeg-demo-4/

首先定义native方法run(),然后同理生成.h文件,再编写.c文件,在.c文件里函数接受命令,并将命令传递给ffmpeg.c去执行,ffmpeg.c是ffmpeg的入口文件,里面的main方法接受命令并执行。所以把main()方法改run(),并返回执行结果状态int。

这是编写的.c文件:

可以看到它 include “ffmpeg.h”
然后run()函数里接收commands命令,再最后调用ffmpeg里的run(),这个run其实就是ffmpeg.c里的原main()

#include "softmicro_com_ffmpegtest_FFmpegKit.h"
#include "ffmpeg.h"
#include <string.h>
#include <android/log.h>

/*
 * Class:     softmicro_com_ffmpegtest_FFmpegKit_run
 * Method:    run
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_softmicro_com_ffmpegtest_FFmpegKit_run(JNIEnv *env,
        jclass obj, jobjectArray commands) {

    int argc = (*env)->GetArrayLength(env, commands);
    char *argv[argc];

    __android_log_print(ANDROID_LOG_ERROR,"Kit","argc %d\n", argc);
    int i;
    for (i = 0; i < argc; i++) {
        jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
        argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
        __android_log_print(ANDROID_LOG_ERROR,"Kit","argv %s\n", argv[i]);
    }
    return run(argc, argv);
}

再解释下ndk-build命令是做什么的:
ndk-build是根据写的Android.mk文件来生成.so的,也是个脚本文件。
以下面这个为例:
这里分两段,前一段是将之前编译好的libffmpeg.so搬到libs文件夹下,后面一段是将LOCAL_SRC_FILES里列的.c文件生成我们需要的LOCAL_MODULE := ffmpeginvoke 生成一个libffmpeginvoke.so

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeginvoke
LOCAL_SRC_FILES := softmicro_com_ffmpegtest_FFmpegKit.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
LOCAL_C_INCLUDES := /Users/wangshuainan/Desktop/ffmpeg-3.3.3
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg

include $(BUILD_SHARED_LIBRARY)
阅读更多

没有更多推荐了,返回首页