ffmpeg入门(二)

背景

上一篇文章介绍了ffmpeg在windows平台的使用,这一篇以Android 平台为例,介绍下ffmpeg的编译,以及在Android应用层如何使用。

编译ffmpeg

环境

为保证统一的环境,本文使用wmware虚拟机进行编译。
我本地有两套linux 环境,kali linux和r0env,其实只要普通的ubuntu 就能支持编译,推荐使用kali,很多组件免安装。

下载

wget http://ffmpeg.org/releases/ffmpeg-5.1.2.tar.xz

解压

tar -xvJf ffmpeg-5.1.2.tar.xz

查看编译配置

./configure --help

编译

对于Android 环境,需要生成动态链接库,需要依赖Android ndk,
下载ndk,粘贴到虚拟机目录,

wget https://dl.google.com/android/repository/android-ndk-r25b-linux.zip

解压ndk到该目录

unzip android-ndk-r25b-linux.zip

修改configure文件的路径配置,注意空格是必须的

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根目录下新建android_build.sh,执行

chmod 777 android_build.sh

为避免出现bad interpreter: /bin/bash^M: 没有那个文件或目录问题,执行

sed -i 's/\r$//' android_build.sh

格式处理之后,再执行

./android_build.sh

android_build.sh 的 具体配置如下:

#!/bin/bash

# 如果编译的时候,编译日志输出,同时最后成功,但是没有找到文件夹,说明编译是失败的,


# 设置你自己的NDK位置
 NDK_HOME=/root/ndk/android-ndk-r25b
# 设置你自己的平台,Mac上的是 darwin-x86_64 ,linux上的是 linux-x86_64,linux上的是 windows-x86_64
 NDK_HOST_PLATFORM=linux-x86_64
# 动态库 so 编译后的输出文件夹
 PREFIX=$(pwd)/android-build-out

# 删除 so 的输出文件夹
#  rm -rf $PREFIX

# gcc的编译选项拓展参数
# ADDI_CFLAGS="-marm"

# 编译的库应用于什么系统,android 的可以有 linux、android
 TARGET_OS=android

# 下面的参数都是需要变化的
# 处理器类型(有 arm、aarch64、x86、x86_64)
 # ARCH_TYPE=arm
 ARCH_TYPE=""
# CPU架构类型
 # CPU_TYPE=armv7-a
 CPU_TYPE=""

# ndk 的cpu架构对应的文件夹,就是(${NDK_HOME}/toolchains/llvm)这部分
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
 NDK_TOOLCHAINS_CPU_TYPE_FOLDER=""
# ndk 的bin目录下以指定字符串开头的文件,后面的 - 表示通配
# NDK_TOOLCHAINS_BIN_FOLDER_FILES="arm-linux-androideabi-"
 NDK_TOOLCHAINS_BIN_FOLDER_FILES=""
 TOOLCHAIN=""
# Android交叉编译的工具链
# /root/ndk/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang
# ${NDK_HOME}/toolchains/llvm/prebuilt/${NDK_HOST_PLATFORM}/bin/aarch64-linux-android21-
# CROSS_PREFIX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}"
CROSS_PREFIX=""
# SYSROOT 指向的目录,即 ${NDK_HOME}/platforms 目录下的具体平台文件夹的cpu架构文件夹
# SYSROOT_PLATFORM_AND_ARCH=arm-linux-androideabi
SYSROOT_PLATFORM_AND_ARCH=""

# 交叉编译工具的根路径(android平台使用的库和头文件的路径)
# /root/ndk/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android
 # SYSROOT="${TOOLCHAIN}/sysroot/"
 # usr/lib/${SYSROOT_PLATFORM_AND_ARCH}/
 SYSROOT=""

 # android 里的 abi 类型[例如:armeabi, arm64-v8a, armeabi-v7a, x86, x86_64]
 ANDROID_ABI=""

