由于较新版本NDK编译FFmpeg源码资料较少,网上的教程大多都是NDK<=17的,自己也是经过漫长的调试才编译成功的,这里整理过来记录一下
本文参考的文章,大家需要的话可以去学习看一下
(1) Android NDK Build FFMPEG in 2021
https://stackoverflow.com/questions/68862476/android-ndk-build-ffmpeg-in-2021
(2) Android FFmpeg 编译和集成(十四)- PengJie
https://cloud.tencent.com/developer/article/1773965
如果你想在FFmpeg使用libx264,你可能还需要参考以下文章,因为ffmpeg默认是不带libx264的,只是加--enable-libx264是不行的,还需要编译相应的源码
(1) Android NDK编译libx264源码
(2) Android FFmpeg编译时导入libx264

#官方源码克隆地址
git clone https://git.ffmpeg.org/ffmpeg.git
#github克隆地址
git clone https://github.com/FFmpeg/FFmpeg.git
#!/bin/bash
set -x
###########根据自己电脑环境进行修改, 确保CC,CXX文件存在################Start
#Compile android api level, if compile armv7a,change to eabi21
API=21
#arm64,armv7-a,i686,x86-64
ARCH=arm64
#armv8-a,armv7-a,i686,x86-64
CPU=armv8-a
#aarch64,armv7a, i686, x86_64
TOOL_CPU_NAME=aarch64
#NDK path
NDK=/root/Android/Sdk/ndk/21.4.7075529
#Compile output
OUTPUT=/home/qlx/ffmpeg_build/$CPU
#Toolchain path, make sure this file exists
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
#Sysroot, Default
SYSROOT=$TOOLCHAIN/sysroot
TOOL_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android"
#Clang C/C++ executable file
CC="$TOOL_PREFIX$API-clang"
CXX="$TOOL_PREFIX$API-clang++"
#Strip executable file path, use to reduce .so file size, if compile release .so file,
# can remove --disable-stripping attribute from below
STRIP="$TOOL_PREFIX-strip"
#If compile armv7,change to this
#STRIP="$TOOLCHAIN/bin/arm-linux-androideabi-strip"
#########################################################################End
#启用优化
OPTIMIZE_CFLAGS="-march=$CPU"
function build
{
./configure \
--prefix=$OUTPUT \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--disable-asm \
--disable-stripping \
--disable-programs \
--disable-static \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--disable-ffmpeg \
--enable-shared \
--enable-cross-compile \
--cc=$CC \
--cxx=$CXX \
--strip=$STRIP \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
make clean all
# 这里是定义用几个CPU编译
make -j8
make install
}
build
| 变量名称 | 说明 |
|---|---|
| API | 构建的API版本,注意旧版本NDK是没有这个api版本数字的,你需要看你的$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin下的clang文件是否存在21,22,23这样的数字,也就是aarch64-linux-android21-clang之类的文件 |
| ARCH | 构建的so文件类型(arm64, armv7a, i686, x86_64), 如若是armv7a,请将API改成eabi21,因为要与ndk的文件对应才能编译 |
| CPU | 构建目标平台的CPU类型(armv8-a,armv7-a,i686,x86-64) |
| TOOL_CPU_NAME | CPU名称,其实也要与NDK下的文件对应(aarch64,armv7a, i686, x86_64) |
| OUTPUT | .so库文件和头文件输出的路径 |
| NDK | 本地NDK目录的位置,注意,NDK是区分Linux,Windows,Mac的 |
| TOOLCHAIN | 构建工具的目录,这个位置通常是固定,注意结尾的linux-x86_64,在windows,linux,mac是不同的,需要自己看着路径去修改 |
| TOOL_PREFIX | 编译文件前缀,不需要理会,其实就是定位clang。clang++文件位置的前缀 |
| CC | (重要)编译C文件的工具位置,指clang |
| CXX | (重要)编译C++文件工具的位置,指clang++ |
| STRIP | strip文件的位置,用来减小so文件体积用的,ffmpeg默认开启,所以需要指定文件位置,如果是调试阶段,不需要去除符号,可以在配置中加入--disable-strapping |
#加入可执行权限
chmod +x build.sh
#执行脚本
./build.sh
执行完毕后,没报错的话去OUTPUT目录去找文件就好了
打开OUTPUT路径下lib目录,将so文件复制到app目录下的libs目录
这里由于我是在模拟器测试的,所以只编译导入了x86的so文件,需要armv8的可以根据脚本文件给出的注释进行修改,然后再编译即可(注意事项:不要在app的build.gradle中加入jniLibs.srcDirs的这种方式让so库文件附加到apk中,这是java调用的方式,由于我们需要在C++代码中引用so文件和编写native代码,所以你需要用CMakeList的方式去引入,这里后面会提到)

