一、前言
编译ffmpeg是学习ffmpeg的第一步,本篇博客的环境是mac os 上 NDK21 版本编译ffmpeg。之所以写这篇博客,主要是因为去年编译的时候一切顺利进行,而今年电脑CPU烧了(使用电脑设备要特别小心电量问题,电量太低容易造成cpu发热)之后,所有数据都没了,所以都是新环境,和去年一样的脚本,今年编译却失败了。
注意:本文中b4 中的b代表6,不然发不出去,一直提示敏感词
二、失败原因
失败是成功之母,造成编译失败的原因是,所选的sysroot和toolchains出了问题
工具链选择了:
/Users/${USER}/Library/Android/sdk/ndk/21.0.6113669/toolchains/arm-linux-androideabi-4.9,
sysroot选择了:
/Users/${USER}/Library/Android/sdk/ndk/21.0.6113669/sysroot
现象是:
【1】部分头文件找不到,连ndk 核心jni文件也找不到
【2】clang、clang++找不到
以上问题莫名其妙,希望有了解的可以指教一下
三、成功方案
成功方案是参考网上博客的,使用了llvm,发现成功了。
#!/bin/bash
NDK=/Users/${USER}/Library/Android/sdk/ndk/21.0.6113669
TOOLCHAIN_ROOT_DIR=darwin-x86_b4
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$TOOLCHAIN_ROOT_DIR/
API=18
#要编译的ffmpeg内容方法
function build_android {
make clean
echo "Compiling FFmpeg for $CPU"
./configure \
--prefix=$PREFIX \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
--enable-gpl \
--enable-shared \
--enable-runtime-cpudetect \
--enable-small \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-jni \
--enable-mediacodec \
--enable-h2b4_mediacodec \
--enable-hwaccels_mediacodec \
--disable-debug \
--disable-hwaccels \
--disable-postproc \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-avdevice \
--disable-doc \
--disable-symver \
--disable-avdevice \
--disable-stripping \
$ADDITIONAL_CONFIGURE_FLAG
make j4
make install
echo "The Compilation of FFmpeg for $CPU is completed"
}
#armv7-a
ARCH=arm
CPU=armv7-a
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/platforms/android-${API}/arch-arm
# SYSROOT=$NDK/toolchains/llvm/prebuilt/$TOOLCHAIN_ROOT_DIR/sysroot #这个也是可以的,大概率和和ndk版本一致,如果是21,则不支持之前的版本
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS=" -DANDROID -mfloat-abi=softfp -mfpu=vfp -marm - march=$CPU "
# OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm - march=$CPU "
build_android
四,为ExoPlayer 编译ffmpeg
exoplayer中仅仅支持ffmpeg去解码音频操作,实际上对ffmpeg支持不完美,但是我们也可以自己去实现视频相关的FfmpegVideoDecoder,当然前提是先学会编译。
步骤如下:
【1】下载ExoPlayer 源码
git clone https://github.com/google/ExoPlayer.git
【2】进入ExoPlayer项目,找到ffmpeg模块,切换到下面jni目录
ExoPlayer/extensions/ffmpeg/src/main/jni
【3】在jni目录下载ffmpeg
git clone git://source.ffmpeg.org/ffmpeg
cd ffmpeg
git checkout release/4.2
【4】返回到jni目录,修改构建脚本 build_ffmpeg.sh ,内容修改如下即可
#!/bin/bash
set -eu
FFMPEG_MODULE_PATH=../
NDK_PATH=/Users/${USER}/Library/Android/sdk/ndk/21.0.6113669
HOST_PLATFORM=darwin-x86_b4
ENABLED_DECODERS=(mp3 aac ac3 flac alac)
JOBS=$(nproc 2> /dev/null || sysctl -n hw.ncpu 2> /dev/null || echo 4)
echo "Using $JOBS jobs for make"
COMMON_OPTIONS="
--target-os=android
--enable-static
--disable-shared
--disable-doc
--disable-programs
--disable-everything
--disable-avdevice
--disable-avformat
--disable-swscale
--disable-postproc
--disable-avfilter
--disable-symver
--disable-avresample
--enable-swresample
--extra-ldexeflags=-pie
"
TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin"
for decoder in "${ENABLED_DECODERS[@]}"
do
COMMON_OPTIONS="${COMMON_OPTIONS} --enable-decoder=${decoder}"
done
cd "${FFMPEG_MODULE_PATH}/jni/ffmpeg"
./configure \
--libdir=android-libs/armeabi-v7a \
--arch=arm \
--cpu=armv7-a \
--cross-prefix="${TOOLCHAIN_PREFIX}/armv7a-linux-androideabi16-" \
--nm="${TOOLCHAIN_PREFIX}/llvm-nm" \
--ar="${TOOLCHAIN_PREFIX}/llvm-ar" \
--ranlib="${TOOLCHAIN_PREFIX}/llvm-ranlib" \
--strip="${TOOLCHAIN_PREFIX}/llvm-strip" \
--extra-cflags="-march=armv7-a -mfloat-abi=softfp" \
--extra-ldflags="-Wl,--fix-cortex-a8" \
${COMMON_OPTIONS}
make -j$JOBS
make install-libs
make clean
./configure \
--libdir=android-libs/armb4-v8a \
--arch=aarchb4 \
--cpu=armv8-a \
--cross-prefix="${TOOLCHAIN_PREFIX}/aarchb4-linux-android21-" \
--nm="${TOOLCHAIN_PREFIX}/llvm-nm" \
--ar="${TOOLCHAIN_PREFIX}/llvm-ar" \
--ranlib="${TOOLCHAIN_PREFIX}/llvm-ranlib" \
--strip="${TOOLCHAIN_PREFIX}/llvm-strip" \
${COMMON_OPTIONS}
make -j$JOBS
make install-libs
make clean
【5】执行build_ffmpeg.sh ,然后会在下面目录生成相应的jni
ExoPlayer/extensions/ffmpeg/src/main/jni/ffmpeg/android-libs
【6】切换至ExoPlayer/extensions/ffmpeg目录,使用gradle构建
如: gradlew assemble 或 gradle -b build.gradle assemble
【7】生成ffmpeg相关的aar,引入到应用项目中
implementation files('libs/extension-ffmpeg-release.aar')
【8】在Exo中添加FfmpegAudioRenderer,当然顺序可以前可以可以后,前面的容易被优先使用
private val mRenderFactory =
object : DefaultRenderersFactory(MyApp.instance.applicationContext) {
override fun buildAudioRenderers(
context: Context,
extensionRendererMode: Int,
mediaCodecSelector: MediaCodecSelector,
enableDecoderFallback: Boolean,
audioSink: AudioSink,
eventHandler: Handler,
eventListener: AudioRendererEventListener,
out: ArrayList<Renderer>
) {
//添加FfmpegAudioRenderer
out.add(FfmpegAudioRenderer())
super.buildAudioRenderers(
context,
extensionRendererMode,
mediaCodecSelector,
enableDecoderFallback,
audioSink,
eventHandler,
eventListener,
out
)
}
}.apply {
setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
}
【8】完毕
错误处理
/bin/sh: /Users/dagege/Library/Android/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_敏感词/bin/aarch敏感词-linux-androideabi-ar: No such file or directory
make: *** [libavcodec/libavcodec.a] Error 127
make: *** Waiting for unfinished jobs....
AR libavcodec/libavcodec.a
/bin/sh: /Users/dagege/Library/Android/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_敏感词/bin/aarch敏感词-linux-androideabi-ar: No such file or directory
make: *** [libavcodec/libavcodec.a] Error 127
如果出现这种情况
意味着configure 时找不到改路径,没关系,添加上正确的路径即可
--ar="${TOOLCHAIN_PREFIX}/aarch敏感词-linux-android-ar" \
比如静态编译就需要新增一些配置
COMMON_OPTIONS="
--target-os=android
--enable-static
--disable-shared
--enable-jni
--enable-asm
--enable-runtime-cpudetect
--enable-cross-compile
--disable-doc
--disable-programs
--disable-everything
--disable-avdevice
--disable-avformat
--disable-swscale
--disable-postproc
--disable-avfilter
--disable-symver
--disable-avresample
--disable-ffmpeg
--disable-ffplay
--disable-ffprobe
--enable-swresample
--extra-ldexeflags=-pie
"
TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin"
for decoder in "${ENABLED_DECODERS[@]}"
do
COMMON_OPTIONS="${COMMON_OPTIONS} --enable-decoder=${decoder}"
done
./configure \
--libdir=android-libs/armeabi-v7a \
--arch=arm \
--cpu=armv7-a \
--cross-prefix="${TOOLCHAIN_PREFIX}/armv7a-linux-androideabi21-" \
--nm="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-nm" \
--ar="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-ar" \
--ranlib="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-ranlib" \
--strip="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-strip" \
--extra-cflags="-march=armv7-a -mfloat-abi=softfp" \
--extra-ldflags="-Wl,--fix-cortex-a8" \
${COMMON_OPTIONS}
make -j4
make install-libs
make clean
注意:使用CMAKELists 静态库会自动编入动态库
cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR)
# Enable C++11 features.
set(CMAKE_CXX_STANDARD 11)
project(libffmpeg_jni C CXX)
if(${ANDROID_ABI} MATCHES "arm敏感词-v8a")
set(CMAKE_CXX_FLAGS "-Wl,-Bsymbolic")
endif()
set(ffmpeg_location "${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg")
set(ffmpeg_binaries "${ffmpeg_location}/android-libs/${ANDROID_ABI}")
foreach(ffmpeg_lib avutil swresample avcodec)
set(ffmpeg_lib_filename lib${ffmpeg_lib}.a)
set(ffmpeg_lib_file_path ${ffmpeg_binaries}/${ffmpeg_lib_filename})
add_library(
${ffmpeg_lib}
STATIC
IMPORTED)
set_target_properties(
${ffmpeg_lib} PROPERTIES
IMPORTED_LOCATION
${ffmpeg_lib_file_path})
endforeach()
include_directories(${ffmpeg_location})
find_library(android_log_lib log)
add_library(ffmpeg_jni
SHARED
ffmpeg_jni.cc)
#静态库会自动编入ffmpeg,无需手动合并
target_link_libraries(ffmpeg_jni
PRIVATE android
PRIVATE swresample
PRIVATE avcodec
PRIVATE avutil
PRIVATE ${android_log_lib})