上篇介绍了如何 在 mac 系统下打包 ffmpeg 的 so

这篇介绍如何引入 so 到项目中

开发环境:

macos
android studio 3.2.1
ndk: 15.2.4203891

新建一个工程

新建工程时,勾选 c++支持

20190114171810.png

将文件复制到项目内

需要复制的文件

.h 头文件
so文件

20190114172127.png

20190114172229.png

在 cpp 中新建一个文件夹include/ffmpeg 把 include 中的文件夹复制到 ffmpeg 下

在 main 下新建文件夹jniLibs,把 so 文件复制到其中 20190114172435.png

修改 CMakeList

完整的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.4.1)

# 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.

#
#
set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs)

#


#set(FFDIR /Users/cai/code/c/ffmpeg-3.3.6/android/arm/lib)

include_directories(
        src/main/cpp/include/ffmpeg
)

# ffmpeg 的library
add_library(
        avcodec
        SHARED
        IMPORTED
)
set_target_properties(
        avcodec
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so
)

add_library(
        avdevice
        SHARED
        IMPORTED
)
set_target_properties(
        avdevice
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavdevice-57.so
)

add_library(
        avfilter
        SHARED
        IMPORTED
)
set_target_properties(
        avfilter
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavfilter-6.so
)

add_library(
        avformat
        SHARED
        IMPORTED
)
set_target_properties(
        avformat
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavformat-57.so
)

add_library(
        avutil
        SHARED
        IMPORTED
)
set_target_properties(
        avutil
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavutil-55.so
)


add_library(
        swresample
        SHARED
        IMPORTED
)
set_target_properties(
        swresample
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswresample-2.so
)

add_library(
        swscale
        SHARED
        IMPORTED
)
set_target_properties(
        swscale
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswscale-4.so
)

# ffmpeg 的library end

# 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)

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).
        src/main/cpp/native-lib.cpp
        )

# 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.
        native-lib

        # ffmpeg 相关库
        avcodec
        avdevice
        avfilter
        avformat
        avutil
        swresample
        swscale
        #

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

简单解释下:

# 设置so文件的目录
set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs)
# 引入头文件
include_directories(
        src/main/cpp/include/ffmpeg
)
#视为一组

# 第一个为库名称,类型,已导入
add_library(
        avcodec
        SHARED
        IMPORTED
)

# 库名称, 属性名称, 文件路径
set_target_properties(
        avcodec
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so
)
# 这两个是新建项目时自带的, 这个应该是ndk中的库
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)

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).
        src/main/cpp/native-lib.cpp
        )

# 最后是连接到项目中
target_link_libraries( # Specifies the target library.
        native-lib

        # ffmpeg 相关库
        avcodec
        avdevice
        avfilter
        avformat
        avutil
        swresample
        swscale
        #

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

作用解释没有那么准确,大概理解意思就行

修改 cpp/kotlin 文件

新建项目时会有一个 cpp 文件,

#include <jni.h>
#include <string>

// 这里的导入是我们新建的
extern "C" {
#include <libavutil/log.h>
#include <libavformat/avformat.h>
}

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


// 这里是我们自己新建的方法
extern "C" JNIEXPORT jint JNICALL
Java_top_kikt_ffmpegandroidexample_MainActivity_dumpInfo(
        JNIEnv *env,
        jobject) {

    av_log_set_level(AV_LOG_DEBUG);
    av_register_all();

    return 1;
}

有 2 个方法,第二个是我们自定义的, 导包部分引入了 avutil 和 avformat 的头文件

这里有一点要格外注意 extern "C" 我们必须要用这个,这个是在 cpp 中使用 c 文件的必备方法

不然在运行时会报错,当然如果你本身是 c 文件就不存在这个问题了


然后修改 kotlin 文件

package top.kikt.ffmpegandroidexample

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Example of a call to a native method
        sample_text.text = stringFromJNI()

        sample_text.text = dumpInfo().toString()
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    external fun stringFromJNI(): String

    external fun dumpInfo(): Int

    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("native-lib")
            System.loadLibrary("avformat")
            System.loadLibrary("avutil")
        }
    }
}

这里可以看到我们输出的内容来源于自己新建的内容 dumpInfo 会调用到刚刚在 cpp 中定义的第二个方法,并且运行时 loadLibrary 也没有报找不到 so 的异常, 这就说明成功了

后记

本篇在 android 项目中引入了 ffmpeg ,完成了这一步的跨越后,很多 ffmpeg 的功能就可以自己写 jni 方法来调用了!

比如直播,滤镜,yuv 处理等等,依托于 ffmpeg 强大的生态,音视频领域可以说有大半可做了!