Android studio准备

Android studio需要下载Android6.0版本(Android SDK Platform 23),最小支持Android6.0

NDK 下载

android ndk读写文件 android ndk视频教程_bc


cmake下载安装

android ndk读写文件 android ndk视频教程_音视频_02

Android studio 代理配置

dl.google.com可能会被屏蔽,首先查下其IP地址。查到IP地址后修改etc/hosts文件。

android ndk读写文件 android ndk视频教程_android ndk读写文件_03

Android 市场份额

Android 个版本市场份额

android ndk读写文件 android ndk视频教程_音视频_04


Android 支持6.0就可以了。

CMake

C/C++ 编译成so库,需要使用到CMake。

新建一个Empty的Android工程,我们看下如何从0配置NDK开发环境。

android ndk读写文件 android ndk视频教程_android ndk读写文件_05


然后打开Terminal

进入JNILoader所在的目录,输入如下命令

android ndk读写文件 android ndk视频教程_bc_06

javac JNILoader.java

注意不要忘了后缀.java

android ndk读写文件 android ndk视频教程_bc_07


输入javac -h ./ JNILoader.java,会生成相应的文件,注意./前后都要有空格。

然后将生成的com_example_testndkempty_JNILoader.h文件移动到cpp文件夹中。

然后创建JNILoader.cpp文件,并输入相关内容,如下图

android ndk读写文件 android ndk视频教程_android_08


注意,该文件代码错误,第九行代码应该是 return env->NewStringUTF(labelString.c_str());我截图中忘了增加()

接下来我们使用CMake编译cpp文件夹下的文件。

在app文件夹下创建CMakeLists.txt文件,注意,名字一定不要写错

android ndk读写文件 android ndk视频教程_android ndk读写文件_09


CMakeLists.txt文件内容

#这里在SDK里面查看cmake支持的版本
cmake_minimum_required(VERSION 3.10)
#Test221123 编译成动态库,编译的时候使用JNILoader.cpp文件
add_library(Test221123 SHARED src/main/cpp/JNILoader.cpp)
#需要使用到的系统库
find_library(log-lib log)
#连接到哪些库
target_link_libraries(Test221123 ${log-lib})

Test221123是在JNILoader类中起的名字,详见上文。
Gradle文件中的配置,有两个地方需要新增,这两个地方已经加了汉语注释

