preface

In daily App development, it is inevitable that some functions need the help of NDK to complete, such as now common audio and video processing, today to FFMPEG, to learn the Android NDK development routine.

JNI and the NDK

Many people don’t clear the concept of JNI and NDK, often confusing the two things, but let’s take a look at their respective definitions.

JNI

  • What is the

    JNI is short for Java Native Interface, which provides several apis to implement communication between Java and other languages (mainly C&C++).

  • Designed to

    The standard Java class libraries do not support the features your program needs. Or you already have a library or program written in another language that you want to use in Your Java programs. Or maybe you need a high-performance library to do something.Copy the code
  • Using the step

    1. Write Java classes with native declared methods
    2. Compile the Java classes you wrote using the Javac command
    3. Then use javah + Java class name to generate a header file with extension H
    4. Implement native methods using C/C++
    5. Create a dynamic link library * from a file written in C/C++.
    6. Call native methods in Java code

NDK

The NDK, which stands for Native Development Kit, is an Android Development Kit that has nothing to do with Java.

One of the core purposes of the NDK is to let you build C and C++ source code into shared libraries that you can use in your applications. Well, it provides cross-compilation.

The CPU architecture

We all know what a CPU is, but what exactly is a CPU architecture? Returning to the meaning of the word “architecture” itself, CPU architecture is the framework structure and design scheme of CPU, processor manufacturers produce their own CPU based on a certain architecture, just as “total-dem-total” is a kind of architecture of the article, many articles can be based on “total-dem-total” architecture.

Common CPU architectures include x86, x86-64, arm, etc. X86-64 is actually based on x86 architecture, but some extensions are made on the basis of x86 to support 64-bit applications. Common Intel and AMD processors are based on x86 architecture.

The x86 architecture focuses on THE PC end. For the mobile end, THE ARM architecture dominates. Due to its advantages of small size, low power consumption, low cost and high performance, it is widely used in embedded systems. The ARM architecture here refers to the ARM series architecture, including ARMv5, ARMv7 and so on.

Android supports seven CPU architectures: ARMv5, ARMv7, ARMv8, x86, X86_64, MIPS, and MIPS64. This means that hardware with other CPU architectures will not run Android.

cross-compilation

Compiling executables on a platform is called native compilation, for example, compiling Windows executables on a Windows platform. On the x86 platform, compile executable programs for the x86 platform itself.

Cross-compilation is used to compile an executable on one platform for another, such as the arm executable on x86, which is the most commonly used type of cross-compilation on Android.

During cross-compilation, due to the different architecture and environment of host and target, cross-compilation is much more complicated than local compilation. Some tools are needed to solve the problem of different features of host and target. The tool set composed by these tools is called cross-compilation chain.

Since cross-compilation is much more complicated than native compilation, why not use native compilation, such as compiling an ARM executable? This is because the target platform usually has limited storage space and computing power, and the compilation process requires large storage space and faster computing power that the target platform does not provide.

NDK is used in the project

Here is an official document, written in Chinese, which goes into great detail: Adding C and C++ code to your project. I strongly recommend reading this section carefully

CMake

There are two ways to build NDK, one is the early use of ndK-build, one is recommended after Android Studio2.2 cmake, we will only talk about the recommended cmake this way.

