保姆式教程!!教你怎么实现Android集成FFmpeg

最近开发一个android项目时需要引入FFmpeg模块,踩了很多坑,所以在这里总结一下

整体集成思路

由于FFmpeg是c语言写的代码,所以要在Android Studio中调用FFmpeg的核心思路是将FFmpeg 编译为Android可用的库(.so文件)然后通过JNl (Java Native Interface)调用FFmpeg 的功能。以下是详细的步骤和核心思路:

 -------------------------------------------------------------------------------------------------------------------------

核心思路


1.编译FFmpeg 为Android可用的库:

  • FFmpeg是一个用C编写的开源多媒体框架,Android无法直接使用,需要将其编译为Andro支持的动态库( .so文件)。
  • 编译时需要针对Android的CPU架构(如ARMv7、ARM64、x86等)进行交叉编译。

2.集成到Android项目, 通过JNI调用FFmpeg:

  • 在Android项目中,Java代码无法直接调用C代码,需要通过JNI实现Java与C的交互。
  • 编写JNI层代码,封装FFmpeg的功能,供Java调用。
  • 将编译好的 FFmpeg库和JNI代码集成到Android Studio 项目中。
  • 使用CMake或NDK-Build配置项目的构建过程。

 1.编译FFmpeg 为Android可用的库:

记住记住一定要使用Linux来进行交叉编译,千万不要用windows来搞,会死!

①创建Linux虚拟机

这里采用的是通过VMware创建虚拟机CentOS7

VMware虚拟机中下载安装CentOS 7(详细图文教程)_vmware阿里云镜像下载-CSDN博客

② 下载ffmpeg源码

https://www.ffmpeg.org/releases/ffmpeg-5.1.6.tar.gz

③ 下载ndk,使用的android-ndk-r21e-linux-x86_64.zip

Unsupported Downloads · android/ndk Wiki · GitHub

坑:千万注意ndk和ffmpeg的版本要下对,不然会出现版本兼容问题

 ④将两个下载包拖拽到Linux中(任意位置),并解压

tar -xzvf ffmpeg-5.1.6.tar.gz
unzip android-ndk-r21e-linux-x86_64.zip

⑤解压ndk,并生成自己的tools-chains目录
 

切换目录至 /root/xxxx/android-ndk-r14b/build/tools,运行该文件夹下的make_standalone_toolchain.py文件来生成工具链

根据要部署在什么CPU结构上的Android来选择生成哪种tool-chains

For arm: 

python make_standalone_toolchain.py --arch arm --stl=libc++ --install-dir /home/(your computer name or user name)/my_toolchains/arm --force

For arm64: 

python make_standalone_toolchain.py --arch arm64 --stl=libc++ --install-dir /home/(your computer name or user name)/my_toolchains/arm64 --force

For x86: 

python make_standalone_toolchain.py --arch x86 --stl=libc++ --install-dir /home/(your computer name or user name)/my_toolchains/x86 --force

For x86_64: 

python make_standalone_toolchain.py --arch x86_64 --stl=libc++ --install-dir /home/(your computer name or user name)/my_toolchains/x86_64 --force

For arm: 

python make_standalone_toolchain.py --arch arm --api 24 --stl=libc++ --install-dir /home/(your computer name or user name)/my_toolchains/arm --force

注意:将上面的地址/home/(your computer name or user name)  修改为自己本机的实际地址

 ⑥ 编写编译各个架构对应脚本,并运行编译得到相应的so动态库

建议先在windows中编写,然后发送给linux,文件放置在ffmpeg目录下(/root/programfiles/ffmpeg-5.1.6)

注意:下面脚本中的TOOLCHAIN变量对应的地址更改为第五步中tool-chains生成的地址,自行修改

arm64脚本,build_android_arm64.sh (执行该脚本前,先生成第五步中的arm64 toolchains)

#!/bin/bash

TOOLCHAIN=/root/programfiles/my_toolchains/arm64
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
rm -f $(pwd)/compat/strtod.o
function build_one
{
./configure --prefix=$PREFIX --enable-shared --disable-static --enable-protocol=file --enable-pic --enable-small --disable-programs --disable-doc --disable-symver --target-os=android --enable-cross-compile --cross-prefix=$CROSS_PREFIX --extra-cflags="-Os -fpic $ADDI_CFLAGS" --extra-ldflags="$ADDI_LDFLAGS" --sysroot=$TOOLCHAIN/sysroot $ADDITIONAL_CONFIG_FLAG
make clean
make -j2
make install
}

CPU=arm64-v8a
mkdir -p $(pwd)/android/$CPU
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-march=armv8-a"
ADDI_LDFLAGS="-L$TOOLCHAIN/sysroot/usr/lib"
ADDITIONAL_CONFIG_FLAG="--arch=aarch64 --enable-yasm"
build_one

armv7a脚本,build_android_armv7a.sh (执行该脚本前,先生成第五步中的armv7a toolchains)

#!/bin/bash
 
TOOLCHAIN=/root/programfiles/my_toolchains/arm
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
rm -f $(pwd)/compat/strtod.o
function build_one
{
./configure --prefix=$PREFIX --enable-shared --disable-static --enable-protocol=file --enable-pic --enable-small --disable-programs --disable-doc --disable-symver --target-os=android --enable-cross-compile --cross-prefix=$CROSS_PREFIX --extra-cflags="-Os -fpic $ADDI_CFLAGS" --extra-ldflags="$ADDI_LDFLAGS" --sysroot=$TOOLCHAIN/sysroot $ADDITIONAL_CONFIG_FLAG
make clean
make -j8
make install
}
 