plugins {
    id 'com.android.application'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.testndkempty"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        //编译可增加些flags,但我们这里不增加任何东西
        externalNativeBuild{
            cmake{
                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
    }
    externalNativeBuild{
        //cmake 文件的路径
        cmake{
            path "CMakeLists.txt"
        }
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

运行项目,build会出现下面的文件夹。

android ndk读写文件 android ndk视频教程_android ndk读写文件_10


我们也可以指定编译的ndk 种类。

android ndk读写文件 android ndk视频教程_音视频_11


gradle里增加如下代码

ndk{
            abiFilters 'x86_64','armeabi-v7a'
        }

android ndk读写文件 android ndk视频教程_bc_12


这里的代码会有红线,原因就是我们在c代码中使用了c++,可以做如下修改

//
// Created by 王学岗 on 2022/11/23.
//
#include <string>
#include "com_example_testndkempty_JNILoader.h"

extern "C" jstring Java_com_example_testndkempty_JNILoader_getNativeString
        (JNIEnv *env, jobject obj) {
    std::string labelString = "Hello 音视频开发";
    return env->NewStringUTF(labelString.c_str());
}

或者这样写,也可以消除红线

//
// Created by 王学岗 on 2022/11/23.
//
#include <string>
#include "com_example_testndkempty_JNILoader.h"
#ifdef __cplusplus
extern "C" {
#endif
jstring Java_com_example_testndkempty_JNILoader_getNativeString
        (JNIEnv *env, jobject obj) {
    std::string labelString = "Hello 音视频开发";
    return env->NewStringUTF(labelString.c_str());
}

#ifdef __cplusplus
}
#endif

第一个FFmpeg程序

把FFmpeg库引入

android ndk读写文件 android ndk视频教程_android_13


修改CMakeLists

cmake_minimum_required(VERSION 3.10)
#设置FFMPENG_DIR   CMAKE_CURRENT_SOURCE_DIR 在cmake同一级目录下找
SET(FFMPENG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/libFFmpeg/armeabi-v7a)
#引用FFMPENG_DIR
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/include)
#链接lib
LINK_DIRECTORIES(${FFMPENG_DIR}/lib)
#打印日志
MESSAGE(STATUS "${FFMPENG_DIR}/include")
MESSAGE(STATUS "${FFMPENG_DIR}/lib")


#Test221123 编译成动态库,编译的时候使用JNILoader.cpp文件
add_library(Test221123 SHARED src/main/cpp/JNILoader.cpp)
#需要使用到的系统库
find_library(log-lib log)
#连接到那哪些库 z c m dl ${log-lib}是系统自带的
target_link_libraries(Test221123 avformat avcodec avdevice avfilter avutil fdk-aac x264 z c m dl ${log-lib})

修改com_example_testndkempty_JNILoader.h和JNILoader.cpp文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_testndkempty_JNILoader */

#ifndef _Included_com_example_testndkempty_JNILoader
#define _Included_com_example_testndkempty_JNILoader
#ifdef __cplusplus
extern "C" {
#endif
    //引入FFmpeg库
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavutil/avutil.h>
/*
 * Class:     com_example_testndkempty_JNILoader
 * Method:    getNativeString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_testndkempty_JNILoader_getNativeString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
//
// Created by 王学岗 on 2022/11/23.
//
#include <string>
#include "com_example_testndkempty_JNILoader.h"
#ifdef __cplusplus
extern "C" {
#endif
jstring Java_com_example_testndkempty_JNILoader_getNativeString
        (JNIEnv *env, jobject obj) {
    char buff[10240] = {0};
    //avcodec_configuration得到FFmpeg编译时配置的字符串
    sprintf(buff,"%s",avcodec_configuration());
    return env->NewStringUTF(buff);
}

#ifdef __cplusplus
}
#endif

看下运行效果

android ndk读写文件 android ndk视频教程_bc_14

cmakeList实战

android ndk读写文件 android ndk视频教程_android_15


cpp文件如上

cmakeList文件代码如下

#这里在SDK里面查看cmake支持的版本
cmake_minimum_required(VERSION 3.0.0)

#设置FFMPENG_DIR   CMAKE_CURRENT_SOURCE_DIR 在cmake同一级目录下找
SET(FFMPENG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp)
#引用FFMPENG_DIR
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/camera_preview)
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/libcommon/egl_core)
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/libcommon/message_queue)
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/libcommon/opengl_media)
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/libcommon/opensl_media)
INCLUDE_DIRECTORIES(${FFMPENG_DIR}/libcommon)

#Test221123 编译成动态库,编译的时候使用JNILoader.cpp文件
add_library(innerScreenLauncher SHARED src/main/cpp/ChangbaRecordingPreviewScheduler.cpp
        src/main/cpp/camera_preview/mv_recording_preview_controller.cpp
        src/main/cpp/camera_preview/recording_preview_renderer.cpp
        src/main/cpp/libcommon/egl_core/egl_core.cpp
        src/main/cpp/libcommon/egl_core/egl_share_context.cpp
        src/main/cpp/libcommon/message_queue/handler.cpp
        src/main/cpp/libcommon/message_queue/message_queue.cpp
        src/main/cpp/libcommon/opengl_media/render/video_gl_surface_render.cpp
        src/main/cpp/libcommon/opengl_media/texture/gpu_texture_frame.cpp
        src/main/cpp/libcommon/opengl_media/texture/texture_frame.cpp
        src/main/cpp/libcommon/opengl_media/texture/yuv_texture_frame.cpp
        src/main/cpp/libcommon/opengl_media/texture_copier/gpu_texture_frame_copier.cpp
        src/main/cpp/libcommon/opengl_media/texture_copier/texture_frame_copier.cpp
        src/main/cpp/libcommon/opengl_media/texture_copier/yuv_texture_frame_copier.cpp
        src/main/cpp/libcommon/opengl_media/movie_frame.cpp
        src/main/cpp/libcommon/opensl_media/opensl_es_context.cpp
        src/main/cpp/libcommon/matrix.cpp
        src/main/cpp/libcommon/thread.cpp)

#需要使用到的系统库
find_library(log-lib log)
#连接到哪些库
#${log-lib} OpenSLES -lGLESv2 -lEGL android为系统库
target_link_libraries(innerScreenLauncher ${log-lib} OpenSLES -lGLESv2 -lEGL android)