前言:经过前几篇的介绍,我们已经掌握在linux下编译ffmpeg.so。我们编译出so,目的是为了能够使用so达到使用ffmpeg相关功能的目的。本篇介绍如何在android上使用ffmpeg的so库的基础功能。本文要求读者有一定的jni基础。

本文开发环境:

ffmpeg版本:ffmpeg4.2.2

androidStudio:2022.1.1 Patch 1;gradle版本:7.4.1;ndk21(必须要有ndk环境)

ffmpeg专辑(一):linux编译ffmpeg.so入门

ffmpeg专辑(二):linux编译ffmpeg.so脚本改良与裁编

ffmpeg专辑(三):linux编译ffmpeg.so多架构编译

ffmpeg专辑(四):linux编译ffmpeg.so合并so篇


一、androidStudio新建jni(C++)项目

Android 平台学习ffmpeg_Android 平台学习ffmpeg

Android 平台学习ffmpeg_ffmpeg_02

Android 平台学习ffmpeg_android_03

Android 平台学习ffmpeg_linux_04

 (新建后,这是默认的目录结构)

Android 平台学习ffmpeg_ffmpeg_05


二、导入linux生成的ffmepg的7个so文件和头文件

1、在libs目录下新建文件夹arm64-v8a(本文只引入arm64-v8a版本,读者可以参照自行引入armeabi-v7a版本),在其中复制进来ffmepg相关的7个so文件,在cpp目录下导入include的C头文件:

Android 平台学习ffmpeg_android_06

 2、编辑CMakeLists.txt联编脚本

cmake_minimum_required(VERSION 3.22.1)
#自动生成,自己的项目名
project("ffmpegndk")

message("start............................................................")
# 1. 定义so库和头文件所在目录相对与脚本的位置的变量,方面后面使用
set(ffmpeg_lib_dir_neway ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/include)

# 2. 添加头文件目录
include_directories(${ffmpeg_head_dir})

#==========================ffmpeg引入开始====================================
#add_library(配置模块so库编译信息),set_target_properties(为目标配置编译属性),对于三方so,这两个方法要成对出现
add_library( avcodec
        SHARED
        IMPORTED)
set_target_properties(
        avcodec
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libavcodec.so)

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

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

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

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

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

add_library( swscale
        SHARED
        IMPORTED)
set_target_properties(
        swscale
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libswscale.so)
#==========================ffmpeg引入结束====================================

#对最后一个自己的so(名称可以自定义),不需要再set_target_properties。这个so最终会生成到build中。
add_library(
        ffmpegNdkCustom
        SHARED
        native-lib.cpp)

#查找代码中使用到的android系统预留(预构建)库(如log、zlib压缩库等)
find_library(
        log-lib
        log)

#指定编译最终目标库时,cmke要链接的库。
#解释:一般形式是:target_link_libraries(your-lib library1 library2 ...)
target_link_libraries(
        #指定目标库
        ffmpegNdkCustom
        #连接 FFmpeg 相关的库
        avcodec
        avdevice
        avfilter
        avformat
        avutil
        swresample
        swscale
        ${log-lib})

 3、编辑native-lib.cpp文件,编写native方法。

下面的 stringFromJNI 是项目默认帮我们生成的默认方法,getConfiguration是我们测试ffmpeg获取版本号的一个方法

#include <jni.h>
#include <string>
extern "C" {
#include "libavcodec/avcodec.h"
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_neway_ffmpegndk_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_neway_ffmpegndk_MainActivity_getConfiguration(JNIEnv *env, jobject thiz) {
    return env->NewStringUTF(avcodec_configuration());
}

三、app中使用

1、修改MainActivity方法:

package com.neway.ffmpegndk;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.neway.ffmpegndk.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
    //用于在程序启动时加载cmake编译好的目标库ffmpegNdkCustom.so,注意大小写
    static {
        System.loadLibrary("ffmpegNdkCustom");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        TextView tv = binding.sampleText;
        //tv.setText(stringFromJNI());
        tv.setText(getConfiguration());
    }

    /**
     * 在 ffmpegNdkCustom.so 库中声明的原生c++的native方法,起连接作用
     */
    public native String stringFromJNI();
    public native String getConfiguration();
}

2、修改app级别的build.gradle文件,用以使app支持arm64-v8a架构:

android {
    namespace 'com.neway.ffmpegndk'
    compileSdk 33

    defaultConfig {
        applicationId "com.neway.ffmpegndk"
        minSdk 21
        targetSdk 33
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'arm64-v8a'//,'armeabi-v7a'//,'x86','arm64-v8a',
            }
        }
    }
    ...
}

3、运行(因为指定了arm64-v8a架构,故不能在电脑模拟器上运行,需要真机,电脑模拟器需要编译使用x86架构的),即展示了我们的ffmpeg版本号:

Android 平台学习ffmpeg_linux编译_07

4、此时,我们也可以观察下,cmake联编的目标so同时也生成在了我们的build目录下:

Android 平台学习ffmpeg_android_08


 后记:

本文使用了ffmpeg未合并的7个so文件方式,当然也可以使用合并so(见系列文章第四篇ffmpeg专辑(四):linux编译ffmpeg.so合并so篇)后的ffmpeg.so文件,此时libs文件夹和cmake联编文件需要变更,其余地方保持不变:

Android 平台学习ffmpeg_linux编译_09

cmake_minimum_required(VERSION 3.22.1)

project("ffmpegndk")

message("start............................................................")
# 1. 定义so库和头文件所在目录,方面后面使用
set(ffmpeg_lib_dir_neway ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/include)

# 2. 添加头文件目录
include_directories(${ffmpeg_head_dir})

#==========================ffmpeg引入开始====================================
#ffmpegsofile为自定义名称,libffmpeg.so是libs目录下的linux编译出的so文件
add_library( ffmpegsofile
        SHARED
        IMPORTED)
set_target_properties(
        ffmpegsofile
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libffmpeg.so)
#==========================ffmpeg引入结束====================================

#对最后一个自己的so,不需要再set_target_properties。这个so最终会生成到build中。
add_library(
        ffmpegNdkCustom
        SHARED
        native-lib.cpp)

find_library(
        log-lib
        log)

target_link_libraries(
        #指定目标库
        ffmpegNdkCustom
        #连接 FFmpeg 相关的库
        ffmpegsofile
        ${log-lib})