Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

LLhon/FFmpegBuild

Open more actions menu

Repository files navigation

本项目将介绍如何编译FFmpeg在Android中使用

编译环境
系统:Windows 11
终端:msys2
Android Studio 版本:Hedgehog
NDK版本:21.4.7075529
FFmpeg版本:6.1

强烈建议:windows系统还是安装一个ubuntu的虚拟机吧,少踩很多坑

一、修改FFmpeg源码

既然要编译FFmpeg源码,那么就需要更改一些配置,大概就两步:

  • 下载FFmpeg源码,以及ndk-r22b

  • 进入下载的源码,编辑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)'
  • 在当前目录下新建build_android.sh文件,并写入以下内容:
#!/bin/bash
# 清空上次的编译
make clean
#你自己的NDK路径.
NDK=/home/anjoiner/Android/Sdk/ndk-bundle
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
SYSROOT=$TOOLCHAIN/sysroot
API=21

function build_android
{
echo "Compiling FFmpeg for $CPU"
./configure \
    --prefix=$PREFIX \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-doc \
    --disable-symver \
    --enable-nonfree \
    --enable-gpl \
    --enable-small \
    --enable-neon \
    --enable-hwaccels \
    --enable-avdevice \
    --enable-postproc \
    --enable-shared \
    --enable-jni \
    --enable-mediacodec \
    --enable-decoder=h264_mediacodec \
    --cross-prefix=$CROSS_PREFIX \
    --target-os=android \
    --arch=$ARCH \
    --cpu=$CPU \
    --nm=$NM \
    --strip=$STRIP \
    --cc=$CC \
    --cxx=$CXX \
    --enable-cross-compile \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
echo "The Compilation of FFmpeg for $CPU is completed"
}

#armv8-a
ARCH=aarch64-linux-android-
VERSION=arm64
CPU=armv8-a
CROSS_PREFIX=$TOOLCHAIN/bin/${ARCH}
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
NM=$TOOLCHAIN/bin/${ARCH}nm
STRIP=$TOOLCHAIN/bin/${ARCH}strip
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build_android

#armv7-a
ARCH=arm-linux-androideabi-
VERSION=arm
CPU=armv7-a
CROSS_PREFIX=$TOOLCHAIN/bin/${ARCH}
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
NM=$TOOLCHAIN/bin/${ARCH}nm
STRIP=$TOOLCHAIN/bin/${ARCH}strip
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -march=$CPU"
build_android

二、开始编译

在编译之前需要为刚刚我们创建的文件赋予执行权限

sudo chmod +x build_android.sh

然后就开始编译

./build_android.sh

经过漫长的等待...就会在源码目录下得到如下内容

三、ndk编译

上面得到的一系列so文件还不能直接使用,还需要进行如下配置

配置

  • 在任意目录下创建jni文件夹

  • 将刚刚编译出来的android/armv7-a/include下的所有目录拷贝进jni文件夹

  • jni下创建一个prebuilt文件夹

  • android/armv7-a/lib下所有的so文件拷贝进入prebuilt

  • 将源码下fftools文件夹下面的如下文件拷贝进入jni

    • cmdutils.c
    • cmdutils.h
    • ffmpeg_filter.c
    • ffmpeg_hw.c
    • ffmpeg_opt.c
    • ffmpeg.c
    • ffmpeg.h
  • 将源码下的config.h进入jni

编辑

  • 编辑 ffmpeg.c

将如下代码

int main(int argc, char **argv)

替换为

int run(int argc, char **argv)
  • 编辑 ffmpeg.h

ffmpeg.h中增加int int run(int argc, char **argv);

...
int hw_device_setup_for_encode(OutputStream *ost);

int hwaccel_decode_init(AVCodecContext *avctx);
// add it
int run(int argc, char **argv);

#endif /* FFTOOLS_FFMPEG_H */
  • 编辑cmdutils.h 将如下内容
void show_help_children(const AVClass *class, int flags);

替换为

void show_help_children(const AVClass *clazz, int flags);

JNI

接下来就是开始使用jni去调用ffmpeg中的方法了

  • 在jni文件夹中创建ffmpeg-invoke.cpp,但是需要使用你自己的文件路径替换com_llhon_ffmpeg_FFmpegCmd
#include <jni.h>
#include <string.h>
#include "android/log.h"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ffmpeg-invoke", __VA_ARGS__)

