Base for Native Activity example added.
authorAlexander Smorkalov <alexander.smorkalov@itseez.com>
Mon, 4 Mar 2013 10:49:33 +0000 (14:49 +0400)
committerAlexander Smorkalov <alexander.smorkalov@itseez.com>
Mon, 18 Mar 2013 06:37:11 +0000 (10:37 +0400)
cmake/OpenCVDetectAndroidSDK.cmake
samples/android/CMakeLists.txt
samples/android/native-activity/AndroidManifest.xml [new file with mode: 0644]
samples/android/native-activity/jni/Android.mk [new file with mode: 0644]
samples/android/native-activity/jni/Application.mk [new file with mode: 0644]
samples/android/native-activity/jni/native.cpp [new file with mode: 0644]
samples/android/native-activity/res/drawable/icon.png [new file with mode: 0644]
samples/android/native-activity/res/values/strings.xml [new file with mode: 0644]

index 0e0240ca86691c665b7f799d3e14a69d8f5c7fe0..b125561d4a2419a650776323ca1d769ea063073a 100644 (file)
@@ -264,13 +264,23 @@ macro(add_android_project target path)
     ocv_list_filterout(android_proj_jni_files "\\\\.svn")
 
     if(android_proj_jni_files AND EXISTS ${path}/jni/Android.mk AND NOT DEFINED JNI_LIB_NAME)
+      # find local module name in Android.mk file to build native lib
       file(STRINGS "${path}/jni/Android.mk" JNI_LIB_NAME REGEX "LOCAL_MODULE[ ]*:=[ ]*.*" )
       string(REGEX REPLACE "LOCAL_MODULE[ ]*:=[ ]*([a-zA-Z_][a-zA-Z_0-9]*)[ ]*" "\\1" JNI_LIB_NAME "${JNI_LIB_NAME}")
 
+      # find using of native app glue to determine native activity
+      file(STRINGS "${path}/jni/Android.mk" NATIVE_APP_GLUE REGEX ".*(call import-module,android/native_app_glue)" )
+
       if(JNI_LIB_NAME)
         ocv_include_modules_recurse(${android_proj_NATIVE_DEPS})
         ocv_include_directories("${path}/jni")
 