CPU=armeabi-v7a
mkdir -p $(pwd)/android/$CPU
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm -march=armv7-a -mfloat-abi=softfp -mthumb -mfpu=vfpv3-d16 -mtune=cortex-a8"
ADDI_LDFLAGS="-marm -march=armv7-a -Wl,--fix-cortex-a8"
ADDITIONAL_CONFIG_FLAG="--arch=arm --disable-asm"
build_one

x86脚本,build_android_x86.sh  (执行该脚本前,先生成第五步中的x86 和 arm toolchains)

#!/bin/bash

TOOLCHAIN=/root/programfiles/my_toolchains/x86
CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
rm -f $(pwd)/compat/strtod.o
function build_one
{
./configure --prefix=$PREFIX --enable-shared --disable-static --enable-protocol=file --enable-pic --enable-small --disable-programs --disable-doc --disable-symver --target-os=android --enable-cross-compile --cross-prefix=$CROSS_PREFIX --extra-cflags="-Os -fpic $ADDI_CFLAGS" --extra-ldflags="$ADDI_LDFLAGS" --sysroot=$TOOLCHAIN/sysroot $ADDITIONAL_CONFIG_FLAG
make clean
make -j2
make install
}

CPU=x86
mkdir -p $(pwd)/android/$CPU
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
ADDI_LDFLAGS=""
ADDITIONAL_CONFIG_FLAG="--arch=x86 --cpu=i686 --enable-x86asm"
build_one

x86_64脚本, build_android_x86_64.sh  (执行该脚本前,先生成第五步中的x86_64 和 arm toolchains)

#!/bin/bash

TOOLCHAIN=/root/programfiles/my_toolchains/x86_64
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
rm -f $(pwd)/compat/strtod.o
function build_one
{
./configure --prefix=$PREFIX --enable-shared --disable-static --enable-protocol=file --enable-pic --enable-small --disable-programs --disable-doc --disable-symver --target-os=android --enable-cross-compile --cross-prefix=$CROSS_PREFIX --extra-cflags="-Os -fpic $ADDI_CFLAGS" --extra-ldflags="$ADDI_LDFLAGS" --sysroot=$TOOLCHAIN/sysroot $ADDITIONAL_CONFIG_FLAG
make clean
make -j2
make install
}

CPU=x86_64
mkdir -p $(pwd)/android/$CPU
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-march=x86-64"
ADDI_LDFLAGS=""
ADDITIONAL_CONFIG_FLAG="--arch=x86_64 --enable-x86asm"
build_one

其它架构的脚本文件参考FFmpeg-3.4-Android/build_arm64-v8a.sh at master · ejoker88/FFmpeg-3.4-Android · GitHub

⑦执行⑥编写的脚本文件进行编译

  在ffmpeg目录下执行

chmod +x 脚本文件名
sed 's/\r//' -i 脚本文件名
./脚本文件名字

编译过程中可能会出现各种各样的错误,通过查询 /ffmpeg-5.1.6/ffbuild/config.log来排查问题

 检查动态库是否生成(生成结果在/root/programfiles/ffmpeg-5.1.6/android/arm64-v8a)

点进lib里有以下几个文件则编译成功 

 

以上内容参考文章:

         ffmpeg编译Android版本的armeabi-v7a和arm64-v8a_csdn ffmpeg armeabi-CSDN博客

      Tutorial : FFmpeg 3.4 for Android · ejoker88/FFmpeg-3.4-Android Wiki · GitHub 

2.集成到Android项目, 通过JNI调用FFmpeg:

参考下面这个博文后半段做的

Android使用FFmpeg的API库—FFmpeg API教程 · FFmpeg原理

注意注意注意:我编译成功后,apk中怎么也找不到打包的FFmpeg so动态库,搞了半天才发现,虚拟机用的是x86_64架构,我只编译了armv7a和arm64,所以打包不进去。建议你们做的时候把armv7a、arm64、x86和x86_64都编译好放进项目中,这样兼容性也更高

补充:在跟着上面博文做的时候编译出现了好多问题,一一排查之后,对该博文的补充如下

我的项目结构

CMakeLists.txt文件


cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("myapplication")

# 1、定义so库和头文件所在目录
set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR})

# 2、添加头文件目录
include_directories(${ffmpeg_head_dir}/include)


# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

# 3、添加ffmpeg相关的so库,该库的源文件来自${ffmpeg_lib_dir}/libavutil.so
# 导入FFmpeg所有依赖库
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${ffmpeg_lib_dir}/libavcodec.so)

add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${ffmpeg_lib_dir}/libavformat.so)

add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${ffmpeg_lib_dir}/libavutil.so)

add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${ffmpeg_lib_dir}/libswresample.so)

# 系统自动生成的自动库,该库的源文件是myapplication.cpp
add_library( # Sets the name of the library.
        myapplication

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        myapplication.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
# 链接库文件
target_link_libraries(myapplication
        avformat
        avcodec
        avutil
        swresample
        # 其他FFmpeg依赖库按需添加
        ${log-lib}
        )

build.gradle文件(不完全)

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdk 28
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"


        sourceSets.main {
            jniLibs.srcDirs = ['jniLibs']// This prevents the auto generation of Android.mk
        }




    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.18.1'
        }
    }
    ndkVersion '21.4.7075529'
}

最主要的是这两块文件,其它文件参考博文里写的就可以


PS:关于如何批量生成FFmpeg的JNI代码,查阅我主页的相关博文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值