# ffmpeg 的一些常见选项
 COMMON_OPTIONS="
    --target-os=${TARGET_OS} \
    --enable-static \
    --enable-shared \
    --enable-small \
    --disable-programs \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-doc \
    --disable-symver \
    --disable-asm \
    --enable-cross-compile \
    --enable-pic \
    --disable-neon \
    --disable-debug \
    "
CC=""
CXX=""
NM=""
STRIP=""
# 更新需要拼接的参数(不重新调用一下,最新值就没有替换上去)
update_param() {
    TOOLCHAIN="${NDK_HOME}/toolchains/${NDK_TOOLCHAINS_CPU_TYPE_FOLDER}/prebuilt/${NDK_HOST_PLATFORM}"
    CROSS_PREFIX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-"
    SYSROOT="${TOOLCHAIN}/sysroot"
    CC="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang"
    CXX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang++"
    LD="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang"
    AR="${TOOLCHAIN}/bin/llvm-ar"
    NM="${TOOLCHAIN}/bin/llvm-nm"
    STRIP="${TOOLCHAIN}/bin/llvm-strip"
    RUNLIB="${TOOLCHAIN}/bin/llvm-ranlib"
    CC_L="${TOOLCHAIN}/lib64/clang/14.0.6/lib/linux"
    SYSROOT_L="${SYSROOT}/usr/lib/${SYSROOT_PLATFORM_AND_ARCH}"
    LD_L="${TOOLCHAIN}/bin/ld.lld"
}

# 声明一个编译 Android 的方法
build_android() 
{
    update_param

    ./configure \
    --prefix=${PREFIX} \
    --libdir=${PREFIX}/libs/${ANDROID_ABI} \
    --incdir=${PREFIX}/includes \
    --pkgconfigdir=${PREFIX}/pkgconfig/${ANDROID_ABI} \
    --arch=${ARCH_TYPE} \
    --cpu=${CPU_TYPE} \
    --cross-prefix=${CROSS_PREFIX} \
    --sysroot=${SYSROOT} \
    --extra-cflags="-Os -fpic -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOA -DVK_ENABLE_BETA_EXTENSIONS=0" \
    --extra-ldflags="-lc -ldl -lm -lz -llog -lgcc -L${PREFIX}/libs" \
    --extra-ldexeflags=-pie \
    --cc=${CC} \
    --cxx=${CXX} \
    --ld=${LD} \
    --ar=${AR} \
    --ranlib=${RUNLIB} \
    --nm=${NM} \
    --strip=${STRIP} \
    ${COMMON_OPTIONS} \
    ${ADDITIONAL_CONFIGURE_FLAG}
    
    make clean
    make -j8
    make install
    echo "${ANDROID_ABI} install完成了"
    
    
    # ${LD_L} -L${PREFIX}/libs/${ANDROID_ABI} -L${CC_L} \
    # -rpath-link=${SYSROOT_L} -L${SYSROOT_L} -soname libffmpeg.so \
    # -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o ${PREFIX}/libffmpeg.so \
    # -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswscale -lswresample \
    # -lc -ldl -lm -lz -llog \
    # --dynamic-linker=/system/bin/linker

    # echo "${ANDROID_ABI} 编译完成了"
}

#编译 armeabi-v7a
ANDROID_ABI=armeabi-v7a
ARCH_TYPE=arm
CPU_TYPE=armv7-a
NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
API=21
NDK_TOOLCHAINS_BIN_FOLDER_FILES=armv7a-linux-androideabi${API}
SYSROOT_PLATFORM_AND_ARCH=arm-linux-androideabi/${API}
build_android

# # 编译 arm64-v8a
# ANDROID_ABI=arm64-v8a
# ARCH_TYPE=aarch64
# CPU_TYPE=armv8-a
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
# API=21
# NDK_TOOLCHAINS_BIN_FOLDER_FILES=aarch64-linux-android${API}
# SYSROOT_PLATFORM_AND_ARCH=aarch64-linux-android/${API}
# build_android

