├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── cpp │ │ │ ├── logger.h │ │ │ ├── nvapp.h │ │ │ ├── global_interface.h │ │ │ ├── nv_cam_background.h │ │ │ ├── jniapi.h │ │ │ ├── nvapp.cpp │ │ │ ├── nvrenderer.h │ │ │ ├── jniapi.cpp │ │ │ ├── nv_cam_background.cpp │ │ │ └── nvrenderer.cpp │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── nvision │ │ │ └── facetracker │ │ │ ├── MainActivity.java │ │ │ ├── PermissionHelper.java │ │ │ └── CameraRenderView.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── nvision │ │ │ └── face_tracker_android │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── nvision │ │ └── face_tracker_android │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro ├── build.gradle └── CMakeLists.txt ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── caches │ └── build_file_checksums.ser ├── vcs.xml ├── runConfigurations.xml ├── gradle.xml ├── misc.xml └── codeStyles │ └── Project.xml ├── docs └── Android’s_New_Stream-Based_Camera_Architecture.pdf ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | face_tracker_android 3 | 4 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /docs/Android’s_New_Stream-Based_Camera_Architecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangcan26/CameraCVGLView/HEAD/docs/Android’s_New_Stream-Based_Camera_Architecture.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 08 09:08:45 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/cpp/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H_ 2 | #define LOGGER_H_ 3 | 4 | #include 5 | 6 | #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 7 | #define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 8 | 9 | 10 | 11 | #endif //LOGGER_H_ -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/nvision/face_tracker_android/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.nvision.face_tracker_android; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/nvapp.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_APP_H_ 2 | #define NV_APP_H_ 3 | 4 | #include 5 | 6 | namespace nv 7 | { 8 | namespace render 9 | { 10 | class NVRenderer; 11 | } 12 | class NVApp{ 13 | public: 14 | NVApp(); 15 | 16 | ~NVApp(); 17 | 18 | void Init(); 19 | 20 | void Resume(); 21 | 22 | void Pause(); 23 | 24 | void Deinit(); 25 | 26 | render::NVRenderer *Render(); 27 | 28 | protected: 29 | void CreateGLThread(); 30 | 31 | void DestroyGLThread(); 32 | 33 | private: 34 | render::NVRenderer *renderer_; 35 | 36 | std::thread gl_thread_; 37 | 38 | }; 39 | } 40 | 41 | 42 | #endif //NV_APP_H_ -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CameraCVGLView 2 | 3 | This Project is a collection demos of opengl or computer vision based on android camera2 api 4 | 5 | ## camera2 basic demo 6 | The master branch shows a basic demo for displaying camera preview using a SurfaceView with a SurfaceTexture created by OpenGL ES 7 | 8 | The demo contains two parts: 9 | 10 | ### CameraRenderView.java 11 | 12 | All the functions of camera2 api is encapsulating or to be encapsulated into this java class. 13 | 14 | Its lifecycles are as follows: 15 | 16 | 1. init(Activity activity) 17 | 2. onResume() 18 | 3. onPause() 19 | 4. deinit() 20 | 21 | 22 | 23 | 24 | 25 | ### NVRenderer.h 26 | 27 | This file implements a gl environment for rendering things into android SurfaceView 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Welcom to join QQ Group: 207298950 36 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/cpp/global_interface.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_INTERFACE_H_ 2 | #define GLOBAL_INTERFACE_H_ 3 | #include "jni.h" 4 | 5 | extern "C" 6 | { 7 | typedef union { 8 | JNIEnv *env; 9 | void *venv; 10 | }UnionJNIEnvToVoid; 11 | 12 | extern JavaVM *g_vm; 13 | extern JNIEnv *g_env; 14 | extern int g_attached; 15 | extern UnionJNIEnvToVoid g_uenv; 16 | 17 | extern void android_app_update_tex_image(); 18 | 19 | #define ATTATCH_JNI_THREAD g_attatched = g_vm->AttachCurrentThread(&g_env, NULL);\ 20 | if(g_attatched > 0)\ 21 | {\ 22 | g_vm->DetachCurrentThread();\ 23 | }else{\ 24 | } 25 | 26 | #define DETATCH_JNI_THREAD if(g_attatched <=0)\ 27 | {\ 28 | g_vm->DetachCurrentThread();\ 29 | } 30 | 31 | } 32 | 33 | 34 | 35 | 36 | #endif //GLOBAL_INTERFACE_H_ -------------------------------------------------------------------------------- /app/src/androidTest/java/com/nvision/face_tracker_android/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.nvision.face_tracker_android; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.nvision.face_tracker_android", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/cpp/nv_cam_background.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 18-8-10. 3 | // 4 | 5 | #ifndef FACE_TRACKER_ANDROID_NV_CAM_BACKGROUND_H 6 | #define FACE_TRACKER_ANDROID_NV_CAM_BACKGROUND_H 7 | #include 8 | 9 | 10 | namespace nv 11 | { 12 | namespace render 13 | { 14 | class NVRenderer; 15 | class NVCameraBackground { 16 | public: 17 | NVCameraBackground(NVRenderer *renderer); 18 | ~NVCameraBackground(); 19 | 20 | void Render(bool flip); 21 | 22 | private: 23 | NVRenderer *renderer_; 24 | GLuint vertex_id_; 25 | GLuint uv_id_; 26 | GLuint uv_flip_id; 27 | GLuint indice_id_; 28 | GLuint program_id_; 29 | GLuint position_handle_; 30 | GLuint uv_handle_; 31 | GLuint texture_handle_; 32 | 33 | }; 34 | } 35 | 36 | } 37 | 38 | #endif //FACE_TRACKER_ANDROID_NV_CAM_BACKGROUND_H 39 | -------------------------------------------------------------------------------- /app/src/main/cpp/jniapi.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef JNIAPI_H_ 4 | #define JNIAPI_H_ 5 | 6 | #define NATIVE_METHOD(METHOD_NAME) Java_com_nvision_facetracker_CameraRenderView_##METHOD_NAME 7 | 8 | extern "C" { 9 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeCreateApp)(JNIEnv* jenv, jobject obj); 10 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeResumeApp)(JNIEnv* jenv, jobject obj); 11 | JNIEXPORT void JNICALL NATIVE_METHOD(nativePauseApp)(JNIEnv* jenv, jobject obj); 12 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeDestroyApp)(JNIEnv* jenv, jobject obj); 13 | 14 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeSetSurface)(JNIEnv* jenv, jobject obj, jobject surface); 15 | JNIEXPORT jobject JNICALL NATIVE_METHOD(nativeSurfaceTexture)(JNIEnv* jenv, jobject obj, jboolean flip); 16 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeDestroyTexture)(JNIEnv* jenv, jobject obj); 17 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeRequestUpdateTexture)(JNIEnv* jenv, jobject obj); 18 | } 19 | 20 | #endif //JNIAPI_H_ -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.nvision.face_tracker_android" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | externalNativeBuild { 13 | cmake { 14 | cppFlags "-std=c++11" 15 | } 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | externalNativeBuild { 25 | cmake { 26 | path "CMakeLists.txt" 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | implementation 'com.android.support:appcompat-v7:28.0.0-rc01' 34 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 37 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/cpp/nvapp.cpp: -------------------------------------------------------------------------------- 1 | #include "nvapp.h" 2 | #include "nvrenderer.h" 3 | 4 | namespace nv 5 | { 6 | NVApp::NVApp(): 7 | renderer_(0) 8 | { 9 | 10 | } 11 | 12 | NVApp::~NVApp() { 13 | 14 | } 15 | 16 | void NVApp::Init() { 17 | renderer_ = new render::NVRenderer(); 18 | CreateGLThread(); 19 | } 20 | 21 | void NVApp::Resume() { 22 | if(renderer_ != 0) 23 | { 24 | renderer_->Resume(); 25 | } 26 | } 27 | 28 | void NVApp::Pause() { 29 | if(renderer_ != 0) 30 | { 31 | renderer_->Pause(); 32 | } 33 | } 34 | 35 | void NVApp::Deinit() { 36 | 37 | 38 | if(renderer_ != 0) 39 | { 40 | renderer_->Destroy(); 41 | DestroyGLThread(); 42 | delete renderer_; 43 | renderer_ = 0; 44 | } 45 | 46 | } 47 | 48 | render::NVRenderer *NVApp::Render() 49 | { 50 | return renderer_; 51 | } 52 | 53 | void NVApp::CreateGLThread() { 54 | if(renderer_ != 0) 55 | { 56 | gl_thread_ = std::thread(&render::NVRenderer::_Run, renderer_); 57 | } 58 | } 59 | 60 | void NVApp::DestroyGLThread() { 61 | if(renderer_ != 0) 62 | { 63 | if(gl_thread_.joinable()) 64 | gl_thread_.join(); 65 | } 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/nvision/facetracker/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.nvision.facetracker; 2 | 3 | import android.Manifest; 4 | import android.content.pm.PackageManager; 5 | import android.support.v4.content.ContextCompat; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.widget.TextView; 9 | 10 | import com.nvision.face_tracker_android.R; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | private CameraRenderView mCameraView; 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | 19 | PermissionHelper.requestCameraPermission(this, true); 20 | 21 | 22 | setContentView(R.layout.activity_main); 23 | 24 | // Example of a call to a native method 25 | mCameraView = (CameraRenderView) findViewById(R.id.camera_view); 26 | mCameraView.init(this); 27 | } 28 | 29 | @Override 30 | protected void onResume() { 31 | super.onResume(); 32 | if(mCameraView != null) 33 | { 34 | mCameraView.onResume(); 35 | } 36 | } 37 | 38 | @Override 39 | protected void onPause() { 40 | if(mCameraView != null) 41 | { 42 | mCameraView.onPause(); 43 | } 44 | super.onPause(); 45 | } 46 | 47 | @Override 48 | protected void onDestroy() { 49 | super.onDestroy(); 50 | if(mCameraView != null){ 51 | mCameraView.deinit(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | nvision_core 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | src/main/cpp/jniapi.cpp 21 | src/main/cpp/nvapp.cpp 22 | src/main/cpp/nvrenderer.cpp 23 | src/main/cpp/nv_cam_background.cpp) 24 | 25 | # Searches for a specified prebuilt library and stores the path as a 26 | # variable. Because CMake includes system libraries in the search path by 27 | # default, you only need to specify the name of the public NDK library 28 | # you want to add. CMake verifies that the library exists before 29 | # completing its build. 30 | 31 | 32 | # Specifies libraries CMake should link to your target library. You 33 | # can link multiple libraries, such as libraries you define in this 34 | # build script, prebuilt third-party libraries, or system libraries. 35 | 36 | target_link_libraries( # Specifies the target library. 37 | nvision_core 38 | # Links the target library to the log library 39 | # included in the NDK. 40 | android 41 | EGL 42 | GLESv2 43 | log 44 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/cpp/nvrenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_RENDERER_H_ 2 | #define NV_RENDERER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace nv 10 | { 11 | namespace render 12 | { 13 | class NVCameraBackground; 14 | class NVRenderer 15 | { 16 | public: 17 | NVRenderer(); 18 | ~NVRenderer(); 19 | 20 | void Resume(); 21 | 22 | void Pause(); 23 | 24 | void Destroy(); 25 | 26 | void SetWindow(ANativeWindow* window); 27 | 28 | void FlipBackground(bool flip); 29 | 30 | GLuint GetSurfaceTextureId(){return surface_texture_id_;} 31 | 32 | void CheckGlError(const char *op); 33 | 34 | GLuint LoaderShader(GLenum shader_type, const char* source); 35 | GLuint CreateProgram(const char* vertex_source, const char* fragment_source); 36 | 37 | void _Run(); 38 | 39 | protected: 40 | bool Initialise(); 41 | 42 | bool WindowRestore(); 43 | 44 | bool WindowChanged(); 45 | 46 | void CreateSurfaceTextureId(); 47 | 48 | void ShutDown(); 49 | 50 | void DrawFrame(); 51 | 52 | void RenderBackground(); 53 | 54 | void SwapBuffers(); 55 | 56 | private: 57 | void _renderLoop(); 58 | 59 | 60 | private: 61 | enum RenderMessage{ 62 | MSG_NONE = 0, 63 | MSG_WINDOW_CREATE, 64 | MSG_WINDOW_UPDATE, 65 | MSG_WINDOW_DESTROY, 66 | MSG_LOOP_EXIT 67 | }; 68 | 69 | enum RenderMessage msg_; 70 | 71 | //Android Window 72 | ANativeWindow* window_; 73 | 74 | //EGL Resources 75 | EGLDisplay display_; 76 | EGLSurface surface_; 77 | EGLContext context_; 78 | EGLConfig config_; 79 | 80 | NVCameraBackground *cam_background_; 81 | 82 | std::mutex mut_; 83 | std::condition_variable cond_; 84 | 85 | int width_; 86 | int height_; 87 | 88 | GLuint surface_texture_id_; 89 | bool flip_background_; 90 | 91 | bool window_init_; 92 | bool pause_; 93 | 94 | }; 95 | } 96 | } 97 | 98 | 99 | #endif -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/nvision/facetracker/PermissionHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.nvision.facetracker; 18 | 19 | 20 | import android.Manifest; 21 | import android.app.Activity; 22 | import android.content.Intent; 23 | import android.content.pm.PackageManager; 24 | import android.net.Uri; 25 | import android.provider.Settings; 26 | import android.support.v4.app.ActivityCompat; 27 | import android.support.v4.content.ContextCompat; 28 | import android.widget.Toast; 29 | 30 | /** 31 | * Helper class for handling dangerous permissions for Android API level >= 23 which 32 | * requires user consent at runtime to access the camera. 33 | */ 34 | class PermissionHelper { 35 | public static final int RC_PERMISSION_REQUEST = 9222; 36 | public static boolean hasCameraPermission(Activity activity) { 37 | return ContextCompat.checkSelfPermission(activity, 38 | Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; 39 | } 40 | public static boolean hasWriteStoragePermission(Activity activity) { 41 | return ContextCompat.checkSelfPermission(activity, 42 | Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; 43 | } 44 | public static void requestCameraPermission(Activity activity, boolean requestWritePermission) { 45 | 46 | boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, 47 | Manifest.permission.CAMERA) || (requestWritePermission && 48 | ActivityCompat.shouldShowRequestPermissionRationale(activity, 49 | Manifest.permission.WRITE_EXTERNAL_STORAGE)); 50 | if (showRationale) { 51 | Toast.makeText(activity, 52 | "Camera permission is needed to run this application", Toast.LENGTH_LONG).show(); 53 | } else { 54 | 55 | // No explanation needed, we can request the permission. 56 | 57 | String permissions[] = requestWritePermission ? new String[]{Manifest.permission.CAMERA, 58 | Manifest.permission.WRITE_EXTERNAL_STORAGE}: new String[]{Manifest.permission.CAMERA}; 59 | ActivityCompat.requestPermissions(activity,permissions,RC_PERMISSION_REQUEST); 60 | } 61 | } 62 | 63 | public static void requestWriteStoragePermission(Activity activity) { 64 | boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, 65 | Manifest.permission.WRITE_EXTERNAL_STORAGE); 66 | if (showRationale) { 67 | Toast.makeText(activity, 68 | "Writing to external storage permission is needed to run this application", 69 | Toast.LENGTH_LONG).show(); 70 | } else { 71 | 72 | // No explanation needed, we can request the permission. 73 | 74 | String permissions[] = new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}; 75 | 76 | ActivityCompat.requestPermissions(activity,permissions,RC_PERMISSION_REQUEST); 77 | } 78 | } 79 | 80 | /** Launch Application Setting to grant permission. */ 81 | public static void launchPermissionSettings(Activity activity) { 82 | Intent intent = new Intent(); 83 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 84 | intent.setData(Uri.fromParts("package", activity.getPackageName(), null)); 85 | activity.startActivity(intent); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/cpp/jniapi.cpp: -------------------------------------------------------------------------------- 1 | #include // requires ndk r5 or newer 2 | #include // requires ndk r5 or newer 3 | #include 4 | #include "jniapi.h" 5 | #include "logger.h" 6 | #include "nvapp.h" 7 | #include "nvrenderer.h" 8 | #include "global_interface.h" 9 | 10 | 11 | #define LOG_TAG "JniApi" 12 | 13 | 14 | 15 | extern "C" 16 | { 17 | 18 | //Global variables 19 | UnionJNIEnvToVoid g_uenv; 20 | JavaVM *g_vm = NULL; 21 | JNIEnv *g_env = NULL; 22 | int g_attatched = 1; 23 | 24 | 25 | static nv::NVApp *kApp = 0; 26 | static ANativeWindow *kWindow = 0; 27 | 28 | jobject jni_surfacetexture = 0; 29 | jmethodID mid_update_tex; 30 | static bool request_update_tex = false; 31 | static std::mutex kMutex; 32 | 33 | 34 | 35 | void android_app_update_tex_image() 36 | { 37 | ATTATCH_JNI_THREAD 38 | if(g_vm->GetEnv(&g_uenv.venv, JNI_VERSION_1_4) != JNI_OK) 39 | { 40 | 41 | return; 42 | } 43 | 44 | g_env = g_uenv.env; 45 | 46 | std::lock_guard lk(kMutex); 47 | if(jni_surfacetexture != 0 && request_update_tex) 48 | { 49 | LOG_INFO("nv log jni update surface texture"); 50 | g_env->CallVoidMethod(jni_surfacetexture, mid_update_tex); 51 | request_update_tex = false; 52 | } 53 | 54 | DETATCH_JNI_THREAD 55 | } 56 | 57 | jint JNI_OnLoad(JavaVM* vm, void* reserved) 58 | { 59 | g_uenv.venv = 0; 60 | jint result = -1; 61 | JNIEnv *env = 0; 62 | 63 | if(vm->GetEnv(&g_uenv.venv, JNI_VERSION_1_4)!=JNI_OK){ 64 | goto fail; 65 | } 66 | 67 | LOG_INFO("nv log jni load successful"); 68 | env = g_uenv.env; 69 | result = JNI_VERSION_1_4; 70 | g_vm = vm; 71 | g_env = env; 72 | 73 | fail: 74 | 75 | return result; 76 | } 77 | 78 | void JNI_OnUnload(JavaVM *vm, void *reserved) 79 | { 80 | 81 | } 82 | 83 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeCreateApp)(JNIEnv* jenv, jobject obj) 84 | { 85 | kApp = new nv::NVApp(); 86 | kApp->Init(); 87 | } 88 | 89 | 90 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeResumeApp)(JNIEnv* jenv, jobject obj) 91 | { 92 | kApp->Resume(); 93 | } 94 | 95 | 96 | JNIEXPORT void JNICALL NATIVE_METHOD(nativePauseApp)(JNIEnv* jenv, jobject obj) 97 | { 98 | kApp->Pause(); 99 | } 100 | 101 | 102 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeDestroyApp)(JNIEnv* jenv, jobject obj) 103 | { 104 | kApp->Deinit(); 105 | } 106 | 107 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeSetSurface)(JNIEnv* jenv, jobject obj, jobject surface) 108 | { 109 | 110 | if(surface != 0){ 111 | 112 | kWindow = ANativeWindow_fromSurface(jenv, surface); 113 | LOG_INFO("nv log native set surface window %p", kWindow); 114 | kApp->Render()->SetWindow(kWindow); 115 | 116 | }else{ 117 | kApp->Render()->SetWindow(0); 118 | ANativeWindow_release(kWindow); 119 | LOG_INFO("nv log native release surface window "); 120 | kWindow = 0; 121 | } 122 | } 123 | 124 | JNIEXPORT jobject JNICALL NATIVE_METHOD(nativeSurfaceTexture)(JNIEnv* jenv, jobject obj, jboolean flip) 125 | { 126 | jclass clazz = jenv->FindClass("android/graphics/SurfaceTexture"); 127 | jmethodID mid_construct = jenv->GetMethodID(clazz, "", "(I)V"); 128 | mid_update_tex = jenv->GetMethodID(clazz, "updateTexImage", "()V"); 129 | 130 | jobject obj_texture = jenv->NewObject(clazz, mid_construct, kApp->Render()->GetSurfaceTextureId()); 131 | jni_surfacetexture = jenv->NewGlobalRef(obj_texture); 132 | 133 | //If flip the camera background 134 | kApp->Render()->FlipBackground(flip); 135 | return obj_texture; 136 | 137 | } 138 | 139 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeDestroyTexture)(JNIEnv* jenv, jobject obj){ 140 | jenv->DeleteGlobalRef(jni_surfacetexture); 141 | jni_surfacetexture = 0; 142 | } 143 | 144 | JNIEXPORT void JNICALL NATIVE_METHOD(nativeRequestUpdateTexture)(JNIEnv* jenv, jobject obj){ 145 | std::lock_guard lk(kMutex); 146 | request_update_tex = true; 147 | } 148 | 149 | 150 | } 151 | 152 | -------------------------------------------------------------------------------- /app/src/main/cpp/nv_cam_background.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 18-8-10. 3 | // 4 | 5 | #include "nv_cam_background.h" 6 | 7 | #define LOG_TAG "NVCameraBackground" 8 | 9 | #include "nvrenderer.h" 10 | #include "logger.h" 11 | 12 | static auto kVertexShader = 13 | "attribute vec4 vPosition;\n" 14 | "attribute vec2 vUv;\n" 15 | "varying vec2 oUv;\n" 16 | "void main() {\n" 17 | "oUv = vUv;\n" 18 | " gl_Position = vPosition;\n" 19 | "}\n"; 20 | 21 | static auto kFragmentShader = 22 | "#extension GL_OES_EGL_image_external:require\n" 23 | "precision mediump float;\n" 24 | "varying vec2 oUv;\n" 25 | "uniform samplerExternalOES uTexture;\n" 26 | "void main() {\n" 27 | " vec4 color = texture2D(uTexture, oUv);\n" 28 | " gl_FragColor = color;\n" 29 | "}\n"; 30 | 31 | static const GLfloat kTriangleVertices[] = { -1.0f, -1.0f, -1.0f, 1.0f, 32 | 1.0f, 1.0f, 1.0f, -1.0f }; 33 | 34 | static const GLfloat kUvs[] = {1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0}; 35 | 36 | static const GLfloat kUvs_flip[] = {0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0}; 37 | 38 | static const GLushort kIndices[] = {0, 3, 1, 1, 3, 2}; 39 | 40 | namespace nv 41 | { 42 | namespace render 43 | { 44 | NVCameraBackground::NVCameraBackground(NVRenderer *renderer): 45 | renderer_(renderer) 46 | { 47 | //bind data to gpu 48 | glGenBuffers(1, &vertex_id_); 49 | glBindBuffer(GL_ARRAY_BUFFER, vertex_id_); 50 | glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8, kTriangleVertices, GL_STATIC_DRAW); 51 | 52 | glGenBuffers(1, &uv_id_); 53 | glBindBuffer(GL_ARRAY_BUFFER, uv_id_); 54 | glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8, kUvs, GL_STATIC_DRAW); 55 | 56 | glGenBuffers(1, &uv_flip_id); 57 | glBindBuffer(GL_ARRAY_BUFFER, uv_flip_id); 58 | glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8, kUvs_flip, GL_STATIC_DRAW); 59 | 60 | glBindBuffer(GL_ARRAY_BUFFER, 0); 61 | 62 | glGenBuffers(1, &indice_id_); 63 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indice_id_); 64 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort)*6, kIndices, GL_STATIC_DRAW); 65 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 66 | 67 | //create program 68 | program_id_ = renderer->CreateProgram(kVertexShader, kFragmentShader); 69 | if (!program_id_) { 70 | LOG_ERROR("CameraBackground Could not create program."); 71 | } 72 | //Get variables from glsl 73 | position_handle_ = glGetAttribLocation(program_id_, "vPosition"); 74 | 75 | uv_handle_ = glGetAttribLocation(program_id_, "vUv"); 76 | texture_handle_ = glGetUniformLocation(program_id_, "uTexture"); 77 | 78 | } 79 | 80 | NVCameraBackground::~NVCameraBackground() { 81 | 82 | } 83 | 84 | void NVCameraBackground::Render(bool flip) { 85 | glUseProgram(program_id_); 86 | 87 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, renderer_->GetSurfaceTextureId()); 88 | glActiveTexture(GL_TEXTURE0); 89 | glUniform1i(texture_handle_, 0); 90 | 91 | glVertexAttribPointer(position_handle_, 2, GL_FLOAT, GL_FALSE, 0, kTriangleVertices); 92 | glEnableVertexAttribArray(position_handle_); 93 | if(flip) 94 | { 95 | glVertexAttribPointer(uv_handle_, 2, GL_FLOAT, GL_FALSE, 0, kUvs_flip ); 96 | }else{ 97 | glVertexAttribPointer(uv_handle_, 2, GL_FLOAT, GL_FALSE, 0, kUvs ); 98 | } 99 | 100 | glEnableVertexAttribArray(uv_handle_); 101 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indice_id_); 102 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); 103 | 104 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); 105 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/cpp/nvrenderer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "nvrenderer.h" 6 | #include "logger.h" 7 | #include "global_interface.h" 8 | #include "nv_cam_background.h" 9 | 10 | #define LOG_TAG "NVRenderer" 11 | 12 | namespace nv 13 | { 14 | namespace render 15 | { 16 | NVRenderer::NVRenderer(): 17 | msg_(MSG_NONE), 18 | display_(0), 19 | surface_(0), 20 | context_(0), 21 | config_(0), 22 | cam_background_(0), 23 | width_(0), 24 | height_(0), 25 | surface_texture_id_(0), 26 | flip_background_(false), 27 | window_init_(false), 28 | pause_(false) 29 | { 30 | 31 | 32 | } 33 | 34 | NVRenderer::~NVRenderer() { 35 | 36 | } 37 | 38 | void NVRenderer::Resume() { 39 | pause_ = false; 40 | } 41 | 42 | 43 | void NVRenderer::Pause() { 44 | pause_ = true; 45 | } 46 | 47 | void NVRenderer::Destroy() { 48 | msg_ = MSG_LOOP_EXIT; 49 | } 50 | 51 | void NVRenderer::SetWindow(ANativeWindow *window) { 52 | if(window) 53 | { 54 | window_ = window; 55 | if(window_init_) 56 | { 57 | msg_ = MSG_WINDOW_UPDATE; 58 | return; 59 | } 60 | 61 | LOG_INFO("nv log renderer SetWindow Create"); 62 | msg_ = MSG_WINDOW_CREATE; 63 | 64 | //Block the UI thread 65 | std::unique_lock lk(mut_); 66 | cond_.wait(lk); 67 | lk.unlock(); 68 | LOG_INFO("nv log renderer unblock ui thread"); 69 | }else{ 70 | msg_ = MSG_WINDOW_DESTROY; 71 | } 72 | 73 | } 74 | 75 | //Run on the gl thread 76 | void NVRenderer::_Run() { 77 | //LOG_INFO("nv log renderer run loop"); 78 | _renderLoop(); 79 | } 80 | 81 | //Create a gl context and surface 82 | bool NVRenderer::Initialise() { 83 | LOG_INFO("nv log renderer initialise"); 84 | const EGLint attribs[] = { 85 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 86 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 87 | EGL_BLUE_SIZE, 8, 88 | EGL_GREEN_SIZE, 8, 89 | EGL_RED_SIZE, 8, 90 | EGL_ALPHA_SIZE, 8, 91 | EGL_NONE 92 | }; 93 | //opengl es2.0 context 94 | EGLint contextAtt[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE }; 95 | EGLDisplay display; 96 | EGLConfig config; 97 | EGLint numConfigs; 98 | EGLint format; 99 | EGLSurface surface; 100 | EGLContext context; 101 | GLfloat ratio; 102 | 103 | if ((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) { 104 | LOG_ERROR("eglGetDisplay() returned error %d", eglGetError()); 105 | return false; 106 | } 107 | if (!eglInitialize(display, 0, 0)) { 108 | LOG_ERROR("eglInitialize() returned error %d", eglGetError()); 109 | return false; 110 | } 111 | 112 | if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs)) { 113 | LOG_ERROR("eglChooseConfig() returned error %d", eglGetError()); 114 | ShutDown(); 115 | return false; 116 | } 117 | 118 | if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { 119 | LOG_ERROR("eglGetConfigAttrib() returned error %d", eglGetError()); 120 | ShutDown(); 121 | return false; 122 | } 123 | 124 | ANativeWindow_setBuffersGeometry(window_, 0, 0, format); 125 | 126 | if (!(surface = eglCreateWindowSurface(display, config, window_, 0))) { 127 | LOG_ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); 128 | ShutDown(); 129 | return false; 130 | } 131 | 132 | if (!(context = eglCreateContext(display, config, 0, contextAtt))) { 133 | LOG_ERROR("eglCreateContext() returned error %d", eglGetError()); 134 | ShutDown(); 135 | return false; 136 | } 137 | 138 | if (!eglMakeCurrent(display, surface, surface, context)) { 139 | LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); 140 | ShutDown(); 141 | return false; 142 | } 143 | 144 | if (!eglQuerySurface(display, surface, EGL_WIDTH, &width_) || 145 | !eglQuerySurface(display, surface, EGL_HEIGHT, &height_)) { 146 | LOG_ERROR("eglQuerySurface() returned error %d", eglGetError()); 147 | ShutDown(); 148 | return false; 149 | } 150 | 151 | display_ = display; 152 | surface_ = surface; 153 | context_ = context; 154 | config_ = config; 155 | 156 | window_init_ = true; 157 | 158 | 159 | CheckGlError("glBindFramebuffer"); 160 | glClearColor(0.0, 0.0, 0.0, 1.0); 161 | CheckGlError("glClearColor"); 162 | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 163 | CheckGlError("glClear"); 164 | glViewport(0, 0, width_, height_); 165 | CheckGlError("glViewport"); 166 | 167 | //surfaceTexture Id Used for rendering camera preview 168 | CreateSurfaceTextureId(); 169 | //Create camera background 170 | cam_background_ = new NVCameraBackground(this); 171 | 172 | msg_ = MSG_NONE; 173 | 174 | std::lock_guard lk(mut_); 175 | cond_.notify_one(); 176 | return true; 177 | } 178 | 179 | void NVRenderer::CreateSurfaceTextureId() { 180 | glGenTextures(1, &surface_texture_id_); 181 | if(surface_texture_id_ >0) 182 | { 183 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, surface_texture_id_); 184 | CheckGlError("surfaceTexture glBindTexture"); 185 | //Linear filter type without mipmap 186 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 187 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 188 | 189 | //Wrap clmap to edge 190 | glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 191 | glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 192 | } 193 | } 194 | 195 | void NVRenderer::FlipBackground(bool flip) { 196 | flip_background_ = flip; 197 | } 198 | 199 | bool NVRenderer::WindowRestore() { 200 | EGLContext context; 201 | EGLSurface surface; 202 | 203 | 204 | if (!(surface = eglCreateWindowSurface(display_, config_, window_, 0))) { 205 | LOG_ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); 206 | ShutDown(); 207 | return false; 208 | } 209 | 210 | if (!eglMakeCurrent(display_, surface, surface, context)) { 211 | LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); 212 | ShutDown(); 213 | return false; 214 | } 215 | 216 | surface_ = surface; 217 | context_ = context; 218 | msg_ = MSG_NONE; 219 | 220 | } 221 | 222 | bool NVRenderer::WindowChanged() { 223 | LOG_INFO("Renderer update Window"); 224 | 225 | if (!eglMakeCurrent(display_, surface_, surface_, context_)) { 226 | LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); 227 | ShutDown(); 228 | return false; 229 | } 230 | 231 | 232 | if (!eglQuerySurface(display_, surface_, EGL_WIDTH, &width_) || 233 | !eglQuerySurface(display_, surface_, EGL_HEIGHT, &height_)) { 234 | LOG_ERROR("eglQuerySurface() returned error %d", eglGetError()); 235 | ShutDown(); 236 | return false; 237 | } 238 | 239 | msg_ = MSG_NONE; 240 | return true; 241 | } 242 | 243 | 244 | 245 | void NVRenderer::ShutDown() { 246 | 247 | if(cam_background_ != 0) 248 | { 249 | delete cam_background_; 250 | cam_background_ = 0; 251 | } 252 | 253 | LOG_INFO("Destroying context"); 254 | 255 | eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 256 | eglDestroyContext(display_, context_); 257 | eglDestroySurface(display_, surface_); 258 | eglTerminate(display_); 259 | 260 | display_ = EGL_NO_DISPLAY; 261 | surface_ = EGL_NO_SURFACE; 262 | context_ = EGL_NO_CONTEXT; 263 | config_ = 0; 264 | window_ = 0; 265 | window_init_ = false; 266 | msg_ = MSG_NONE; 267 | 268 | return; 269 | } 270 | 271 | void NVRenderer::DrawFrame() { 272 | //LOG_INFO("nv log renderer drawframe"); 273 | glClearColor(0.0, 0.0, 0.0, 1.0); 274 | CheckGlError("glClearColor"); 275 | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 276 | glViewport(0, 0, width_, height_); 277 | 278 | RenderBackground(); 279 | } 280 | 281 | void NVRenderer::RenderBackground() { 282 | android_app_update_tex_image(); 283 | if(cam_background_ != 0) 284 | cam_background_->Render(flip_background_); 285 | } 286 | 287 | void NVRenderer::SwapBuffers() { 288 | if (!eglSwapBuffers(display_, surface_)) { 289 | LOG_ERROR("eglSwapBuffers() returned error %d", eglGetError()); 290 | } 291 | } 292 | 293 | void NVRenderer::CheckGlError(const char *op) { 294 | for (GLint error = glGetError(); error; error 295 | = glGetError()) { 296 | LOG_INFO("nv log renderer after %s() glError (0x%x)\n", op, error); 297 | } 298 | } 299 | 300 | GLuint NVRenderer::LoaderShader(GLenum shader_type, const char *source) { 301 | GLuint shader = glCreateShader(shader_type); 302 | if (shader) { 303 | glShaderSource(shader, 1, &source, NULL); 304 | glCompileShader(shader); 305 | GLint compiled = 0; 306 | glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 307 | if (!compiled) { 308 | GLint infoLen = 0; 309 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 310 | if (infoLen) { 311 | char* buf = (char*) malloc(infoLen); 312 | if (buf) { 313 | glGetShaderInfoLog(shader, infoLen, NULL, buf); 314 | LOG_ERROR("Could not compile shader %d:\n%s\n", 315 | shader_type, buf); 316 | free(buf); 317 | } 318 | glDeleteShader(shader); 319 | shader = 0; 320 | } 321 | } 322 | } 323 | return shader; 324 | } 325 | 326 | GLuint NVRenderer::CreateProgram(const char *vertex_source, const char *fragment_source) { 327 | GLuint vertex_shader = LoaderShader(GL_VERTEX_SHADER, vertex_source); 328 | if (!vertex_shader) { 329 | return 0; 330 | } 331 | 332 | GLuint pixel_shader = LoaderShader(GL_FRAGMENT_SHADER, fragment_source); 333 | if (!pixel_shader) { 334 | return 0; 335 | } 336 | 337 | GLuint program = glCreateProgram(); 338 | if (program) { 339 | glAttachShader(program, vertex_shader); 340 | CheckGlError("glAttachShader"); 341 | glAttachShader(program, pixel_shader); 342 | CheckGlError("glAttachShader"); 343 | glLinkProgram(program); 344 | GLint linkStatus = GL_FALSE; 345 | glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); 346 | if (linkStatus != GL_TRUE) { 347 | GLint bufLength = 0; 348 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); 349 | if (bufLength) { 350 | char* buf = (char*) malloc(bufLength); 351 | if (buf) { 352 | glGetProgramInfoLog(program, bufLength, NULL, buf); 353 | LOG_ERROR("Could not link program:\n%s\n", buf); 354 | free(buf); 355 | } 356 | } 357 | glDeleteProgram(program); 358 | program = 0; 359 | } 360 | } 361 | return program; 362 | } 363 | 364 | 365 | void NVRenderer::_renderLoop() { 366 | bool run = true; 367 | while(run) 368 | { 369 | switch (msg_) 370 | { 371 | case MSG_WINDOW_CREATE: 372 | if(!window_init_) 373 | { 374 | if(!Initialise()) 375 | { 376 | run = false; 377 | } 378 | 379 | }else{ 380 | if(WindowRestore()) 381 | { 382 | run = false; 383 | } 384 | } 385 | break; 386 | case MSG_WINDOW_UPDATE: 387 | WindowChanged(); 388 | break; 389 | case MSG_WINDOW_DESTROY: 390 | ShutDown(); 391 | break; 392 | case MSG_LOOP_EXIT: 393 | run = false; 394 | break; 395 | default: 396 | break; 397 | } 398 | 399 | if(window_init_ && !pause_) 400 | { 401 | DrawFrame(); 402 | SwapBuffers(); 403 | } 404 | } 405 | } 406 | }//render 407 | } 408 | -------------------------------------------------------------------------------- /app/src/main/java/com/nvision/facetracker/CameraRenderView.java: -------------------------------------------------------------------------------- 1 | package com.nvision.facetracker; 2 | 3 | 4 | import android.Manifest; 5 | import android.app.Activity; 6 | import android.content.Context; 7 | import android.content.pm.ConfigurationInfo; 8 | import android.content.pm.PackageManager; 9 | import android.content.res.Configuration; 10 | import android.graphics.ImageFormat; 11 | import android.graphics.Point; 12 | import android.graphics.SurfaceTexture; 13 | import android.hardware.Camera; 14 | import android.hardware.camera2.CameraAccessException; 15 | import android.hardware.camera2.CameraCaptureSession; 16 | import android.hardware.camera2.CameraCharacteristics; 17 | import android.hardware.camera2.CameraDevice; 18 | import android.hardware.camera2.CameraManager; 19 | import android.hardware.camera2.CaptureRequest; 20 | import android.hardware.camera2.CaptureResult; 21 | import android.hardware.camera2.TotalCaptureResult; 22 | import android.hardware.camera2.params.StreamConfigurationMap; 23 | import android.media.Image; 24 | import android.media.ImageReader; 25 | import android.media.VolumeShaper; 26 | import android.os.Handler; 27 | import android.os.HandlerThread; 28 | import android.support.annotation.NonNull; 29 | import android.support.v4.app.NavUtils; 30 | import android.support.v4.content.ContextCompat; 31 | import android.util.AttributeSet; 32 | import android.util.Log; 33 | import android.util.Size; 34 | import android.view.Surface; 35 | import android.view.SurfaceHolder; 36 | import android.view.SurfaceView; 37 | 38 | import java.io.IOException; 39 | import java.lang.ref.WeakReference; 40 | import java.util.ArrayList; 41 | import java.util.Arrays; 42 | import java.util.Collections; 43 | import java.util.Comparator; 44 | import java.util.List; 45 | import java.util.concurrent.Semaphore; 46 | import java.util.concurrent.TimeUnit; 47 | 48 | 49 | public class CameraRenderView extends SurfaceView implements SurfaceHolder.Callback{ 50 | 51 | private WeakReference mWeakActivity; 52 | private CameraManager mCamManager; 53 | private CameraDevice mCamera; 54 | private String mCameraId; 55 | 56 | private CaptureRequest.Builder mPreviewBuilder; 57 | private CameraCaptureSession mCaptureSession; 58 | private CaptureRequest.Builder mCaptureRequestBuilder; 59 | 60 | private ImageReader mImageReader; 61 | 62 | boolean mIsSurfaceAvailable; 63 | private SurfaceHolder mSurfaceHolder; 64 | private Surface mSurface; 65 | private SurfaceTexture mSurfaceTexture; 66 | private volatile Handler //Create a handler from the ui thread 67 | mUIHandler = new Handler(); 68 | private Semaphore mCameraOpenCloseLock = new Semaphore(1); 69 | private HandlerThread mCamSessionThread; 70 | private Handler mCamSessionHandler; 71 | 72 | private int mSensorOrientation; 73 | private static final int MAX_PREVIEW_WIDTH = 1920; 74 | private static final int MAX_PREVIEW_HEIGHT = 1080; 75 | 76 | private Size mPreviewSize; 77 | private int mWidth, mHeight; 78 | 79 | public static int IMAGE_WIDTH = 640, IMAGE_HEIGHT= 480; 80 | public static final String CAMERA_FACE_BACK = "" + CameraCharacteristics.LENS_FACING_BACK; 81 | public static final String CAMERA_FACE_FRONT = "" + CameraCharacteristics.LENS_FACING_FRONT; 82 | 83 | //CameraDevice StateCallback 84 | private CameraDevice.StateCallback mCameraDeviceCallback = new CameraDevice.StateCallback() { 85 | @Override 86 | public void onOpened(@NonNull CameraDevice cameraDevice) { 87 | mCameraOpenCloseLock.release(); 88 | mCamera = cameraDevice; 89 | try{ 90 | createCameraPreviewSession(); 91 | }catch (CameraAccessException e) 92 | { 93 | e.printStackTrace(); 94 | } 95 | } 96 | 97 | @Override 98 | public void onDisconnected(@NonNull CameraDevice cameraDevice) { 99 | mCameraOpenCloseLock.release(); 100 | cameraDevice.close(); 101 | mCamera = null; 102 | } 103 | 104 | @Override 105 | public void onError(@NonNull CameraDevice cameraDevice, int i) { 106 | 107 | } 108 | }; 109 | 110 | // Session State Callback 111 | private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() { 112 | @Override 113 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 114 | if(null == mCamera) return; 115 | 116 | 117 | mCaptureSession = cameraCaptureSession; 118 | try{ 119 | mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 120 | //mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF); 121 | mPreviewBuilder.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_ON); 122 | mPreviewBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, 1600); 123 | //mPreviewBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0); 124 | 125 | startPreview(mCaptureSession); 126 | }catch (CameraAccessException e){ 127 | e.printStackTrace(); 128 | } 129 | 130 | } 131 | 132 | @Override 133 | public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { 134 | Log.e("CameraRenderView", "onConfigureFailed"); 135 | } 136 | }; 137 | 138 | 139 | private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { 140 | @Override 141 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { 142 | //Log.i("CameraRenderView","CameraCaptureSession Capture Completed"); 143 | } 144 | 145 | @Override 146 | public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { 147 | 148 | } 149 | 150 | private void process(CaptureRequest result) 151 | { 152 | //Nothing 153 | } 154 | }; 155 | 156 | private long last_time = System.currentTimeMillis(); 157 | //This is a callback object for ImageReader OnImageAvailble will be called when a still image is ready for process 158 | private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { 159 | @Override 160 | public void onImageAvailable(ImageReader imageReader) { 161 | long cur_time = System.currentTimeMillis(); 162 | //Log.i("CameraRenderView", "CameraRenderView OnImageAvailable Since last time " + (cur_time - last_time)); 163 | Image image = imageReader.acquireNextImage(); 164 | last_time = cur_time; 165 | image.close(); 166 | 167 | } 168 | }; 169 | 170 | static { 171 | System.loadLibrary("nvision_core"); 172 | } 173 | 174 | public CameraRenderView(Context context){ 175 | this(context, null); 176 | } 177 | 178 | public CameraRenderView(Context context, AttributeSet attrs) 179 | { 180 | super(context, attrs); 181 | } 182 | 183 | 184 | public void init(Activity activity) 185 | { 186 | mWeakActivity = new WeakReference<>(activity); 187 | mSurfaceHolder = this.getHolder(); 188 | mSurfaceHolder.addCallback(this); 189 | mSurfaceHolder.setKeepScreenOn(true); 190 | 191 | mIsSurfaceAvailable = false; 192 | 193 | //Create a App 194 | nativeCreateApp(); 195 | } 196 | 197 | public void onResume() 198 | { 199 | nativeResumeApp(); 200 | } 201 | 202 | public void onPause() 203 | { 204 | nativePauseApp(); 205 | } 206 | 207 | public void deinit() 208 | { 209 | nativeDestroyApp(); 210 | } 211 | 212 | 213 | @Override 214 | public void surfaceCreated(SurfaceHolder surfaceHolder) { 215 | mIsSurfaceAvailable = false; 216 | } 217 | 218 | @Override 219 | public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) { 220 | mWidth = width; 221 | mHeight = height; 222 | 223 | //This method may block the ui thread until the gl context and surface texture id created 224 | nativeSetSurface(surfaceHolder.getSurface()); 225 | //configure the output sizes for the surfaceTexture and select a id for camera 226 | configureCamera(width, height); 227 | //Only First time we open the camera and create imageReader 228 | if(!mIsSurfaceAvailable) 229 | { 230 | if(mCameraId != null) 231 | { 232 | startCameraSessionThread(); 233 | openCamera(); 234 | } 235 | 236 | createImageReader(); 237 | 238 | } 239 | mIsSurfaceAvailable = true; 240 | 241 | } 242 | 243 | @Override 244 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 245 | nativeSetSurface(null); 246 | 247 | closeCamera(); 248 | destroyImageReader(); 249 | stopCameraSessionThread(); 250 | destroyPreviewSurface(); 251 | 252 | mIsSurfaceAvailable = false; 253 | } 254 | 255 | private void startCameraSessionThread() 256 | { 257 | mCamSessionThread = new HandlerThread("Camera2"); 258 | mCamSessionThread.start(); 259 | mCamSessionHandler = new Handler(mCamSessionThread.getLooper()); 260 | } 261 | 262 | private void stopCameraSessionThread() 263 | { 264 | mCamSessionThread.quitSafely(); 265 | try{ 266 | mCamSessionThread.join(); 267 | mCamSessionThread = null; 268 | mCamSessionHandler = null; 269 | }catch (InterruptedException e) 270 | { 271 | e.printStackTrace(); 272 | } 273 | } 274 | 275 | private boolean isSurfaceAvailable() 276 | { 277 | return mSurfaceHolder!= null&&mIsSurfaceAvailable; 278 | } 279 | 280 | private void createCameraPreviewSession() throws CameraAccessException 281 | { 282 | try{ 283 | 284 | mSurface = getPreviewSurface(mPreviewSize); 285 | mPreviewBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); //This must called before createCaptureSession 286 | mCamera.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()), mSessionStateCallback,null); 287 | }catch (CameraAccessException e) 288 | { 289 | e.printStackTrace(); 290 | } 291 | } 292 | 293 | private void startPreview(CameraCaptureSession session) throws CameraAccessException{ 294 | //Set Surface of SurfaceView as the target of the builder 295 | mPreviewBuilder.addTarget(mSurface); 296 | mPreviewBuilder.addTarget(mImageReader.getSurface()); 297 | session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mCamSessionHandler); 298 | } 299 | 300 | private void configureCamera(int width, int height) 301 | { 302 | //Assume it is a face back camera 303 | mCameraId = CAMERA_FACE_BACK; 304 | ///Configure camera output surfaces 305 | setupCameraOutputs(mWidth, mHeight); 306 | } 307 | 308 | private void openCamera() 309 | { 310 | if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) 311 | != PackageManager.PERMISSION_GRANTED) { 312 | PermissionHelper.requestCameraPermission(getActivity(), true); 313 | return; 314 | } 315 | 316 | ///Prepare for camera 317 | mCamManager = (CameraManager)getActivity().getSystemService(Context.CAMERA_SERVICE); 318 | try { 319 | if(!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)){ 320 | throw new RuntimeException("Time out waiting to lock camera opening."); 321 | } 322 | }catch (InterruptedException e){ 323 | throw new RuntimeException("Interupted while trying to lock camera opening.", e); 324 | } 325 | 326 | try{ 327 | 328 | mCamManager.openCamera(mCameraId, mCameraDeviceCallback, mCamSessionHandler); 329 | }catch (CameraAccessException e) 330 | { 331 | e.printStackTrace(); 332 | } 333 | } 334 | 335 | 336 | private void closeCamera() 337 | { 338 | try{ 339 | mCameraOpenCloseLock.acquire(); 340 | 341 | if(null != mCaptureSession){ 342 | mCaptureSession.close(); 343 | mCaptureSession = null; 344 | } 345 | 346 | if(null != mCamera){ 347 | mCamera.close(); 348 | mCamera = null; 349 | mCameraId = null; 350 | } 351 | 352 | }catch (InterruptedException e) 353 | { 354 | throw new RuntimeException("Interrupted while trying to lock camera closing", e); 355 | }finally { 356 | mCameraOpenCloseLock.release(); 357 | } 358 | } 359 | 360 | private void createImageReader() 361 | { 362 | mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT,ImageFormat.YUV_420_888, 2); 363 | mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mCamSessionHandler); 364 | } 365 | 366 | private void destroyImageReader() 367 | { 368 | 369 | if(null != mImageReader) 370 | { 371 | mImageReader.close(); 372 | mImageReader = null; 373 | } 374 | } 375 | 376 | 377 | private void setupCameraOutputs(int width, int height) 378 | { 379 | Activity activity = getActivity(); 380 | CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 381 | try{ 382 | 383 | CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId); 384 | 385 | StreamConfigurationMap map =characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 386 | 387 | Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)), new CompareSizesByArea()); 388 | 389 | //Find out if we need to swap dimension to get the preview size relative to sensor coordinate 390 | int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 391 | mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 392 | boolean swappedDimensions = false; 393 | 394 | //Log.i("CameraRenderView", "CameraRenderView displayRotation"); 395 | 396 | switch (displayRotation) { 397 | case Surface.ROTATION_0: 398 | case Surface.ROTATION_180: 399 | if (mSensorOrientation == 90 || mSensorOrientation == 270) { 400 | swappedDimensions = true; 401 | } 402 | break; 403 | case Surface.ROTATION_90: 404 | case Surface.ROTATION_270: 405 | if (mSensorOrientation == 0 || mSensorOrientation == 180) { 406 | swappedDimensions = true; 407 | } 408 | break; 409 | default: 410 | Log.e("CameraRenderView", "Display rotation is invalid: " + displayRotation); 411 | } 412 | 413 | //Log.i("CameraRenderView", "CameraRenderView displaySize"); 414 | Point displaySize = new Point(); 415 | activity.getWindowManager().getDefaultDisplay().getSize(displaySize); 416 | int rotatedPreviewWidth = width; 417 | int rotatedPreviewHeight = height; 418 | int maxPreviewWidth = displaySize.x; 419 | int maxPreviewHeight = displaySize.y; 420 | 421 | if (swappedDimensions) { 422 | rotatedPreviewWidth = height; 423 | rotatedPreviewHeight = width; 424 | maxPreviewWidth = displaySize.y; 425 | maxPreviewHeight = displaySize.x; 426 | } 427 | 428 | if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 429 | maxPreviewWidth = MAX_PREVIEW_WIDTH; 430 | } 431 | 432 | if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 433 | maxPreviewHeight = MAX_PREVIEW_HEIGHT; 434 | } 435 | 436 | Log.i("CameraRenderView", "CameraRenderView PreviewSize"); 437 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 438 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 439 | // garbage capture data. 440 | mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 441 | rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, 442 | maxPreviewHeight, largest); 443 | 444 | // We fit the aspect ratio of TextureView to the size of preview we picked. 445 | int orientation = getResources().getConfiguration().orientation; 446 | 447 | 448 | }catch (CameraAccessException e){ 449 | e.printStackTrace(); 450 | }catch (NullPointerException e){ 451 | Log.e("CameraRenderView", "This device doesn't support Camera2 API"); 452 | } 453 | } 454 | 455 | 456 | private Surface getPreviewSurface(Size size){ 457 | if(mSurface == null) 458 | { 459 | //Get the SurfaceTexture from SurfaceView GL Context 460 | mSurfaceTexture = nativeSurfaceTexture(mCameraId == CAMERA_FACE_BACK?true:false); 461 | mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { 462 | @Override 463 | public void onFrameAvailable(SurfaceTexture surfaceTexture) { 464 | nativeRequestUpdateTexture(); 465 | } 466 | }); 467 | //This is the output surface we need to start preview 468 | mSurface = new Surface(mSurfaceTexture); 469 | } 470 | 471 | mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight()); 472 | return mSurface; 473 | } 474 | 475 | private void destroyPreviewSurface() 476 | { 477 | if(mSurface != null) 478 | { 479 | 480 | mSurfaceTexture.release(); 481 | nativeDestroyTexture(); 482 | mSurfaceTexture = null; 483 | mSurface.release(); 484 | mSurface = null; 485 | } 486 | } 487 | 488 | 489 | private Activity getActivity() 490 | { 491 | return mWeakActivity!= null?mWeakActivity.get():null; 492 | } 493 | 494 | 495 | private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 496 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 497 | 498 | // Collect the supported resolutions that are at least as big as the preview Surface 499 | List bigEnough = new ArrayList<>(); 500 | // Collect the supported resolutions that are smaller than the preview Surface 501 | List notBigEnough = new ArrayList<>(); 502 | int w = aspectRatio.getWidth(); 503 | int h = aspectRatio.getHeight(); 504 | for (Size option : choices) { 505 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 506 | option.getHeight() == option.getWidth() * h / w) { 507 | if (option.getWidth() >= textureViewWidth && 508 | option.getHeight() >= textureViewHeight) { 509 | bigEnough.add(option); 510 | } else { 511 | notBigEnough.add(option); 512 | } 513 | } 514 | } 515 | 516 | // Pick the smallest of those big enough. If there is no one big enough, pick the 517 | // largest of those not big enough. 518 | if (bigEnough.size() > 0) { 519 | return Collections.min(bigEnough, new CompareSizesByArea()); 520 | } else if (notBigEnough.size() > 0) { 521 | return Collections.max(notBigEnough, new CompareSizesByArea()); 522 | } else { 523 | Log.e("CameraRenderView", "Couldn't find any suitable preview size"); 524 | return choices[0]; 525 | } 526 | } 527 | 528 | 529 | static class CompareSizesByArea implements Comparator{ 530 | @Override 531 | public int compare(Size lhs, Size rhs) { 532 | return Long.signum((long)lhs.getWidth()*lhs.getHeight() - (long)rhs.getWidth()*rhs.getHeight()); 533 | } 534 | } 535 | 536 | 537 | static native void nativeCreateApp(); 538 | static native void nativeResumeApp(); 539 | static native void nativeSetSurface(Surface surface); 540 | static native void nativePauseApp(); 541 | static native void nativeDestroyApp(); 542 | static native SurfaceTexture nativeSurfaceTexture(boolean flip); 543 | static native void nativeRequestUpdateTexture(); 544 | static native void nativeDestroyTexture(); 545 | } 546 | --------------------------------------------------------------------------------