CMakeLists. TXT spelled

  • Add_library adds the library to the project using the specified source file

    1. Ordinary library

      / / add common library grammar add_library (< name > [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source[1]source2... ] // Create the default generated example in the NDK project 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 )
      Copy the code

      There’s nothing to say about the name attribute, just note that it’s globally unique.

      [STATIC | SHARED | MODULE] is generated by the library types, STATIC generated is STATIC library, that is, a suffix. We usually use SHARED to generate dynamic link library, also known as.so suffix.

    2. Import libraries

      / / grammar add_library (< name > < SHARED | STATIC | MODULE | OBJECT | UNKNOWN > IMPORTED [GLOBAL]) / / import compiled ffmpeg sample add_library (ffmpeg Set_target_properties (ffMPEG PROPERTIES IMPORTED_LOCATION.. /.. /.. /.. /libs/armeabi-v7a/libffmpeg.so )Copy the code

      This will import the.so library that we’ve compiled externally

      There are a few others THAT I haven’t used yet, but you can refer to the official documentation for add_library

  • Include_directories are used to import related header files

    include_directories(src/main/cpp)
    Copy the code
  • Find_library is used to import libraries provided in the NDK. Android NDK native API

    find_library(
              # define the name of the path variable where the NDK library is stored.
              log-lib
              
              # specify the name of the NDK library that CMake is looking for.
              log )
    Copy the code
  • Target_link_libraries associates the imported library with its own native library

    target_link_libraries( 
                       Specify the target library.
                       native-lib
                       
                       # link the target library to the logging library included in the NDK.
                       ${log-lib} )
    Copy the code

FFmpeg

FFmpeg is an open source framework for recording, processing, and streaming digital audio and video. Under the LPL or GPL license, FFmpeg provides a complete solution for recording, converting, and streaming audio and video. The MPEG in the name comes from the video coding standard MPEG, and the prefix FF is an acronym for Fast Forward. Audio and video processing open source library, can complete the vast majority of audio and video related functions. Many well-known software, open source libraries are based on it for secondary development, such as Bilibi’s ijkPlayer.

Making a link

Compile FFmpeg

FFmpeg is similar to most GNU software in that it uses configure scripts for pre-compilation customization, which allows users to tailor software before compilation and determine the appropriate configuration for certain modules based on the system and target platform they are running on. The Makefile is generated using configure, and then compiled and installed using make and make install.

  1. Configure the environment

    First of all, we need to prepare the relevant compilation environment. It is recommended to compile in Linux for simple configuration and few problems. Of course, Mac is fine, not Windows.

    1. Linux (Ubuntu 16.04) WindowsDownload aVMware WorkstationAnd put aubuntuIt’s convenient.
    2. NDK environment Here is the use of NDK-R17, attached with the relevant download link NDK download
    3. Download FFmpeg source code FFmpeg download address
  2. Modifying the configure file

    Because FFmpeg default generated library file format is libavcodec.so.xx.xx.x. Xx is the major and minor version number. This format is fine for Ubuntu, but it is not considered a valid library file for Android development. So you need to change the format of the other generated file names.

    To do this, open configure and find the following:

    SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'  
    LIB_INSTALL_EXTRA_CMD='? (RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'  
    SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'
    Copy the code

    Is amended as:

    SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
    LIB_INSTALL_EXTRA_CMD='? (RANLIB)"$(LIBDIR)/$(LIBNAME)"'
    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
    SLIB_INSTALL_LINKS='$(SLIBNAME)'
    Copy the code
  3. Create a build.sh script in the FFmpeg root directory to make configuring configure easier. As follows:

    #! /bin/bash
    Configure the NDK path
    NDK=/home/xinyang/develop/android-ndk-r17
    # specifies a cross-compilation environment that can reference native headers and shared library files provided by the NDK during compilation
    SYSROOT=$NDK/platforms/android-23/arch-arm/
    TOOLCHAIN=$NDK/ toolchains/arm - Linux - androideabi - 4.9 / prebuilt/Linux - x86_64# declare method
    function build_one
    {
    ./configure \
    --prefix=$PREFIX \      Set the output path
    --enable-shared \       Open dynamic library output
    --disable-static \      Turn off static library output
    --disable-doc \         # Turn off unwanted features
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-avdevice \
    --disable-symver \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \  # specify the cross-compile tool chain
    --target-os=linux \     The target system android is based on Linux
    --arch=armeabi-v7a \    # Target platform architecture
    --enable-cross-compile \# enable cross-compilation
    --sysroot=$SYSROOT \    # cross-compile environment
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
    make clean
    make
    make install
    }
    CPU=armeabi-v7a
    PREFIX=$(pwd)/android/$CPU
    ADDI_CFLAGS="-marm"
    build_one
    Copy the code

    –cross-prefix

    This is similar to the arm-linux-androideabi- cross-compilation tool in the bin directory. If this configuration is not supported, specify it separately:

    • CC:$TOOLCHAIN/bin/arm-linux-androideabi-gcc
    • CXX:$TOOLCHAIN/bin/arm-linux-androideabi-g++
    • AR:$TOOLCHAIN/bin/arm-linux-androideabi-ar packer for library operations, you can use this tool to remove or add object code modules from a library.
    • LD:$TOOLCHAIN/bin/arm-linux-androideabi-ld linker, allocates address space for previously generated object code, and links multiple object files into a library or executable file.
  4. Run the script CD to the ffmpeg directory

    chmod 777 build.sh
    Copy the code

    Change the executable permission of the script file

    ./build.sh
    Copy the code

    Then execute the script, the whole process is slow, just wait patiently, the whole process takes about 5-10 minutes. After compiling, you can see the following image, including some header files,lib is the generated. So dynamic library

Integrated FFmpeg

Now that we’re ready to integrate the generated.so file into our project, let’s take a look at the steps:

  1. Add C and C++ code to your project by following the tutorial here;

  2. Copy the generated. So file to libs directory (or jniLibs);

  3. Copy the generated include folder to the CPP directory.

  4. Copy files from ffmpeg\ ffTools to CPP.

  5. Writing native methods

    package com.xinyang.ndkdemo;
    
    public class FFmpegCmd {
    
    
    static {
        System.loadLibrary("ffmpeg");
    }
    
    
    
    public native static void handle();
    
    }
    Copy the code
  6. Create ffmpeg_cmd.c file in CPP directory to realize native method. Here, you can use javah to generate header file and realize again, or you can directly use shortcut key prompt in Java class to directly generate method:

    #include <jni.h>
    #include <malloc.h>
    #include <string.h>
    #include <android/log.h>
    #include "ffmpeg/ffmpeg.h"
    
    JNIEXPORT void  JNICALL Java_com_xinyang_ndkdemo_FFmpegCmd_handle
    (JNIEnv *env, jclass obj){
        char info[40000] = {0};
        av_register_all();
        AVCodec *c_temp = av_codec_next(NULL);
        while(c_temp ! = NULL){if(c_temp->decode! =NULL){ sprintf(info,"%s[Dec]",info);
            }else{
                sprintf(info,"%s[Enc]",info);
            }
            switch(c_temp->type) {case AVMEDIA_TYPE_VIDEO:
                    sprintf(info,"%s[Video]",info);
                    break;
                case AVMEDIA_TYPE_AUDIO:
                    sprintf(info,"%s[Audio]",info);
                    break;
                default:
                    sprintf(info,"%s[Other]",info);
                    break;
            }
            sprintf(info,"%s[%10s]\n",info,c_temp->name);
            c_temp=c_temp->next;
        }
        __android_log_print(ANDROID_LOG_INFO,"myTag"."info:\n%s",info);
    }
    Copy the code

    < android/log.h > __android_log_print directly to android Studio logcat.

  7. Edit cmakelists. TXT to import the relevant.so file, use add_library to import the generated.so file, use include_directories to import the header file, and then use target_link_libraries to merge the imported libraries with the generated.so file The target library is associated as follows:

    # 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 track)# 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.
    
    add_library( # Sets the name of the library.
             ffmpeg
    
             # Sets the library as a shared library.
             SHARED
    
             # Provides a relative path to your source file(s).src/main/cpp/ffmpeg_cmd.c src/main/cpp/ffmpeg/cmdutils.c src/main/cpp/ffmpeg/ffmpeg.c src/main/cpp/ffmpeg/ffmpeg_filter.c src/main/cpp/ffmpeg/ffmpeg_opt.c ) include_directories(src/main/cpp) include_directories(src/main/cpp/include) add_library( avutil-55 SHARED IMPORTED ) set_target_properties( avutil-55 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libavutil-55.so ) add_library( avcodec-57 SHARED IMPORTED ) set_target_properties( avcodec-57 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libavcodec-57.so ) add_library( avformat-57 SHARED IMPORTED ) set_target_properties( avformat-57 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libavformat-57.so ) add_library( avdevice-57 SHARED IMPORTED ) set_target_properties( avdevice-57 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libavdevice-57.so ) add_library( swresample-2 SHARED IMPORTED ) set_target_properties( swresample-2 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libswresample-2.so ) add_library( swscale-4 SHARED IMPORTED ) set_target_properties( swscale-4 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libswscale-4.so ) add_library( postproc-54 SHARED IMPORTED ) set_target_properties( postproc-54 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libpostproc-54.so ) add_library( avfilter-6 SHARED IMPORTED ) set_target_properties( avfilter-6 PROPERTIES IMPORTED_LOCATION .. /.. /.. /.. /libs/armeabi-v7a/libavfilter-6.so )# 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 )
    
    # 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.
                        ffmpeg
                        avutil-55
                        avcodec-57
                        avformat-57
                        avdevice-57
                        swresample-2
                        swscale-4
                        postproc-54
                        avfilter-6
    
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
    Copy the code
  8. Try calling the native method and view the output in logcat as follows:

conclusion

In general, it is easy to use CMake. Just write cmakelists. TXT and specify the location of the file in Gradle. The focus is on cross-compiling related libraries and writing C files that call related API files, which requires some C basics.

reference

Android integration FFmpeg (a) basic knowledge and simple call

Add C and C++ code to your project

xufuji456/FFmpegAndroid