Android Studio 单独编译WebRTC的 vad 模块

Android Studio 3.0.1

Mac OS X 10.12.6

先配置 jni 环境,按照一年前写的文章 android Studio jni 编程 进行配置在build 工程的时候出现错误

Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
  Consider using CMake or ndk-build integration. For more information, go to:
   https://d.android.com/r/studio-ui/add-native-code.html
  Alternatively, you can use the experimental plugin:
   https://developer.android.com/r/tools/experimental-plugin.html

然后搜索到 Android Studio 的文档 向您的项目添加 C 和 C++ 代码

原生库构建的默认工具是 CMake, 所以决定改用 CMake.

首先是配置环境, 向您的项目添加 C 和 C++ 代码 中有就不详细介绍了,首先下载 NDK、CMake、LLDB.
然后创建一个只是 C++的项目看看与以前ndk-build的时候有啥不一样.

build.gradle 文件中,多了 两个 externalNativeBuild 参数 ,defaultConfig 下的 externalNativeBuild 里面的参数 是在创建工程的时候选择的,
-fexceptions 对应创建工程时的选项 Exceptions Support,是启用对 C++异常处理的支持
-frtti 对应创建工程时的选项 Runtime Type Information Support 选项,支持 RTTI 的




多了一个 CMakeyLists.txt 文件 里面的内容就4个函数
cmake_minimum_required,add_library,find_library,target_link_libraries




不同就这些,在现有工程上照着弄一遍就好了.下面下载 webrtc 源码.

在终端 执行命令

git clone https://android.googlesource.com/platform/external/webrtc
下载成功后 

cd webrtc/common_audio/vad 
然后将里面的文件(带 test 的不要)拷贝到统一个目录下(vad_src),然后打开 vad_core.c 文件,根据 #include 的文件路径去找到相应的文件并且把他们都拷贝到现在的目录(递归查找拷贝),并将 #include 的路径去掉.
都弄好后在现有工程上根据上面的分析配置环境, 现在 c++文件都存放在 app/src/main/cpp 路径下了.

将 vad_src 文件夹拷贝到 cpp 目录下 ,执行 Build -> Refresh Linked C++ Projects 刷新,然后修改 CMakeLists.txt文件.


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
             src/main/cpp/vad_src/vad_core.c
             			.
             			.	
             			.			)


需要包含的源文件添加到 add_library 函数中,但是文件有好多,一个一个加得累死,幸好 CMake 有一个命令 aux_source_directory ,查找在某个路径下的所有源文件,将搜索到的源文件列表存储到指定的变量中.就像这样

aux_source_directory(src/main/cpp/vad_src/ DIR_LIB_SRCS)

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
             ${DIR_LIB_SRCS})

用aux_source_directory命令的一个缺点就是你添加新的源文件的时候,它不会触发 CMake 编译,你得手动重新运行CMake.


现在编译会报错 
Error:error: C++ requires a type specifier for all declarations
Error:(20, 2) error: "Must define either WEBRTC_WIN or WEBRTC_POSIX."


第二个错误 是没有宏定义的原因
在 CMakeLists.txt 文件中添加 add_definitions(-DWEBRTC_POSIX) 


第一个错误 点击后会跳到c++0x_warning.h 文件有这一句


This file requires compiler and library support for the \
ISO C++ 2011 standard. This support is currently experimental, and must be \
enabled with the -std=c++11 or -std=gnu++11 compiler options.


需要启用 C++11,需要修改 build.gradle 文件 cppFlags 选项 
cppFlags "-std=c++11 -frtti -fexceptions"


继续编译 错误
Error:(21, 10) fatal error: 'execinfo.h' file not found
execinfo.h 是属于glibc的,Android 用的是bionic 

通过 CMake 和 C++ 库支持 知道ANDROID_STL  CMake 默认用 gnustl_static,那就换一个试试 

在 cppFlags 上一行添加 

arguments = ['-DANDROID_STL=c++_static']
然后编译报错
Error:(44, 9) error: reinterpret_cast from 'pthread_t' (aka 'long') to 'pid_t' (aka 'int') is not allowed
出错文件为 platform_thread.cc 文件,看了源码后发现还是确实宏定义,因为现在是在给 android 编译,
所以在 CMakeLists.txt 文件中添加 add_definitions(-DWEBRTC_ANDROID)