将编译后OUTPUT路径的include里的.h头文件放置到cpp目录的ffmpeg中,然后修改CMakeList.txt配置
这是我的目录结构

这是我的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.18.1)
# Declares and names the project.
project("surfacework")
#声明FFMPEG的头文件所在路径
set(CPP_DIR ${CMAKE_SOURCE_DIR})
set(FFMPEG_HEAD_PATH "${CPP_DIR}/ffmpeg")
get_filename_component(FFMPEG_LIB_PATH ${CPP_DIR}, PATH)
get_filename_component(FFMPEG_LIB_PATH ${FFMPEG_LIB_PATH}, PATH)
get_filename_component(FFMPEG_LIB_PATH ${FFMPEG_LIB_PATH}, PATH)
set(FFMPEG_LIB_PATH ${FFMPEG_LIB_PATH}/libs/${ANDROID_ABI})
include_directories(${FFMPEG_HEAD_PATH})
#输出环境信息
message("==========================FFMPEG Enviroment=================================")
message("FFMPEG_HEAD_PATH => ${FFMPEG_HEAD_PATH}")
message("FFMPEG_LIB_PATH => ${FFMPEG_LIB_PATH}")
#声明要导入的库
set(
# List variable name
ffmpeg_libs_names
# Values in the list
avformat avcodec avfilter avutil swresample swscale)
message("==========================FFMPEG Shared Library==============================")
#导入ffmpeg的so库
foreach (ffmpeg_lib_name ${ffmpeg_libs_names})
add_library(
${ffmpeg_lib_name}
SHARED
IMPORTED)
set_target_properties(
${ffmpeg_lib_name}
PROPERTIES
IMPORTED_LOCATION
${FFMPEG_LIB_PATH}/lib${ffmpeg_lib_name}.so)
message("[import so file] => ${FFMPEG_LIB_PATH}/lib${ffmpeg_lib_name}.so")
endforeach ()
# 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.
surfacework
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
surfacework.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
surfacework
# Links the target library to the log library
# included in the NDK.
${log-lib}
${ffmpeg_libs_names} )
在最后的打印结果中,FFMPEG_HEAD_PATH 和 FFMPEG_LIB_PATH 变量需要分别指向.h头文件路径和.so文件的路径,也就是目录结构中的ffmpeg文件夹和libs文件夹
....................
#声明FFMPEG的头文件所在路径
message("==========================FFMPEG Enviroment=================================")
message("FFMPEG_HEAD_PATH => ${FFMPEG_HEAD_PATH}")
message("FFMPEG_LIB_PATH => ${FFMPEG_LIB_PATH}")
....................
声明要导入的so库,这里基本复制粘贴就好了,avformat avcodec avfilter avutil swresample swscale这些的按需要加上,但要注意顺序,因为有些so库文件要引入其它so库,在其它so文件没导入的时候就先导入,可能会报错
#声明要导入的库
set(
# List variable name
ffmpeg_libs_names
# Values in the list
avformat avcodec avfilter avutil swresample swscale)
message("==========================FFMPEG Shared Library==============================")
#导入ffmpeg的so库
foreach (ffmpeg_lib_name ${ffmpeg_libs_names})
add_library(
${ffmpeg_lib_name}
SHARED
IMPORTED)
set_target_properties(
${ffmpeg_lib_name}
PROPERTIES
IMPORTED_LOCATION
${FFMPEG_LIB_PATH}/lib${ffmpeg_lib_name}.so)
message("[import so file] => ${FFMPEG_LIB_PATH}/lib${ffmpeg_lib_name}.so")
endforeach ()
在最后将ffmpeg加入链接库就好了
target_link_libraries( # Specifies the target library.
surfacework
# Links the target library to the log library
# included in the NDK.
${log-lib}
${ffmpeg_libs_names} )
package com.qmel.surfacework;
public class AVUtils {
//导入so文件
static {
System.loadLibrary("avformat");
System.loadLibrary("avcodec");
System.loadLibrary("avutil");
System.loadLibrary("swscale");
System.loadLibrary("swresample");
System.loadLibrary("avfilter");
//这是我自己的库,根据自己的项目自行修改
System.loadLibrary("surfacework");
}
//获取所有的编解码
public static native String getFFmpegCodecList();
}
#include <jni.h>
#include <cstdlib>
#include "android/log.h"
#include "string.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
JNIEXPORT jstring JNICALL
Java_com_qmel_surfacework_AVUtils_getFFmpegCodecList(JNIEnv *env, jclass clazz) {
// TODO: implement getFFmpegInfo()
char info[40000] = {0};
//列举所有的编解码器
void *p = nullptr;
const AVCodec *c_temp;
while ((c_temp = av_codec_iterate(&p))) {
if (av_codec_is_decoder(c_temp)) {
sprintf(info, "%sdecode => %s\n", info, c_temp->name);
} else {
sprintf(info, "%sencode => %s\n",info, c_temp->name);
}
}
return env->NewStringUTF(info);
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TextView
android:layout_margin="16dp"
android:text="info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:id="@+id/tv_info"/>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.qmel.surfacework;
import android.os.Bundle;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private TextView tvInfo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvInfo = findViewById(R.id.tv_info);
tvInfo.setText(AVUtils.getFFmpegCodecList());
}
}