+        if (NATIVE_APP_GLUE)
+          include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
+          list(APPEND android_proj_jni_files ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
+          set(android_proj_NATIVE_DEPS ${android_proj_NATIVE_DEPS} android)
+        endif()
+
         add_library(${JNI_LIB_NAME} MODULE ${android_proj_jni_files})
         target_link_libraries(${JNI_LIB_NAME} ${OPENCV_LINKER_LIBS} ${android_proj_NATIVE_DEPS})
 
index a0794cb972e56ad10dae707941cd0f907a698530..9d7b0cbf0fc2f9b08895bf7b2e592dad776e9201 100644 (file)
@@ -11,6 +11,10 @@ add_subdirectory(face-detection)
 add_subdirectory(image-manipulations)
 add_subdirectory(color-blob-detection)
 
+if (ANDROID_NATIVE_API_LEVEL GREATER 8)
+  add_subdirectory(native-activity)
+endif()
+
 add_subdirectory(tutorial-1-camerapreview)
 add_subdirectory(tutorial-2-mixedprocessing)
 add_subdirectory(tutorial-3-cameracontrol)
diff --git a/samples/android/native-activity/AndroidManifest.xml b/samples/android/native-activity/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..e7b1f12
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.opencv.examples.native_activity"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="9" />
+    <application android:label="@string/app_name"
+                 android:icon="@drawable/icon"
+                 android:hasCode="false" android:debuggable="true">
+        <activity android:name="android.app.NativeActivity"
+                  android:label="@string/app_name">
+            <meta-data android:name="android.app.lib_name"
+                    android:value="native_activity" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+   <uses-permission android:name="android.permission.CAMERA"/>
+   
+   <uses-feature android:name="android.hardware.camera" android:required="false"/>
+   <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
+   <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
+   <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
+   
+</manifest>
diff --git a/samples/android/native-activity/jni/Android.mk b/samples/android/native-activity/jni/Android.mk
new file mode 100644 (file)
index 0000000..6b1564a
--- /dev/null
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := native_activity
+LOCAL_SRC_FILES := native.cpp
+LOCAL_LDLIBS    := -lm -llog -landroid
+LOCAL_STATIC_LIBRARIES := android_native_app_glue
+
+include $(BUILD_SHARED_LIBRARY)
+
+$(call import-module,android/native_app_glue)
diff --git a/samples/android/native-activity/jni/Application.mk b/samples/android/native-activity/jni/Application.mk
new file mode 100644 (file)
index 0000000..a89e12d
--- /dev/null
@@ -0,0 +1,2 @@
+APP_ABI := armeabi-v7a
+APP_PLATFORM := android-9
diff --git a/samples/android/native-activity/jni/native.cpp b/samples/android/native-activity/jni/native.cpp
new file mode 100644 (file)
index 0000000..494ad66
--- /dev/null
@@ -0,0 +1,222 @@
+#include <android_native_app_glue.h>
+
+#include <errno.h>
+#include <jni.h>
+#include <sys/time.h>
+#include <time.h>
+#include <android/log.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <float.h>
+#include <queue>
+
+#include <opencv2/core/core.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+#include <opencv2/highgui/highgui.hpp>
+
+#define  LOG_TAG    "OCV:libnative_activity"
+#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
+#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
+#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+
+struct Engine
+{
+    android_app* app;
+    cv::Ptr<cv::VideoCapture> capture;
+};
+
+cv::Size calcOptimalResolution(const char* supported, int width, int height)
+{
+    int frameWidth = 0;
+    int frameHeight = 0;
+
+    size_t prev_idx = 0;
+    size_t idx = 0;
+    float minDiff = FLT_MAX;
+
+    do
+    {
+        int tmp_width;
+        int tmp_height;
+
+        prev_idx = idx;
+        while ((supported[idx] != '\0') && (supported[idx] != ','))
+            idx++;
+
+        sscanf(&supported[prev_idx], "%dx%d", &tmp_width, &tmp_height);
+
+        int w_diff = width - tmp_width;
+        int h_diff = height - tmp_height;
+        if ((h_diff >= 0) && (w_diff >= 0))
+        {
+            if ((h_diff <= minDiff) && (tmp_height <= 720))
+            {
+                frameWidth = tmp_width;
+                frameHeight = tmp_height;
+                minDiff = h_diff;
+            }
+        }
+
+        idx++; // to skip coma symbol
+
+    } while(supported[idx-1] != '\0');
+
+    return cv::Size(frameWidth, frameHeight);
+}
+
+static void engine_draw_frame(Engine* engine, const cv::Mat& frame)
+{
+    if (engine->app->window == NULL)
+    {
+        return; // No window.
+    }
+
+    ANativeWindow_Buffer buffer;
+    if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0)
+    {
+        LOGW("Unable to lock window buffer");
+        return;
+    }
+
+    void* pixels = buffer.bits;
+
+    for (int yy = 0; yy < std::min(frame.rows, buffer.height); yy++)
+    {
+        unsigned char* line = (unsigned char*)pixels;
+        memcpy(line, frame.ptr<unsigned char>(yy),
+               std::min(frame.cols, buffer.width)*4*sizeof(unsigned char));
+        // go to next line
+        pixels = (int32_t*)pixels + buffer.stride;
+    }
+    ANativeWindow_unlockAndPost(engine->app->window);
+}
+
+static void engine_handle_cmd(android_app* app, int32_t cmd)
+{
+    Engine* engine = (Engine*)app->userData;
+    switch (cmd)
+    {
+        case APP_CMD_INIT_WINDOW:
+            if (app->window != NULL)
+            {
+                LOGI("APP_CMD_INIT_WINDOW");
+
+                engine->capture = new cv::VideoCapture(0);
+
+                union {double prop; const char* name;} u;
+                u.prop = engine->capture->get(CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING);
+
+                cv::Size resolution;
+                if (u.name)
+                    resolution = calcOptimalResolution(u.name,
+                                                       ANativeWindow_getWidth(app->window),
+                                                       ANativeWindow_getHeight(app->window));
+                else
+                {
+                    LOGE("Cannot get supported camera resolutions");
+                    resolution = cv::Size(ANativeWindow_getWidth(app->window),
+                                          ANativeWindow_getHeight(app->window));
+                }
+
+                if ((resolution.width != 0) && (resolution.height != 0))
+                {
+                    engine->capture->set(CV_CAP_PROP_FRAME_WIDTH, resolution.width);
+                    engine->capture->set(CV_CAP_PROP_FRAME_HEIGHT, resolution.height);
+                }
+
+                if (ANativeWindow_setBuffersGeometry(app->window, resolution.width,
+                    resolution.height, WINDOW_FORMAT_RGBA_8888) < 0)
+                {
+                    LOGE("Cannot set pixel format!");
+                    return;
+                }
+
+                LOGI("Camera initialized at resoution %dx%d", resolution.width, resolution.height);
+            }
+            break;
+        case APP_CMD_TERM_WINDOW:
+            LOGI("APP_CMD_TERM_WINDOW");
+
+            engine->capture->release();
+            break;
+    }
+}
+
+void android_main(android_app* app)
+{
+    Engine engine;
+
+    // Make sure glue isn't stripped.
+    app_dummy();
+
+    memset(&engine, 0, sizeof(engine));
+    app->userData = &engine;
+    app->onAppCmd = engine_handle_cmd;
+    engine.app = app;
+
+    float fps = 0;
+    cv::Mat drawingFrame;
+    bool firstFrame = true;
+    std::queue<int64> timeQueue;
+
+    // loop waiting for stuff to do.
+    while (1)
+    {
+        // Read all pending events.
+        int ident;
+        int events;
+        android_poll_source* source;
+
+        // Process system events
+        while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0)
+        {
+            // Process this event.
+            if (source != NULL)
+            {
+                source->process(app, source);
+            }
+
+            // Check if we are exiting.
+            if (app->destroyRequested != 0)
+            {
+                LOGI("Engine thread destroy requested!");
+                return;
+            }
+        }
+
+        int64 then;
+        int64 now = cv::getTickCount();
+        timeQueue.push(now);
+
+        // Capture frame from camera and draw it
+        if (!engine.capture.empty())
+        {
+            if (engine.capture->grab())
+            {
+                engine.capture->retrieve(drawingFrame, CV_CAP_ANDROID_COLOR_FRAME_RGBA);
+//                 if (firstFrame)
+//                 {
+//                     firstFrame = false;
+//                     engine.capture->set(CV_CAP_PROP_AUTOGRAB, 1);
+//                 }
+            }
+             char buffer[256];
+             sprintf(buffer, "Display performance: %dx%d @ %.3f", drawingFrame.cols, drawingFrame.rows, fps);
+             cv::putText(drawingFrame, std::string(buffer), cv::Point(8,64), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(0,255,0,255));
+             engine_draw_frame(&engine, drawingFrame);
+        }
+
+        if (timeQueue.size() >= 2)
+            then = timeQueue.front();
+        else
+            then = 0;
+
+        if (timeQueue.size() >= 25)
+            timeQueue.pop();
+
+        fps = 1.f*timeQueue.size()*(float)cv::getTickFrequency()/(float)(now-then);
+    }
+}
diff --git a/samples/android/native-activity/res/drawable/icon.png b/samples/android/native-activity/res/drawable/icon.png
new file mode 100644 (file)
index 0000000..6304549
Binary files /dev/null and b/samples/android/native-activity/res/drawable/icon.png differ
diff --git a/samples/android/native-activity/res/values/strings.xml b/samples/android/native-activity/res/values/strings.xml
new file mode 100644 (file)
index 0000000..888534e
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">OCV Native Activity</string>
+</resources>