1、FFmpeg简单介绍
前面编译好了资源文件,如下:
做下简单说明:
- libavformat:多媒体文件或协议的封装和解封装库,如 Mp4、Flv 等文件封装格式,RTMP、RTSP 等网络协议封装格式;
- libavcodec:音视频编解码库;
- libavfilter:音视频、字幕滤镜库;
- libswscale:图像格式转换库;
- libswresample:音频重采样库;
- libavutil:工具库;
2、FFmpeg集成
2.1 创建C++工程
1、直接借助Android Studio创建C++工程
2、看一下工程目录
2.2 引用ffmpeg编译出来的资源
1、首先,在 app/src/main/ 目录下,新建文件夹,并命名为 jniLibs 。
app/src/main/jniLibs是 Android Studio 默认的放置 so 动态库的目录。
接着,在 jniLibs 目录下,新建 armeabi-v7a 目录。
最后把 FFmpeg 编译得到的所有 so 库粘贴到 armeabi-v7a 目录。如下:
2、添加 FFmpeg so 的头文件
在编译 FFmpeg 的时候,除了生成 so 外,还会生成对应的 .h 头文件,也就是 FFmpeg 对外暴露的所有接口。
在 cpp 目录下,新建 ffmpeg 目录,然后把编译时生成的 include 文件粘贴进来。
3、添加、链接 FFmpeg so 库
上面已经把 so 和 头文件 放置到对应的目录中了,但是编译器是不会把它们编译、链接、并打包到 Apk 中的,我们还需要在 CMakeLists.txt 中显性的把相关的 so 添加和链接起来。完整的 CMakeLists.txt 如下:
bash
复制代码
cmake_minimum_required(VERSION 3.4.1)
# 支持gnu++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
# 1. 定义so库和头文件所在目录,方面后面使用
set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/ffmpeg)
# 2. 添加头文件目录
include_directories(${ffmpeg_head_dir}/include)
# 3. 添加ffmpeg相关的so库
add_library( avutil
SHARED
IMPORTED )
set_target_properties( avutil
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavutil.so )
add_library( swresample
SHARED
IMPORTED )
set_target_properties( swresample
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libswresample.so )
add_library( avcodec
SHARED
IMPORTED )
set_target_properties( avcodec
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavcodec.so )
add_library( avfilter
SHARED
IMPORTED)
set_target_properties( avfilter
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavfilter.so )
add_library( swscale
SHARED
IMPORTED)
set_target_properties( swscale
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libswscale.so )
add_library( avformat
SHARED
IMPORTED)
set_target_properties( avformat
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavformat.so )
add_library( avdevice
SHARED
IMPORTED)
set_target_properties( avdevice
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavdevice.so )
# 查找代码中使用到的系统库
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 )
# 配置目标so库编译信息
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
)
# 指定编译目标库时,cmake要链接的库
target_link_libraries(
# 指定目标库,native-lib 是在上面 add_library 中配置的目标库
native-lib
# 4. 连接 FFmpeg 相关的库
avutil
swresample
avcodec
avfilter
swscale
avformat
avdevice
# Links the target library to the log library
# included in the NDK.
${log-lib} )
主要看看注释中新加入的 1~4 点。
1)通过 set 方法定义了 so 和 头文件 所在目录,方便后面使用。
其中
CMAKE_SOURCE_DIR为系统变量,指向CMakeLists.txt所在目录。ANDROID_ABI也是系统变量,指向 so 对应的CPU框架目录:armeabi、armeabi-v7a、x86 ...
bash
复制代码
set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/ffmpeg)
2)通过 include_directories 设置头文件查找目录
bash
复制代码
include_directories(${ffmpeg_head_dir}/include)
3)通过 add_library 添加 FFmpeg 相关的 so 库,以及 set_target_properties 设置 so 对应的目录。
其中,add_library 第一个参数为 so 名字,
SHARED表示引入方式为动态库引入。
scss
复制代码
add_library( avcodec
SHARED
IMPORTED )
set_target_properties( avcodec
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavcodec.so )
4)最后,通过 target_link_libraries 把前面添加进来的 FFMpeg so 库都链接到目标库 native-lib 上。
这样,我们就将 FFMpeg 相关的 so 库都引入到当前工程中了。下面就要来测试一下,是否可以正常调用到 FFmpeg 相关的方法了。
3、使用FFmpeg集成
要检查 FFmpeg 是否可以使用,可以通过获取 FFmpeg 基础信息来验证。
1、在 FFmpegAcrtivity 中添加一个外部方法 stringFromJNI
把获取到的 FFmpeg 信息显示出来。
public class MainActivity extends AppCompatActivity {
// Used to load the 'ffmpeglearn1' library on application startup.
static {
System.loadLibrary("ffmpeglearn1");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'ffmpeglearn1' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
2、C++代码如下
#include <jni.h>
#include <string>
#include <unistd.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavcodec/jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_ffmpeglearn1_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
char strBuffer[1024 * 4] = {0};
strcat(strBuffer, "libavcodec : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));
strcat(strBuffer, "\nlibavformat : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));
strcat(strBuffer, "\nlibavutil : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));
strcat(strBuffer, "\nlibavfilter : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));
strcat(strBuffer, "\nlibswresample : ");
strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));
strcat(strBuffer, "\nlibswscale : ");
strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));
strcat(strBuffer, "\navcodec_configure : \n");
strcat(strBuffer, avcodec_configuration());
strcat(strBuffer, "\navcodec_license : ");
strcat(strBuffer, avcodec_license());
return env->NewStringUTF(strBuffer);
}
}
3、需要声明arm版本和libs路径 由于这里,我只使用了arm64-v8的资源,所以需要先在build.gradle里面声明ndk,不然后续一系列工作都会报错
plugins {
id 'com.android.application'
}
android {
namespace 'com.example.ffmpeglearn1'
compileSdk 33
defaultConfig {
applicationId "com.example.ffmpeglearn1"
minSdk 29
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
// abiFilters "armeabi-v7a", 'arm64-v8a'
abiFilters "armeabi-v7a"
}
externalNativeBuild {
cmake {
//cppFlags '-std=c++11'
// arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_ARM_MODE=arm', '-DANDROID_STL=c++_static'
// cFlags "-ferror-limit=0 -fPIC"
// cppFlags "-std=c++11 -frtti -fexceptions"
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// 2) 配置 CMakeLists 路径
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
buildFeatures {
viewBinding true
}
// 3) 解决2 files found with path 'lib/armeabi-v7a/libavcodec.so' from inputs:的问题
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
4、Java层简单调用:
5、最终的结果如下
集成成功