編譯環境
這次采用的交叉編譯環境是:Macos 13.2 + GCC + Cmake + NDK 21 編譯的第三方庫:x264 + mp3lame + fdk-aac + opencore-amr
交叉編譯
檢測FFmpeg配置是否支持MediaCodec的編碼,確實是支持的,不僅支持
h264
還支持
h265
編碼,結果如下:
./configure --list-encoders | grep mediacodec
h264_mediacodec pcm_f64be wmav1
hevc_mediacodec pcm_s24le_planar zlib
在FFmpeg 6.0上不需要再開啟我們MediaCodec的硬體加速了(是哪個版本取消的,我也不知道 😊),可硬體加速的列表如下:
./configure --list-hwaccels
av1_d3d11va hevc_d3d11va mpeg2_nvdec vp8_nvdec
av1_d3d11va2 hevc_d3d11va2 mpeg2_vaapi vp8_vaapi
av1_dxva2 hevc_dxva2 mpeg2_vdpau vp9_d3d11va
av1_nvdec hevc_nvdec mpeg2_videotoolbox vp9_d3d11va2
av1_vaapi hevc_vaapi mpeg4_nvdec vp9_dxva2
av1_vdpau hevc_vdpau mpeg4_vaapi vp9_nvdec
h263_vaapi hevc_videotoolbox mpeg4_vdpau vp9_vaapi
h263_videotoolbox mjpeg_nvdec mpeg4_videotoolbox vp9_vdpau
h264_d3d11va mjpeg_vaapi prores_videotoolbox vp9_videotoolbox
h264_d3d11va2 mpeg1_nvdec vc1_d3d11va wmv3_d3d11va
h264_dxva2 mpeg1_vdpau vc1_d3d11va2 wmv3_d3d11va2
h264_nvdec mpeg1_videotoolbox vc1_dxva2 wmv3_dxva2
h264_vaapi mpeg2_d3d11va vc1_nvdec wmv3_nvdec
h264_vdpau mpeg2_d3d11va2 vc1_vaapi wmv3_vaapi
h264_videotoolbox mpeg2_dxva2 vc1_vdpau wmv3_vdpau
_nvdec
結尾的是NVIDIA顯卡解碼硬體加速
_videotoolbox
結尾的是蘋果ios和Macos多媒體框架硬體加速
./configure \
--prefix=$PREFIX \ # 編譯之後的保存位置
--disable-encoders \ # 禁用所有編碼器
--disable-decoders \ # 禁用所有解碼器
--disable-doc \ # 禁用文件
--disable-htmlpages \
--disable-manpages \
--disable-podpages \
--disable-txtpages \
--disable-ffmpeg \ # 禁用 ffmpeg 可執行程式構建
--disable-ffplay \ # 禁用 ffplay 可執行程式構建
--disable-ffprobe \ # 禁用 ffprobe 可執行程式構建
--disable-symver \
--disable-shared \ # 禁用共享連結
--disable-asm \
--disable-x86asm \
--disable-avdevice \ # 禁用libavdevice構建
--disable-postproc \ # 禁用libpostproc構建
--disable-cuvid \ # 禁用Nvidia Cuvid
--disable-nvenc \ # 禁用Nvidia視訊編碼
--disable-vaapi \ # 禁用視訊加速API程式碼(Unix/Intel)
--disable-vdpau \ # 禁用禁用Nvidia解碼和API程式碼(Unix)
--disable-videotoolbox \ # 禁用ios和macos的多媒體處理框架videotoolbox
--disable-audiotoolbox \ # 禁用ios和macos的音訊處理框架audiotoolbox
--disable-appkit \ # 禁用蘋果 appkit framework
--disable-avfoundation \ 禁用蘋果 avfoundation framework
--enable-static \ # 啟用靜態連結
--enable-nonfree \ # 啟用非免費的元件
--enable-gpl \ # 啟用公共授權元件
--enable-version3 \
--enable-pic \
--enable-pthreads \ # 啟用多執行緒
--enable-encoder=bmp \
--enable-encoder=flv \
--enable-encoder=gif \
--enable-encoder=mpeg4 \
--enable-encoder=rawvideo \
--enable-encoder=png \
--enable-encoder=mjpeg \
--enable-encoder=yuv4 \
--enable-encoder=aac \
--enable-encoder=pcm_s16le \
--enable-encoder=subrip \
--enable-encoder=text \
--enable-encoder=srt \
--enable-libx264 \ # 啟用支持h264
--enable-encoder=libx264 \
--enable-libfdk-aac \ # 啟用支持fdk-aac
--enable-encoder=libfdk_aac \
--enable-decoder=libfdk_aac \
--enable-libmp3lame \ # 啟用支持mp3lame
--enable-encoder=libmp3lame \
--enable-libopencore-amrnb \ # 啟用支持opencore-amrnb
--enable-encoder=libopencore_amrnb \
--enable-decoder=libopencore_amrnb \
--enable-libopencore-amrwb \ # 啟用支持opencore-amrwb
--enable-decoder=libopencore_amrwb \
--enable-mediacodec \ # 啟用支持mediacodec
--enable-encoder=h264_mediacodec \
--enable-encoder=hevc_mediacodec \
--enable-decoder=h264_mediacodec \
--enable-decoder=hevc_mediacodec \
--enable-decoder=mpeg4_mediacodec \
--enable-decoder=vp8_mediacodec \
--enable-decoder=vp9_mediacodec \
--enable-decoder=bmp \
--enable-decoder=flv \
--enable-decoder=gif \
--enable-decoder=mpeg4 \
--enable-decoder=rawvideo \
--enable-decoder=h264 \
--enable-decoder=png \
--enable-decoder=mjpeg \
--enable-decoder=yuv4 \
--enable-decoder=aac \
--enable-decoder=aac_latm \
--enable-decoder=pcm_s16le \
--enable-decoder=mp3 \
--enable-decoder=flac \
--enable-decoder=srt \
--enable-decoder=xsub \
--enable-small \
--enable-neon \
--enable-hwaccels \
--enable-jni \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$COMPILE_ARCH \
--cpu=$ANDROID_CUP \
--cc=$CC \
--cxx=$CXX \
--nm=$NM \
--ar=$AR \
--as=$AS \
--strip=$STRIP \
--ranlib=$RANLIB \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"
我這裏編譯的是靜態連結庫也就是
.a
檔,所以我禁用了共享連結庫,如果你編譯
.so
檔,那麽就修改如下:
--disable-static \ # 禁用靜態連結
--enable-shared \ # 啟用共享連結
因為我們不需要使用到avdevice和postproc,所以我選擇禁用,這樣最終只會生成6個
.a
檔
--disable-avdevice \ # 禁用libavdevice構建
--disable-postproc \ # 禁用libpostproc構建
透過構建指令碼的方式我們將上面的
configure
和
make
命令做成一個
shell
指令碼,如果需要可以在評論區留言。
開始編譯
然後我們就進入FFmpeg 6.0目錄,透過
terminal
執行我們的
shell
指令碼。編譯的時候只遇到了這麽一個問題:
x264 pkg-config沒找到,報錯的內容如下:
➜ ffmpeg ./darwin_android_lite.sh
compiling ffmpeg for armeabi-v7a
ERROR: x264 not found using pkg-config
If you think configure made a mistake, make sure you are using the latest
version from Git. If the latest version fails, report the problem to the
[email protected] mailing list or IRC #ffmpeg on irc.libera.chat.
Include the log file "ffbuild/config.log" produced by configure as this will help
solve the problem.
這時就需要我們在我們的配置裏面加上如下內容:
--pkg-config="pkg-config --static"
這個就表示需要我們指定pkg-config位置
export PKG_CONFIG_PATH=$(pwd)/../x264/android/$ANDROID_ABI/lib/pkgconfig
加上如上內容,基本在檢查配置的環節基本就不會有問題了,然後我們繼續執行就可以得到一個編譯結果。
.h
的表頭檔。
.so
或者
.a
檔以及pkg-config。
ld
工具將所有的
.a
合成了一個
libffmpeg-org.so
的檔,就不需要對上面6個進行分別載入了,只需要載入一個就行。
System.loadLibrary("ffmpeg-org")
那多個合成一個的配置,也在我們的
shell
指令碼中,大致就是如下:
$TOOLCHAIN_EXECUTE/${CROSS_COMPILE}ld \
-rpath-link=$SYSROOT/usr/lib/$HOST/$API \
-L$SYSROOT/usr/lib/$HOST/$API \
-L$TOOLCHAIN/lib/gcc/$HOST/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 \
$X264_LIB/libx264.a \
$FDK_LIB/libfdk-aac.a \
$LAME_LIB/libmp3lame.a \
$AMR_LIB/libopencore-amrnb.a \
$AMR_LIB/libopencore-amrwb.a \
-lc -lm -lz -ldl -llog -landroid --dynamic-linker=/system/bin/linker \
$TOOLCHAIN/lib/gcc/$HOST/4.9.x/libgcc_real.a || exit 1
上方配置有一個需要註意的點,我們一定要使用
libgcc_real.a
,而不是
libgcc.a
,這裏被誤導了好久。以前一直使用的是
libgcc.a
,也沒有問題,但這次使用NDK21就出現了這個問題。
到這裏我們的交叉編譯就完成了,那我們接下來就看看怎麽使用。
使用
-
我們新建一個native計畫,然後將我們編譯好的
libffmpeg-org.so
放置於jniLibs目錄下。
-
將我們的之前的
libffmpeg-org.org
加入我們的CmakeLists.txt的配置中
add_library(
ffmpeg-org
SHARED
IMPORTED
)
SET_TARGET_PROPERTIES(
ffmpeg-org
PROPERTIES IMPORTED_LOCATION
${PROJECT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libffmpeg-org.so
)
include_directories(ffmpeg) # 表頭檔相對路徑
-
新建我們自己的
ffmpeg-cmd.cpp
並加入CmakeLists.txt
add_library(
ffmpeg-cmd
SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
ffmpeg-cmd.cpp)
- 最後動態連結所有內容,完整CmakeLists.txt的配置如下
cmake_minimum_required(VERSION 3.22.1)
project("ffmpeg")
add_library(
ffmpeg-cmd
SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
ffmpeg-cmd.cpp)
add_library(
ffmpeg-org
SHARED
IMPORTED
)
SET_TARGET_PROPERTIES(
ffmpeg-org
PROPERTIES IMPORTED_LOCATION
${PROJECT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libffmpeg-org.so
)
include_directories(ffmpeg) # 表頭檔相對路徑
target_link_libraries(
ffmpeg-cmd
ffmpeg-org
# List libraries link to the target library
android
log)
-
新建我們的FFmpegCmd類,將
so
動態載入,並新增一個獲取所有編解碼器的方法。
class FFmpegCmd {
init {
System.loadLibrary("ffmpeg-org")
System.loadLibrary("ffmpeg-cmd")
}
externalfun getSupportCodecs():String?
}
-
編輯我們的
ffmpeg-cmd.cpp
,在其內容關聯getSupportCodecs
方法
extern"C"JNIEXPORT jstring JNICALL
Java_com_adaiyuns_ffmpeg_jni_FFmpegCmd_getSupportCodecs(JNIEnv *env, jobject thiz){
returnnullptr;
}
這裏需要註意上面的函式名,函式名為FFmpegCmd的全路徑。那我們就實作一個獲取所有的編解碼器
#include<jni.h>#include<string>extern"C"{
#include"ffmpeg/libavcodec/avcodec.h"
}
extern"C"JNIEXPORT jstring JNICALL
Java_com_adaiyuns_ffmpeg_jni_FFmpegCmd_getSupportCodecs(JNIEnv *env, jobject thiz){
// 定義臨時緩存區char info[20000] = {0};
// 初始化編碼器遍歷器void *opaque = NULL;
const AVCodec *avcodec = av_codec_iterate(&opaque);
// 遍歷所有支持的編碼器while (avcodec != NULL) {
sprintf(info, "%s%s,", info, avcodec->name);
avcodec = av_codec_iterate(&opaque);
}
return env->NewStringUTF(info);
}
可以看一下執行之後的效果
從圖上可以看到我們的ffmpeg已經支持
h264_mediacodec
和
hevc_mediacodec
。到此基本就完成了簡單的整合。✿✿ヽ(°▽°)ノ✿
開源庫
基於簡單的音視訊處理,我也開發了FFmpeg的開源庫 FFmpegCommand ,大致支持如下特色功能: 開源庫中提供了常用的命令和方法 FFmpegUtils ,可直接使用其中的方法。MainScope().launch(Dispatchers.IO) {
FFmpegCommand.runCmd(FFmpegUtils.transformAudio(audioPath, targetPath), callback("音訊轉碼完成", targetPath))
}
也可以自訂命令,以下是一個自訂使用
MediaCodec
進行解碼、編碼、轉格式的例子:
// shell 命令: ffmpeg -y -c:v h264_mediacodec -i inputPath -c:v h264_mediacodec outputPathval command = CommandParams()
.append("-c:v") // 設定解碼器
.append("h264_mediacodec")
.append("-i")
.append(inputPath)
.append("-b") // 寫死一般需要設定視訊的位元率(bitrate)
.append("1500k")
.append("-c:v") // 設定編碼器
.append("h264_mediacodec")
.append(outputPath)
.get()
MainScope().launch(Dispatchers.IO) {
FFmpegCommand.runCmd(command, callback("格式轉換成功", targetPath))
}
需要註意:
MediaCodec
進行編碼的時候,必須同時配置
MediaCodec
解碼,如上例子所示,不然會造成失敗!!!
h264_mediacodec
,H265的編解碼器是
hevc_mediacodec
。同時可以使用H264解碼和H265編碼。
CommandParams
構建我們的命令參數,這樣能保證參數不被路徑中空格影響,導致命令執行不成功。
h264_mediacodec
和
libx264
進行轉碼流程,得出如下結果,所以小夥伴們快使用
MediaCodec
進行編碼吧~~
編解器 | 耗時 |
---|---|
h264_mediacodec | 4507毫秒 ≈ 4.5秒 |
libx264 | 57264毫秒 ≈ 57.2秒 |
原文連結: https://juejin.cn/post/7297838901090648118
資源連結: https://github.com/AnJoiner/FFmpegCommand
-- END -- 進技術交流群, 掃碼添加我的微信:Byte-Flow
獲取相關資料和源碼
學習音視訊、OpenGL ES、Vulkan 、Metal、影像濾鏡、視訊特效及相關渲染技術的付費社群,面試指導,1v1 簡歷服務,職業規劃。 我的付費社群 推薦: