一、前提
FFmpeg版本必须与系统版本(iOS)和ndk版本(Android)相匹配,否则就会有各种奇葩的编译问题。有些是ffmpeg代码与iOS系统库冲突,有些是ffmpeg代码与ndkd版本冲突,解决这些问题很浪费时间,且毫无意义。
二、iOS
1. 版本
FFmpeg:4.4
macOS:11.2.2
2. 步骤
- github上下载ffmpeg;
- github上下载gas-preprocessor.pl,并拷贝至/usr/local/bin下,添加777权限;
- mac安装Command Line Tools
- 下载编译脚本:https://github.com/kewlbear/FFmpeg-iOS-build-script
- 修改build-ffmpeg.sh脚本种的FF_VERSION为你的版本号,修改SOURCE为你的ffmpeg路径
- ./build-ffmpeg.sh
三、Android
1. 版本
FFmpeg:4.2.1(建议无历史包袱的项目采用较新版本的FFmpeg)
ndk:r21e
2. 编译脚本
#!/bin/bash # 以下路径需要修改成自己的NDK目录 TOOLCHAIN=/Users/jiayayao/Library/Android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64 # 最低支持的android sdk版本 API=21 function build_android { echo "Compiling FFmpeg for $CPU" ./configure --prefix=$PREFIX --disable-hwaccels --disable-gpl --disable-postproc --enable-shared --enable-jni --disable-mediacodec --enable-small --enable-gpl --disable-decoder=h264_mediacodec --disable-static --disable-doc --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-avdevice --disable-symver --enable-neon --disable-asm --cross-prefix=$CROSS_PREFIX --target-os=android --arch=$ARCH --cpu=$CPU --cc=$CC --cxx=$CXX --enable-cross-compile --sysroot=$SYSROOT --extra-cflags="-mno-stackrealign -Os $OPTIMIZE_CFLAGS -fPIC" --extra-ldflags="$ADDI_LDFLAGS" $ADDITIONAL_CONFIGURE_FLAG make clean make -j16 make install echo "The Compilation of FFmpeg for $CPU is completed" } # armv8-a ARCH=arm64 CPU=armv8-a # r21版本的ndk中所有的编译器都在/toolchains/llvm/prebuilt/darwin-x86_64/目录下(clang) CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ # NDK头文件环境 SYSROOT=$TOOLCHAIN/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android- # so输出路径 PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU" build_android # 交叉编译工具目录,对应关系如下 # armv8a -> arm64 -> aarch64-linux-android- # armv7a -> arm -> arm-linux-androideabi- # x86 -> x86 -> i686-linux-android- # x86_64 -> x86_64 -> x86_64-linux-android- # CPU架构 # armv7-a ARCH=arm CPU=armv7-a CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++ SYSROOT=$TOOLCHAIN/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU " build_android # x86 ARCH=x86 CPU=x86 CC=$TOOLCHAIN/bin/i686-linux-android$API-clang CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++ SYSROOT=$TOOLCHAIN/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32" build_android # x86_64 ARCH=x86_64 CPU=x86-64 CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++ SYSROOT=$TOOLCHAIN/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel" # 方法调用 build_android
3. Android Studio使用编译好的so:
a, Android Studio新建native项目,build.gradle添加如下代码:
defaultConfig { applicationId "com.example.ffmpegtest" minSdkVersion 21 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags '-std=c++11' } } ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } splits { abi { enable true reset() include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' universalApk true } }
b, CMakeList.txt文件添加如下代码:
# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.10.2) # Declares and names the project. project("ffmpegtest") # 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. add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). native-lib.cpp ) set(JNI_LIBS_DIR ${CMAKE_SOURCE_DIR}/../ffmpeg) add_library(avutil SHARED IMPORTED ) set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavutil.so ) add_library(avresample SHARED IMPORTED ) set_target_properties(avresample PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavresample.so ) add_library(swresample SHARED IMPORTED ) set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libswresample.so ) add_library(swscale SHARED IMPORTED ) set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libswscale.so ) add_library(avcodec SHARED IMPORTED ) set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavcodec.so ) add_library(avformat SHARED IMPORTED ) set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavformat.so ) add_library(avfilter SHARED IMPORTED ) set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavfilter.so ) add_library(avdevice SHARED IMPORTED ) set_target_properties(avdevice PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavdevice.so ) include_directories(${JNI_LIBS_DIR}/${ANDROID_ABI}/include) target_link_libraries(native-lib avutil swresample swscale avcodec avformat avfilter ${log-lib} ${android-lib} )
c, native-lib.cpp添加如下代码:
#include <jni.h> #include <string> #include <android/log.h> extern "C" { #include <libavformat/avformat.h> } void ffmpeg_log_output(void* ptr, int level, const char* fmt, va_list vl) { __android_log_print(ANDROID_LOG_INFO, "jiayayao", "[ffmpeg log]%s", fmt); } extern "C" JNIEXPORT jstring JNICALL Java_com_example_ffmpegtest_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { av_log_set_callback(ffmpeg_log_output); AVFormatContext *pContext = avformat_alloc_context(); avformat_open_input(&pContext, "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8", nullptr, nullptr); avformat_find_stream_info(pContext, nullptr); std::string hello = "jiayayao"; return env->NewStringUTF(hello.c_str()); }
d, AndroidManifest.Xml添加如下权限以联网:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
e, 日志显示ffmpeg打开url成功:
I/jiayayao: [ffmpeg log]stream %d: start_time: %0.3f duration: %0.3f [ffmpeg log]format: start_time: %0.3f duration: %0.3f bitrate=%lld kb/s
I/jiayayao: [ffmpeg log]After avformat_find_stream_info() pos: %lld bytes read:%lld seeks:%d frames:%d
f, github链接:https://github.com/jiayayao/ffmpegtest