一. 下载FFmpeg源码
该项目是基于FFmpeg6.0环境编写。文中涉及代码在不同版本可能会有变动
从以下两个地址任选其一下载源码:
- https://github.com/FFmpeg/FFmpeg
- https://ffmpeg.org/
二、对FFmpeg进行安装编译
执行根目录的configure文件,
./configure
该方式会生成相关文件,否则项目不可运行,
参考如下:
https://ffmpeg.org/doxygen/6.0/md_INSTALL.html
编译过程中会出现错误,根据错误进行修改,通常会缺少pkg-config
安装包,这个会在编译时候提示缺少这个,不提示就是不缺少。安装如下:brew install pkg-config
整体错误大概分为缺少依赖的安装包和环境。这个根据提示进行安装即可。没办法绕过
编译成.so参考如下
Android 集成 FFmpeg (一) 基础知识及简单调用
https://blog.csdn.net/yhaolpz/article/details/76408829
如果使用sh文件进行编译的话会出现权限拒绝的情况,可以使用以下命令:
chmod +x build_android.sh
然后再执行sh文件。
以下为sh配置的文件,不需要在项目中配置环境变量,但是所有文件中用到的具体路径需要进行修改。
需要注意的是每次添加新的命令需要使用\
进行分割。在进行配置的时候对于高级选项的命令,比如优化部分--disable-neon
最好不要使用,否则容易出现错误。相关命令可以通过configure --help
进行查看
#!/bin/bash
NDK=/Users/c/Documents/sdk/android/ndk/25.1.8937393
TOOLCHAIN_ROOT_DIR=darwin-x86_64
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$TOOLCHAIN_ROOT_DIR/
API=21
#要编译的ffmpeg内容方法
function build_android {
echo "Compiling FFmpeg for $CPU"
./configure \
--prefix=$PREFIX \
--disable-hwaccels \
--disable-gpl \
--disable-postproc \
--disable-programs \
--disable-mediacodec \
--disable-decoder=h264_mediacodec \
--disable-static \
--disable-vulkan \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--disable-x86asm \
--disable-filters \
--enable-cross-compile \
--enable-jni \
--enable-shared \
--cross-prefix=$CROSS_PREFIX \
--nm=$NM \
--strip=$STRIP \
--pkgconfigdir=$PKG_CONFIG_DIR \
--pkg-config=$PKG_CONFIG \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
echo "The Compilation of FFmpeg for $CPU is completed"
}
#接下来是根据需要来决定
#armv8-a
ARCH=arm64
CPU=armv8-a
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
LLVM_TOOLCHAIN=$TOOLCHAIN/bin
PKG_CONFIG_DIR=/opt/homebrew/Cellar/pkg-config/0.29.2_3
PKG_CONFIG=$PKG_CONFIG_DIR/bin/pkg-config
NM=$LLVM_TOOLCHAIN/llvm-nm
STRIP=$LLVM_TOOLCHAIN/llvm-strip
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
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++
LLVM_TOOLCHAIN=$TOOLCHAIN/bin
PKG_CONFIG_DIR=/usr/local/Cellar/pkg-config/0.29.2_3
PKG_CONFIG=$PKG_CONFIG_DIR/bin/pkg-config
NM=$LLVM_TOOLCHAIN/llvm-nm
STRIP=$LLVM_TOOLCHAIN/llvm-strip
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build_android
其实最初文件可以写成以下方式:
export ANDROID_NDK=<NDK路径>
export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64
export SYSROOT=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
./configure \
--prefix=$PWD/build \
--enable-shared \
--disable-static \
--disable-doc \
--disable-programs \
--disable-symver \
--arch=arm64 \
--target-os=android \
--cc=$TOOLCHAIN/bin/aarch64-linux-android21-clang \
--cxx=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--sysroot=$SYSROOT
但是编译中会出现各种错误,最终要么重新指定路径,要么禁止使用,就改成了如此。
关于命令的含义参考如下:
FFmpeg中configure的参数配置解释
对于pkg-config
的配置如果编译时候找不到,要么禁用--disable-pkgconfig
,要么使用以下方式进行配置
./configure --pkg-config=/path/to/pkg-config --pkgconfigdir=/path/to/pkgconfigdir
最终会在ffmpeg的根目录生成android/<CPU>
目录(这里是android/<armv8-a >
),里面共有三个文件夹include
、lib
、share
。.so位于lib文件夹下:
shell脚本参考如下:
shell脚本语言(超全超详细)
对于cpu和ABI的配置可以Android ABI
三、进行JNI接口编写代码
在FFmpeg编译成.so完成后,需要添加JNI编码,否则的话无法在Android项目中直接使用。该操作需要配置ndk-build环境
参考链接:
Android 集成 FFmpeg (一) 基础知识及简单调用
这里使用文本工具进行代码编写和编译,不使用Android Studio进行编译。
首先在上述的FFmpeg根目录下后面生成的文件夹android
中创建文件ndkBuild\com\jni\FFmpeg.java
和ndkBuild\jni
文件夹FFmpeg.java
代码如下:
package com.jni;
public class FFmpeg {
public static native void run();
}
然后在ndkBuild
目录下执行命令生成com_jni_FFmpeg.h
文件
该命令的中间的.
表示当前路径,可以更改为其余路径,后面是具体的类的名字
javah -classpath . com.jni.FFmpeg
然后在ndkBuild\jni
文件夹下创建com_jni_FFmpeg.c
、Android.mk
、Application.mk
代码如下:com_jni_FFmpeg.c
#include <android/log.h>
#include "com_jni_FFmpeg.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
# 这里是把AV_CODEC_ID_MP2的编码信息打印一下
JNIEXPORT void JNICALL Java_com_jni_FFmpeg_run(JNIEnv *env, jclass obj) {
char info[40000] = {0};
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_MP2);
AVCodecContext *avcodecContext = avcodec_alloc_context3(codec);
const char *license = avcodec_license();
if(avcodecContext->codec_type == AVMEDIA_TYPE_VIDEO){//音频
sprintf(info,"%s[Video]",info);
}else{//视频
sprintf(info,"%s[Audio]",info);
}
sprintf(info,"%s[%10s]\n",info,codec->name);
avcodec_free_context(&avcodecContext); //释放内存
__android_log_print(ANDROID_LOG_INFO,"myTag","info:\n%s",info);
}
Android.mk
LOCAL_PATH:= $(call my-dir)
FFMPEG_PATH:=/Users/c/Documents/CTest/ffmpeg-6.0
FFMPEG_ANDROID:=$(FFMPEG_PATH)/android/armv8-a
INCLUDE_PATH:=$(FFMPEG_ANDROID)/include
FFMPEG_LIB_PATH:=$(FFMPEG_ANDROID)/lib
include $(CLEAR_VARS)
LOCAL_MODULE:= libavcodec
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavcodec.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= libavformat
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavformat.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= libswscale
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libswscale.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= libavutil
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavutil.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= libavfilter
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavfilter.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= libswresample
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libswresample.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := com_jni_FFmpeg.c
LOCAL_C_INCLUDES := $(FFMPEG_PATH)
LOCAL_LDLIBS := -lm -llog
LOCAL_SHARED_LIBRARIES := libavcodec libavfilter libavformat libavutil libswresample libswscale
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := arm64-v8a
APP_PLATFORM=android-21
然后在当前目录下执行命令:
ndk-build
生成Android.mk
中定义的.so
文件。最终会在ndkBuild
下面生成两个文件夹libs
、obj
。在libs\arm64-v8a
下面会有生成的.so库。共七个libavcodec.so
libavfilter.so
libavformat.so
libavutil.so
libswresample.so
libswscale.so
libffmpeg.so
。其中libffmpeg.so
为编写的JNI生成的so库,其余为依赖库。
额外的内容:
上述是使用makefile编译的.so,如果使用cmake的话需要做下额外处理,因为这个库只编译了arm64-v8a,默认Android Studio是编译所有架构的,所以需要使用以下方式进行限制:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a'//, 'x86_64'
}
}
}
编译完的.so库暂时没找到固定存放的目录,不过可以全局搜索文件,例如"ffmpeg.so"。可以找到相关位置。拷贝出来即可
四、在Android 项目中调用.so库
将上述的七个.so库复制到项目的libs文件夹下,可以将整个libs文件夹对Android项目下的libs进行覆盖。app目录下的build.gradle
修改如下:
android{
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
将刚才编写的java文件连同com.jni的文件夹一起复制到src目录下。整体结构如下:
如果将.so连同arm64-v8a文件夹复制到main下面的jniLibs文件夹下面则不需要对build.gradle进行修改。如下
然后在项目中调用代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btn: Button = findViewById(R.id.button)
btn.setOnClickListener {
FFmpeg.run()
}
FFmpeg.test()
}
}
可以在logcat下看到结果
2023-06-08 11:36:01.783 5346-5346 myTag com.test.ffmpeg I info:
[Audio][ mp2]
五、FFmpeg的代码学习技巧
1、整体学习步骤
在学习一个技巧的时候最好根据官方来进行学习,因为其余人的教程可能存在无法实时更新导致的api变动问题,这样使用旧版代码可能无法运行,也就无从下手。在学习ffmpeg的开发过程中主要有以下几个步骤
- 编译ffmpeg为.so。参考:https://ffmpeg.org/doxygen/6.0/md_INSTALL.html,编译过程需要的命令可以通过
configure --help
进行查看 - 将编译成功的结果.so和本地的环境比如Android进行编写桥接代码JNI,这里可以参考Android官方网站: https://developer.android.com/ndk?hl=zh-cn
- 在编写JNI时候需要使用ffmpeg的api来完成我们的目的,这里的学习方式最好使用官方教程: https://ffmpeg.org/documentation.html , 以及相关论坛
2、FFmpeg的代码学习步骤
FFmpeg本身是一个工具系列的库,是一个开源性质的库,所以教程相对于那些商业化的框架会比较难以上手,比如OkHttp
框架。最初通过浏览器搜索的结果,代码基本上都难以运行,大部分都是api过时,这样其实不利于入门,毕竟通过别人的代码学会了,但是api过时了,又不知道怎么更新api。通过仔细查阅官方网站整理了一条路径(不一定是最优学习方式,有更好的可以留言)。
首先官网提供了必要的api文档和一些基本的示例(虽然示例可能不是我们想象中的示例程序)。通过官方文档我们知道整体文档分为: 命令行工具文档、组件文档、图书馆文件、一般文件、API文档、社区贡献文档。
通过每一个都点进去后可以简单知道,内容很多,但是作为初学者来说,实在是难以接受,茫然无措,不知道从何开始。如果是用命令行就可以完成需求的话,当然命令行工具文档是很好的选择,但是如果需要自己通过api开发,就只能查看api文档了。其余的文档可以等后面熟悉了再进行阅读。
api文档位于:https://ffmpeg.org/doxygen/trunk/index.html。每天都会更新。整体页面如下
通过此我们知道的有用信息如下:
整个库分为八个部分
- libavcodec编码/解码库
- libavfilter基于图形的帧编辑库
- libavformat I/O 和 muxing/demuxing 库
- libavdevice特殊设备复用/解复用库
- libavutil通用实用程序库
- libswresample音频重采样、格式转换和混合
- libpostproc后处理库
- libswscale颜色转换和缩放库
在上面的Modules选项下面可以看到都有哪些模块:
通过Examples选项下面可以看到有哪些例子
如果恰巧自己想实现的需求是例子中所有的,那就非常完美,可是点进去例子后发现,例子对初学者来说写的很复杂。我们最初只想写一个简单的测试例子,测试程序是否编译通过,比如打印下ffmpeg的基础信息,打印下基本的信息。如果不这样做,直接完成一个音视频播放的例子,无疑会显得特别困难。然后我们进行需求分析,什么样的场景满足这样的需求?通过上述信息搜集,可以知道我们打印下ffmpeg支持的格式会比较简单,也不需要进行文件读写。这里的话就锁定使用libavformat。点击进去后发现内容如下:
模块如下,文件有三个头文件,一个avformat.h,剩下两个是版本信息,其余的库也是如此,我们的主要参考api就位于第一个文件中,点击进去后发现,里面包含了定义的常量和函数。
被标记的就是会用到的遍历函数。我们点击第一个函数。
这里是函数的定义,解释了需要从传参数为NULL开始便利,下面是引用该函数的位置(有的api下面还要示例程序的引用地址,不过这个api没有)。选择第一个引用的位置点击进去查看:
这里是一个简单的使用方式。将该代码拷贝到实际项目中去,然后别忘了添加相应的头文件,格式如下:<库名/头文件名.h> #include <libavformat/avformat.h>
。相应的库名和头文件名在上述步骤已经给出。
所以整体代码如下:
#include <android/log.h>
#include "com_jni_FFmpeg.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <libavformat/avformat.h>
JNIEXPORT void JNICALL Java_com_jni_FFmpeg_run(JNIEnv *env, jclass obj) {
const AVOutputFormat *fmt = NULL;
const AVOutputFormat *fmt_found = NULL;
void *i = 0;
int score_max, score;
while ((fmt = av_muxer_iterate(&i))) {
score = 0;
const char *fmtName = fmt->name;
__android_log_print(ANDROID_LOG_INFO,"myTag","fmtName:\n%s",fmtName);
}
}
运行程序后可再logcat控制台看到如下内容:
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
a64
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
ac3
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
adts
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
adx
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
aiff
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
alp
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
amr
2023-06-08 14:56:30.886 8824-8824 myTag com.test.ffmpeg I fmtName:
amv
...
...
不过这种代码编写方式不太好,没办法发挥ide的特性,想了解使用ide编写代码的方式可以查看Mac环境下使用Clion编译测试运行Ffmpeg。
下面是根据网上其余人的需求写的代码,打印出支持的编解码类型
#include <android/log.h>
#include "com_jni_FFmpeg.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <libavformat/avformat.h>
#include "libavcodec/avcodec.h" //编码器格式
#include <libavutil/frame.h>
#include <libavutil/mem.h>
void iterateAvOutFormat(){
const AVOutputFormat *fmt = NULL;
const AVOutputFormat *fmt_found = NULL;
void *i = 0;
int score_max, score;
while ((fmt = av_muxer_iterate(&i))) {
score = 0;
const char *fmtName = fmt->name;
__android_log_print(ANDROID_LOG_INFO,"myTag","fmtName:\n%s",fmtName);
}
}
void printMp2Decoder(){
char info[40000] = {0};
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_MP2);
AVCodecContext *avcodecContext = avcodec_alloc_context3(codec);
const char *license = avcodec_license();
if(avcodecContext->codec_type == AVMEDIA_TYPE_VIDEO){//音频
sprintf(info,"%s[Video]",info);
}else{//视频
sprintf(info,"%s[Audio]",info);
}
sprintf(info,"%s[%10s]\n",info,codec->name);
avcodec_free_context(&avcodecContext); //释放内存
__android_log_print(ANDROID_LOG_INFO,"myTag","info:\n%s",info);
}
void iterateMimeType(AVCodecDescriptor *desc){
if (desc == NULL){
return;
}
const char *const *const mime_types = desc->mime_types; //这是个数组类型
if (mime_types == NULL){
return;
}
char** current_str = mime_types;
while (*current_str != NULL) {
// printf("类型:%s\n", *current_str);
__android_log_print(ANDROID_LOG_INFO,"myTag","mimeType:\n%1c",*current_str);
current_str++;
}
}
//打印编码信息
//代码源自: https://ffmpeg.org/doxygen/trunk/opt__common_8c_source.html#l00635
void iterateCodec(){
const AVCodecDescriptor *desc = NULL;
const AVCodecDescriptor **codecs;
unsigned nb_codecs = 0, i = 0;
while ((desc = avcodec_descriptor_next(desc))){
nb_codecs++; //计算有多少个编码器
enum AVCodecID avCodecId = desc->id;
enum AVMediaType avCodecType = desc->type;
const char *avCodecName = desc->name;
const char *avCodecLongName = desc->long_name;
const char *const *const mime_types = desc->mime_types; //这是个数组类型
__android_log_print(ANDROID_LOG_INFO,"myTag","avCodecName:\n%1s, avCodecLongName: %2s",avCodecName, avCodecLongName);
iterateMimeType(desc);
}
}
JNIEXPORT void JNICALL Java_com_jni_FFmpeg_run(JNIEnv *env, jclass obj) {
// printMp2Decoder();
// iterateAvOutFormat();
iterateCodec();
}
六、关于编译报错的解决思路
有时候编译会出现错误,比如如下:
ym@192 ffmpeg-6.0 % ./build_android.sh
Compiling FFmpeg for x86_64
/Users/ym/Work/AndroidDevelop/AndroidSdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/x86_64-linux-android21-clang is unable to create an executable file.
C compiler test failed.
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
ffmpeg-user@ffmpeg.org 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.
GEN libavutil/libavutil.version
GEN libswscale/libswscale.version
GEN libswresample/libswresample.version
GEN libavcodec/libavcodec.version
GEN libavformat/libavformat.version
GEN libavfilter/libavfilter.version
CC libavfilter/aarch64/vf_nlmeans_init.o
CC libavfilter/aeval.o
....
当出现错误时候即使最终执行结束了,后面没有提示失败,也是不会有.so生成的,这里可以看下报错下面的提示,提示去查看"ffbuild/config.log"
文件,这个文件位于ffmpeg源码根目录下面,打开后拉到最下面,可以看到以下内容:
test_ld cc
test_cc
BEGIN /var/folders/0c/q0xxnz590915l9vf7njtx3mc0000gn/T//ffconf.0lN8TtaD/test.c
1 int main(void){ return 0; }
END /var/folders/0c/q0xxnz590915l9vf7njtx3mc0000gn/T//ffconf.0lN8TtaD/test.c
/Users/ym/Work/AndroidDevelop/AndroidSdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/x86_64-linux-android21-clang --sysroot=/Users/ym/Work/AndroidDevelop/AndroidSdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Os -fpic -march=x86_64 -m64 -mtune=intel -c -o /var/folders/0c/q0xxnz590915l9vf7njtx3mc0000gn/T//ffconf.0lN8TtaD/test.o /var/folders/0c/q0xxnz590915l9vf7njtx3mc0000gn/T//ffconf.0lN8TtaD/test.c
error: unknown target CPU 'x86_64'
note: valid target CPU values are: nocona, core2, penryn, bonnell, atom, silvermont, slm, goldmont, goldmont-plus, tremont, nehalem, corei7, westmere, sandybridge, corei7-avx, ivybridge, core-avx-i, haswell, core-avx2, broadwell, skylake, skylake-avx512, skx, cascadelake, cooperlake, cannonlake, icelake-client, rocketlake, icelake-server, tigerlake, sapphirerapids, alderlake, knl, knm, k8, athlon64, athlon-fx, opteron, k8-sse3, athlon64-sse3, opteron-sse3, amdfam10, barcelona, btver1, btver2, bdver1, bdver2, bdver3, bdver4, znver1, znver2, znver3, x86-64, x86-64-v2, x86-64-v3, x86-64-v4
C compiler test failed.
文章来源:https://www.toymoban.com/news/detail-808734.html
然后根据提示发现是找不到'x86_64'
的cpu架构,然后从提示中找到可以使用的架构x86-64
。其实就是名字写错了。文章来源地址https://www.toymoban.com/news/detail-808734.html
七、参考链接:
- ndk-build 脚本
- Android studio添加第三方库和so
- Mac环境下使用Clion编译测试运行Ffmpeg
- 编译ffmpeg安卓库(clang篇),含armeabi-v7a , arm64-v8a, x86, x86_64
- Android ABI
到了这里,关于MAC环境编译Android环境下的FFmpeg6.0版本的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!