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閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容