# # 编译 x86
# ANDROID_ABI=x86
# ARCH_TYPE=x86
# CPU_TYPE=i686
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
# API=21
# NDK_TOOLCHAINS_BIN_FOLDER_FILES=i686-linux-android${API}
# SYSROOT_PLATFORM_AND_ARCH=x86_64-linux-android/${API}
# build_android

# # 编译 x86_64
# ANDROID_ABI=x86_64
# ARCH_TYPE=x86_64
# CPU_TYPE=x86_64
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
# API=21
# NDK_TOOLCHAINS_BIN_FOLDER_FILES=x86_64-linux-android${API}
# SYSROOT_PLATFORM_AND_ARCH=i686-linux-android/${API}
# build_android

编译之后的产物:每种架构都包含了 libavcodec、libavdevice、libavfilter、libavformat、libavutil、libswscale、libswresample。


1674385483168.png

这样多个so不便于项目管理,可以通过改进脚本去只生成一个so。

1674466957714.png

遇到的问题:
1.clang is unable to create an executable file. C compiler test failed,可以查看ffbuild 目录下的config.log文件,该文件记录了编译日志
2.include/vulkan/vulkan.h:89:10: fatal error: 'vulkan_beta.h' file not found,找到ndk发现该文件确实不存在,解决办法:
配置extra-cflags时添加 -DVK_ENABLE_BETA_EXTENSIONS=0,
或者禁用vulkan: --disable-vulkan
3.注意配置nm和strip
4.编译static的时候,遇到install-libavdevice-static报错,发现ranlib的配置缺少空格

gcc版本的ndk编译ffmpeg的编译可以参考以下文章:http://www.ihubin.com/blog/android-ffmpeg-demo-3/

编译脚本总结

以下是编译脚本相关参数的含义,参考了该文章https://juejin.cn/post/7149468268674154510#heading-3

--disable-static : 不构建静态库
--enable-shared : 编译生成动态库
--enable-small : 降低库体积
--disable-programs : 不构建命令行程序(指ffmpeg、ffplay以及ffprobe)
--disable-ffmpeg : 不构建ffmpeg程序
--disable-ffplay : 不构建ffplay程序
--disable-ffprobe : 不构建ffprobe程序
--disable-doc : 不产生文档
--disable-htmlpages : 不产生 HTML类型的文档
--disable-manpages : 不产生 manpage类型的文档
--disable-podpages : 不产生 POD 类型的文档
--disable-txtpages : 不产生 text 类型的文档
--enable-cross-compile : 开启交叉编译
--enable-pic : 生成位置无关代码
--disable-asm:禁止所有汇编优化
--disable-neon:禁止NEON优化
--disable-debug:禁止调试符号,脚本中默认开启。

获取生成的文件

打包out目录
zip -r ffmpeg_out.zip .
在虚拟机的可视化界面,复制生成的 ffmpeg_out.zip,粘贴到电脑中。
ffmpeg编译之后的是so,并没有自带的java层调用库,所以我们使用的时候可以自己写一个类库,或者用网上的写好第三方库。

Android调用

创建Android C++项目

image.png

引用so库,编写c调用类

在 app/src/main/ 目录下,新建文件夹,并命名为 jniLibs ,接着,在 jniLibs 目录下,新建 armeabi-v7a 目录


image.png

在 cpp 目录下,新建 ffmpeg 目录,然后把编译时生成的 includes 文件粘贴进来。


1674523456106.png

修改native-lib.cpp

#include <jni.h>
#include <string>
#include <unistd.h>


extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavcodec/jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_ffmpeglib_MainActivity_ffmpegInfo(JNIEnv *env, jobject  /* this */) {
    std::string ffmpeg = avcodec_configuration();
    return env->NewStringUTF(ffmpeg.c_str());
}
}

遇到的问题:
No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi
配置ndk路径即可

编写java调用类

1674629487426.png

执行程序,可以看到编译信息


device-2023-01-25-145303.png

编译jni生成aar

将项目改造成app和lib两个module,将刚才的代码移植到module中,可以编译生成aar,包含lib中的so和jni链接生成的动态库。


image.png

主项目引用或者发布到私服,即可在项目正常使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容