没有报错,然后在测试 cpp 文件中引用 #include "webrtc_vad.h"

编译报错
Error:(10, 10) fatal error: 'webrtc_vad.h' file not found

解决方式是 在 CMakeLists.txt 中添加 引用头文件
include_directories(src/main/cpp/vad_src/)

编译成功.

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

aux_source_directory(src/main/cpp/vad_src/ DIR_LIB_SRCS)


add_definitions(-DWEBRTC_POSIX)
add_definitions(-DWEBRTC_ANDROID)

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
             ${DIR_LIB_SRCS})

include_directories(src/main/cpp/vad_src/)

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

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

build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.wolf.webrtc_vad"
        minSdkVersion 15
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                arguments = ['-DANDROID_STL=c++_static']
                cppFlags "-std=c++11 -frtti -fexceptions"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

测试文件 native-lib.cpp

#include <jni.h>
#include <string>
#include <stdio.h>
#include <assert.h>
#include <android/log.h>
#include "webrtc_vad.h"

#define  LOG_TAG    "jnitest"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

static jstring jni_sayHello(JNIEnv *env, jobject)
{

    int ret = 0;
    int i = 0;
    int kRates[] = { 8000, 12000, 16000, 24000, 32000, 48000 };
    int kMaxFrameLength = 16;
    int kFrameLengths[] = { 80, 120, 160, 240, 320, 480, 640, 960 };
    short zeros[160] = {0};
    short speech[160] = {0};

    for (i = 0; i < 160; i++) {
        speech[i] = (i * i);
    }
    VadInst* handle = WebRtcVad_Create();
    WebRtcVad_Init(handle);
    WebRtcVad_set_mode(handle,0);

    ret = WebRtcVad_Process(handle, kRates[2], speech, kFrameLengths[2]);
    LOGD("ret1 = %d\n",ret);

    ret = WebRtcVad_Process(handle, kRates[2], zeros, kFrameLengths[2]);
    LOGD("ret2 = %d\n",ret);
    WebRtcVad_Free(handle);
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

static jboolean jni_calcDecibelLevel(JNIEnv *env, jobject,jshortArray buffer,jint offseetInshort,jint readSize)
{
    VadInst* handle = WebRtcVad_Create();
    WebRtcVad_Init(handle);
    WebRtcVad_set_mode(handle,0);
    short speech[160] = {0};
    int index = readSize/160;
    jshort *pcm_data = env->GetShortArrayElements(buffer, JNI_FALSE);
    bool b;
    for (int i = 0; i < index; ++i) {
        int vad = WebRtcVad_Process(handle, 16000, pcm_data+offseetInshort+i*160, 160);
        if(vad==1)
            b = JNI_TRUE;
    }
    env->ReleaseShortArrayElements(buffer,pcm_data,JNI_ABORT);
    WebRtcVad_Free(handle);
    return b;
}

/**
 * JNINativeMethod由三部分组成:(1)Java中的函数名;
                          (2)函数签名,格式为(输入参数类型)返回值类型;
                          (3)native函数名
 */
static JNINativeMethod gMethods[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (void *)jni_sayHello},
        {"calcDecibelLevelJNI", "([SII)Z", (void *)jni_calcDecibelLevel},
};


jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv *env = NULL;
    jint result = JNI_FALSE;

    if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { ////从JavaVM获取JNIEnv
        LOGD("Error GetEnv\n");
        return result;
    }
    assert(env != NULL);
    //获取类引用,这里可以找到要注册的类,前提是这个类已经加载到java虚拟机中
    jclass clazz = env->FindClass("com/example/wolf/webrtc_vad/RecordAudioPcm");
    if (clazz == NULL) {
        return result;
    }
    //注册方法,把本地函数和一个java类方法关联起来
    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
        return result;
    }

    return JNI_VERSION_1_4;
}

java文件

public class RecordAudioPcm {
    private static final String TAG = "RecordAudioPcm";

    static {
        System.loadLibrary("native-lib");
    }
    public native String stringFromJNI();
    public native boolean calcDecibelLevelJNI(short[] buffer,int offseetInshort, int readSize) ;}




猜你喜欢

转载自blog.csdn.net/langzxz/article/details/78858065