当你的程序运行没有闪退,并且能正确的获取到所有的编解码器信息,说明已经成功导入了!
我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.
如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路
我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope
是否有适用于Ruby语言的.NETFramework编译器?我听说过DLR(动态语言运行时),这是否将使Ruby能够用于.NET开发? 最佳答案 IronRuby是Microsoft支持的项目,建立在动态语言运行时之上。 关于.net-是否有Ruby.NET编译器?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/199638/
我正在尝试创建一个与compass一起使用的本地配置文件,这样我们就可以处理开发人员机器上的不同导入路径。到目前为止,我已经尝试将文件导入到异常block中,以防它不存在,然后进一步使用该变量:local_config.rbVENV_FOLDER='venv'config.rbVENV_FOLDER='.'beginrequire'local_config.rb'rescueLoadErrorendputsVENV_FOLDER通常我是一名Python开发人员,所以我希望导入将VENV_FOLDER的值更改为venv,但它仍然是。之后。有没有一种方法可以导入local_config.r
关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭10年前。ImprovethisquestionLinux专家正在转向Mac(10.8)。因为我懒...我使用MacPorts安装MacVim。它似乎安装没有错误。我只需要mvim中的python、ruby和perl支持。$/opt/local/bin/mvim--version|egrep'patches|python|ruby|perl'Includedpatches:1-244,246-646+multi_lang-mzscheme+
最好用一个例子来解释:文件1.rb:deffooputs123end文件2.rb:classArequire'file1'endA.new.foo将给出错误“':调用了私有(private)方法'foo'”。我可以通过执行A.new.send("foo")来解决这个问题,但是有没有办法公开导入的方法?编辑:澄清一下,我没有混淆include和require。另外,我不能使用正常包含的原因(正如许多人正确指出的那样)是因为这是元编程设置的一部分。我需要允许用户在运行时添加功能;例如,他可以说“run-this-app--includefile1.rb”,应用程序的行为将根据他在file1