extern "C"{
#include "ffmpeg.h"
#include "libavcodec/jni.h"
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_llhon_ffmpeg_FFmpegCmd_run(JNIEnv *env, jclass type, jint cmdLen,
                                             jobjectArray cmd) {
    //set java vm
    JavaVM *jvm = NULL;
    env->GetJavaVM(&jvm);
    av_jni_set_java_vm(jvm, NULL);

    char *argCmd[cmdLen] ;
    jstring buf[cmdLen];

    for (int i = 0; i < cmdLen; ++i) {
        buf[i] = static_cast<jstring>(env->GetObjectArrayElement(cmd, i));
        char *string = const_cast<char *>(env->GetStringUTFChars(buf[i], JNI_FALSE));
        argCmd[i] = string;
        LOGD("argCmd=%s",argCmd[i]);
    }

    int retCode = run(cmdLen, argCmd);
    LOGD("ffmpeg-invoke: retCode=%d",retCode);

    return retCode;

}
  • 在jni文件夹中创建Android.mk,需要更改LOCAL_C_INCLUDES
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE :=  libavdevice
LOCAL_SRC_FILES := prebuilt/libavdevice.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  libavutil
LOCAL_SRC_FILES := prebuilt/libavutil.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  libswresample
LOCAL_SRC_FILES := prebuilt/libswresample.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  libswscale
LOCAL_SRC_FILES := prebuilt/libswscale.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavcodec
LOCAL_SRC_FILES := prebuilt/libavcodec.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavformat
LOCAL_SRC_FILES := prebuilt/libavformat.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavfilter
LOCAL_SRC_FILES := prebuilt/libavfilter.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libpostproc
LOCAL_SRC_FILES := prebuilt/libpostproc.so
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-invoke

LOCAL_SRC_FILES :=ffmpeg-invoke.cpp \
                 cmdutils.c \
                 ffmpeg_filter.c \
                 ffmpeg_opt.c \
                 ffmpeg_hw.c \
                 ffmpeg.c

# 将此处的路径改为你ffmepg源码所在位置
LOCAL_C_INCLUDES := /home/llhon/develop/FFmpeg/ffmpeg-6.1

LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid -lm -pthread -L$(SYSROOT)/usr/lib
LOCAL_SHARED_LIBRARIES := libavdevice libavcodec libavfilter libavformat libavutil libswresample libswscale libpostproc

include $(BUILD_SHARED_LIBRARY)
  • 在jni文件夹中创建Application.mk
APP_ABI := armeabi-v7a
APP_PLATFORM := android-21
APP_OPTIM := release
APP_STL := c++_static
  • jni文件夹中执行ndk-build命令
/home/llhon/develop/Android/Sdk/ndk-bundle/ndk-build

如果发生以下错误:

1. D:/develop/workspace/android_ws/FFmpegBuild/app/src/main/jni/ffmpeg.h:896:35: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal]

那就编辑ffmpeg.h,将如下内容

#define SPECIFIER_OPT_FMT_i64 "%"PRId64
#define SPECIFIER_OPT_FMT_ui64 "%"PRIu64

替换为

#define SPECIFIER_OPT_FMT_i64  "%" PRId64
#define SPECIFIER_OPT_FMT_ui64 "%" PRIu64
2. D:/develop/workspace/android_ws/FFmpegBuild/app/src/main/jni/ffmpeg.h:311:20: error: expected member name or ';' after declaration specifiers

编辑ffmpeg.h,将如下内容

typedef struct FilterGraph {
    const AVClass *class;

替换为

typedef struct FilterGraph {
    const AVClass *clazz;
3. D:/develop/workspace/android_ws/FFmpegBuild/app/src/main/jni/libavcodec/packet.h:787:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'AVPacket *' for 1st argument

编辑libavcodec/packet.h,将如下内容

static inline void pkt_move(void *dst, void *src)

改为

static inline void pkt_move(AVPacket *dst, AVPacket *src)
4. D:/develop/workspace/android_ws/FFmpegBuild/app/src/main/jni/ffmpeg.c:795: error: undefined reference to 'fix_sub_duration_heartbeat'

未链接到某些资源文件: 可能的情况是jni文件夹内缺少 ffmpeg 某个源码文件,可以进入D:\develop\workspace\android_ws\FFmpeg-release-6.1\fftools文件夹内是否有需要链接的文件 copy 到jni文件夹

再次执行ndk-build命令,大概会得到如下内容,那就说明编译完成了

四、如何将多个so文件合并成一个独立的so文件

1. 在 build_android.sh 文件里添加以下代码:

# 打包,以下代码是为了把7个so包合成一个so包,如果不需要合成可以去掉这段代码
echo "开始编译libffmpeg-org.so"
$TOOLCHAIN/bin/$ARC-ld \
    -rpath-link=$SYSROOT/usr/lib/$ARC/$API \
    -L$SYSROOT/usr/lib/$ARC/$API \
    -L$TOOLCHAIN/lib/gcc/$ARC/4.9.x \
    -L$PREFIX/lib -soname libffmpeg-org.so \
    -shared -Bsymbolic --whole-archive --no-undefined -o \
    $PREFIX/libffmpeg-org.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 \
    $PREFIX/lib/libavdevice.a \
    $PREFIX/lib/libpostproc.a \
    -lc -lm -lz -ldl -llog -landroid --dynamic-linker=/system/bin/linker \
    $TOOLCHAIN/lib/gcc/$ARC/4.9.x/libgcc_real.a || exit 1

echo "完成编译libffmpeg-org.so"

2. 在 MSYS2 CLANG64 终端命令行中执行 ./build_android.sh 脚本文件

完整的脚本代码参考scripts/build_android.sh

3. 如果遇到以下错误:

1. libswscale.a(half2float.o): multiple definition of 'ff_init_half2float_tables'; libavcodec.a(half2float.o): previous definition here

编译.a文件时,libavcodec.a和libswscale.a包含同一个half2float.o,在链接 libavcodec.a 和 libswscale.a 时会出现函数重定义的错误。需要在执行脚本之前修改libswscale 文件夹下的 Makefile ,将 OBJS 下的 half2float.o 删除即可

2. libavfilter/vf_pp.c:59: error: undefined reference to 'pp_get_mode_by_name_and_quality';libavfilter/vf_pp.c:164: error: undefined reference to 'pp_free_mode'

这一般是由于链接时找不到对应的库报的错误,在 build_android.sh 脚本文件里检查下是否有链接 libpostproc.a 文件

About

init commit

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.