二、Android Studio集成ffmpeg so

目录

1、前言

2、新建AS工程

[2.1 选择Native C++](#2.1 选择Native C++)

[2.2 按图编辑和编辑](#2.2 按图编辑和编辑)

[2.3 选择C++标准](#2.3 选择C++标准)

[2.4 最初工程目录展示](#2.4 最初工程目录展示)

3、拷贝so库到AS

4、编辑CMakeLists.txt

5、修改build.gradle

6、编辑Native-lib.cpp

7、修改MainActivity.java

8、效果展示


1、前言

本文章之前也是参考了很多教程,五花八门,别人的成功了,自己试却还是遇到很多坑。可能跟自己用的android studio版本有关系?我用的是目前最新的(如下图,打字不如上图)。所以还是自己亲手操作并记录下来(保姆级教程)。

2、新建AS工程

有两种建工程方式可以集成so,一种是自己配置,一种是AS"自带的"----Native C++。这里选择简单的Native C++,非该方式的后面有时间再来补充。

2.1 选择Native C++

选择Native C++,点击Next。

2.2 按图编辑和编辑

注意选择Java、build.gradle(其他方式不熟悉),选好后点击Next。

2.3 选择C++标准

我选择了Toolchain Default,然后点击finish。

2.4 最初工程目录展示

可以看到,比一般工程多了一个cpp目录,里面还有一个.cpp文件和一个CMakeLists.txt,这两个就是我们接下来要编辑的,也就是它搭建了java调用c/c++的桥梁(具体原理和介绍可以百度JNI相关)。

这样,该类工程AS已经把集成C/C++ so库的框架打好了,我们只需要做一些简单的配置就可以集成我们交叉编译的库了。

3、拷贝so库到AS

在cpp目录(感觉这个位置比较合适,别的位置也可以,能在CMakeLists.txt找到就行)下创建一个目录------ffmpegLibs7-0-2,以存放交叉编译好的so和头文件,目前手机大部分都是64位的,这里我只拷贝了arm64-v8a的。如图:

4、编辑CMakeLists.txt

这一步非常关键。工程生成的CMakeLists.txt只是将native-lib封装成了一个jni库,这个库是供java调用的,而我们交叉编译的库是这个jni库来调用的,这样就实现了java间接调用我们交叉编译的so的接口了。编辑CMakeLists.txt如下:

bash 复制代码
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.

# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)

# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("ffmpegtestprj")

# 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.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
#定义交叉编译库的基本目录,方便下面指定头文件目录和库的目录
set(PREFIX "${CMAKE_SOURCE_DIR}/ffmpegLibs7-0-2/")
#包含ffmpeg头文件目录
include_directories(${PREFIX}/include)
#添加交叉编译库的目录
link_directories(${PREFIX}/lib)

#添加库,这一步不能漏,这个和我们平时开发C++代码时不太一样,一般我们是直接在目标库中链接就可以
#而这里需要重新添加一下。有些博客这里不添加,在build.gradle里面指定库的路径(该方式我自己试了不行,
#会报xxx.so is not an ABI,应该是我们自己交叉编译的so,AS是不认识的,接口不匹配;这个坑我踩了很久)。
#这里重新添加的原理我还没找到官方的说法,我理解这么做的原理还是因为java程序不能直接访问该库,只能告诉
#目标库,让目标库去加载。(这里我还做了一个尝试,就是把ffmpeg里面的jni选项打开,以为java程序就可以
#直接访问(build.gradle里指定jniLibs)了,但还是不行,还是报xxx.so is not an ABI)
add_library(
        avcodec
        SHARED
        IMPORTED
)
set_target_properties(
        avcodec
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libavcodec.so
)

add_library(
        avdevice
        SHARED
        IMPORTED
)
set_target_properties(
        avdevice
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libavdevice.so
)

add_library(
        avfilter
        SHARED
        IMPORTED
)
set_target_properties(
        avfilter
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libavfilter.so
)

add_library(
        avformat
        SHARED
        IMPORTED
)
set_target_properties(
        avformat
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libavformat.so
)

add_library(
        avutil
        SHARED
        IMPORTED
)
set_target_properties(
        avutil
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libavutil.so
)

add_library(
        postproc
        SHARED
        IMPORTED
)
set_target_properties(
        postproc
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libpostproc.so
)

add_library(
        swresample
        SHARED
        IMPORTED
)
set_target_properties(
        swresample
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libswresample.so
)

add_library(
        swscale
        SHARED
        IMPORTED
)
set_target_properties(
        swscale
        PROPERTIES IMPORTED_LOCATION
        ${PREFIX}/lib/libswscale.so
)

# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp)

# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        #ffmpeg链接到native-lib
        avcodec
        avdevice
        avfilter
        avformat
        avutil
        postproc
        swresample
        swscale
        android
        log)

5、修改build.gradle

修改app目录下的build.gradle,在defaultConfig中添加cmake并指定平台,因为我们只导入一个平台的库(我是用手机运行的)

6、编辑Native-lib.cpp

至此,其实就可以运行看一下效果了,急迫的你可以运行试试。不过目前我们还没有调用ffmpeg接口,为了验证我们AS能否正常调用ffmpeg接口,我们就把它调用起来。这里是调用libavcodec/avcodec.h下的avcodec_configuration接口打印配置信息。

Native-lib.cpp新增getAvCodecConfigurationInfo接口,调用avcodec_configuration,将返回的信息作为jni接口返回值。

cpp 复制代码
#include <jni.h>
#include <string>
extern "C"{//必须要添加该声明
#include "libavcodec/avcodec.h"
}


extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ffmpegtestprj_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ffmpegtestprj_MainActivity_getAAvCodecConfigure(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = avcodec_configuration();
    return env->NewStringUTF(hello.c_str());
}

7、修改MainActivity.java

MainActivity.java新增声明一个jni接口:

cpp 复制代码
public class MainActivity extends AppCompatActivity {

    // Used to load the 'ffmpegtestprj' library on application startup.
    static {
        System.loadLibrary("ffmpegtestprj");
    }

    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());
        tv.setText(getAAvCodecConfigure());
    }

    /**
     * A native method that is implemented by the 'ffmpegtestprj' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    public native String getAAvCodecConfigure();
}

这里就不再新增控件了,直接将之前的text显示换成显示getAvCodecConfigurationInfo返回的。

8、效果展示

相关推荐
热爱生活的五柒1 小时前
vscode利用ofExtensions插件可以调试单进程Openfoam,但是不能调试mpi多进程案例
ide·vscode·编辑器
界面开发小八哥1 小时前
更高效的Java 23开发,IntelliJ IDEA助力全面升级
java·开发语言·ide·intellij-idea·开发工具
Dnelic-1 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen4 小时前
MTK Android12 user版本MtkLogger
android·framework
小江村儿的文杰10 小时前
XCode Build时遇到 .entitlements could not be opened 的问题
ide·macos·ue4·xcode
为什么每天的风都这么大11 小时前
Vscode/Code-server无网环境安装通义灵码
ide·vscode·阿里云·编辑器·ai编程·code-server
长亭外的少年11 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿14 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神15 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛15 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee