BitmapToolkit Scol plugin
CameraInputAndroid-napi.cpp
Go to the documentation of this file.
1/*
2-----------------------------------------------------------------------------
3This source file is part of OpenSpace3D
4For the latest info, see http://www.openspace3d.com
5
6Copyright (c) 2012 I-maginer
7
8This program is free software; you can redistribute it and/or modify it under
9the terms of the GNU Lesser General Public License as published by the Free Software
10Foundation; either version 2 of the License, or (at your option) any later
11version.
12
13This program is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
17You should have received a copy of the GNU Lesser General Public License along with
18this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19Place - Suite 330, Boston, MA 02111-1307, USA, or go to
20http://www.gnu.org/copyleft/lesser.txt
21
22-----------------------------------------------------------------------------
23*/
24
25#include "ArManager.h"
26#include "ICameraInput.h"
27#include "CameraInputAndroid.h"
28
29#include <stdexcept>
30#include <opencv2/opencv.hpp>
31
35
36// Constants
39const std::string CameraBackendAndroid::android_package = "org/imaginer/scol/CameraBackend";
40
41// Construction
43{
44 mUpdated = false;
45 mIsOpen = false;
46 mIndex = 0;
47 initJava();
48}
49
51{
52 mApp = (struct android_app*)SCgetExtra("this_inst");
53 JNIEnv* env = 0;
54 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
55 mApp->activity->vm->AttachCurrentThread(&env, NULL);
56}
57
58CameraBackendAndroid::jmethodbox CameraBackendAndroid::grabMethod(JNIEnv* env, const char* name, const char* signature)
59{
61 result.clazz = 0;
62 result.instance = 0;
63 result.method = 0;
64
65 // Get class
66 jobject oActivity = mApp->activity->clazz;
67 jclass cActivity = env->GetObjectClass(oActivity);
68 if (cActivity)
69 {
70 jfieldID camera_backend_fieldID = env->GetFieldID(cActivity, "mCameraBackend", "Lorg/imaginer/scol/CameraBackend;");
71 if (camera_backend_fieldID)
72 result.instance = env->GetObjectField(oActivity, camera_backend_fieldID);
73 if (result.instance)
74 result.clazz = env->GetObjectClass(result.instance);
75 if (result.clazz)
76 result.method = env->GetMethodID(result.clazz, name, signature);
77
78 env->DeleteLocalRef(cActivity);
79 }
80 return result;
81}
82
84{
85 mApp->activity->vm->DetachCurrentThread();
86 mFrameBuffer.release();
87 mFrameRGB.release();
88}
89
90// Java methods
91void CameraBackendAndroid::startPreview(int width, int height, int index)
92{
93 mIndex = index;
94
95 JNIEnv* env = 0;
96 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
97 mApp->activity->vm->AttachCurrentThread(&env, NULL);
98
99 //wait for default authorization
100 jobject oActivity = mApp->activity->clazz;
101 jclass cActivity = env->GetObjectClass(oActivity);
102
103 if (cActivity)
104 {
105 bool authPassed = false;
106 while (!authPassed)
107 {
108 jfieldID reqBoolean = env->GetFieldID(cActivity, "mMinAuthPassed", "Z");
109 authPassed = (bool) env->GetBooleanField(oActivity, reqBoolean);
110 if (!authPassed)
111 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
112 }
113
114 //check camera permission
115 jmethodID camera_permission = env->GetMethodID(cActivity, "RequestCameraPermission", "()V");
116 if (camera_permission)
117 env->CallVoidMethod(oActivity, camera_permission);
118
119 if (env->ExceptionCheck())
120 {
121 env->ExceptionClear();
122 MMechostr(MSKRUNTIME, "Android camera exception : Check android.permission.CAMERA\n");
123 }
124
125 authPassed = false;
126 while (!authPassed)
127 {
128 jfieldID reqBoolean = env->GetFieldID(cActivity, "mCameraAuthPassed", "Z");
129 authPassed = (bool) env->GetBooleanField(oActivity, reqBoolean);
130 if (!authPassed)
131 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
132 }
133 env->DeleteLocalRef(cActivity);
134 }
135
136 LOGI(">>>>>>> JgrabMethod look for 'openCamera'");
137 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "openCamera", "(III)V");
138 if (!method_box.instance)
139 {
140 LOGI(">>>>>>> JgrabMethod failed for 'openCamera'");
141 return;
142 }
143
144 LOGI(">>>>>>> JgrabMethod call method");
145 env->CallVoidMethod(method_box.instance, method_box.method, (jint)width, (jint)height, (jint)index);
146 env->DeleteLocalRef(method_box.instance);
147 env->DeleteLocalRef(method_box.clazz);
148
149 if (env->ExceptionCheck())
150 {
151 env->ExceptionDescribe();
152 env->ExceptionClear();
153 LOGI(">>>>>>> 'openCamera' error");
154 return;
155 }
156
157 if (isCameraOpened())
158 {
159 mSize = getPreviewSize();
160 mFrameBuffer = cv::Mat(mSize.height + mSize.height / 2, mSize.width, CV_8UC1);
161 mFrameRGB = cv::Mat(mSize.height, mSize.width, CV_8UC3);
162
163 mIsOpen = true;
164 }
165 else
166 {
167 mSize = cv::Size(0, 0);
168 }
169}
170
172{
173 if (mIsOpen)
174 {
175 mIsOpen = false;
176
177 JNIEnv* env = 0;
178 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
179 mApp->activity->vm->AttachCurrentThread(&env, NULL);
180
181 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "closeCamera", "()V");
182 if (!method_box.instance)
183 {
184 LOGI(">>>>>>> JgrabMethod failed for 'closeCamera'");
185 return;
186 }
187
188 env->CallVoidMethod(method_box.instance, method_box.method);
189 env->DeleteLocalRef(method_box.instance);
190 env->DeleteLocalRef(method_box.clazz);
191
192 if (env->ExceptionCheck())
193 {
194 env->ExceptionClear();
195 LOGI(">>>>>>> JgrabMethod failed for 'closeCamera'");
196 }
197 }
198}
199
201{
202 JNIEnv* env = 0;
203 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
204 mApp->activity->vm->AttachCurrentThread(&env, NULL);
205
206 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "torchOn", "()V");
207 if (!method_box.instance)
208 {
209 LOGI(">>>>>>> JgrabMethod failed for 'torchOn'");
210 return;
211 }
212
213 env->CallVoidMethod(method_box.instance, method_box.method);
214 env->DeleteLocalRef(method_box.instance);
215 env->DeleteLocalRef(method_box.clazz);
216
217 if (env->ExceptionCheck())
218 {
219 env->ExceptionClear();
220 LOGI(">>>>>>> JgrabMethod failed for 'torchOn'");
221 }
222}
223
225{
226 JNIEnv* env = 0;
227 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
228 mApp->activity->vm->AttachCurrentThread(&env, NULL);
229
230 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "torchOff", "()V");
231 if (!method_box.instance)
232 {
233 LOGI(">>>>>>> JgrabMethod failed for 'torchOn'");
234 return;
235 }
236
237 env->CallVoidMethod(method_box.instance, method_box.method);
238 env->DeleteLocalRef(method_box.instance);
239 env->DeleteLocalRef(method_box.clazz);
240
241 if (env->ExceptionCheck())
242 {
243 env->ExceptionClear();
244 LOGI(">>>>>>> JgrabMethod failed for 'torchOn'");
245 }
246}
247
249{
250 int ret = 0;
251 JNIEnv* env = 0;
252 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
253 mApp->activity->vm->AttachCurrentThread(&env, NULL);
254
255 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "getDeviceOrientation", "()I");
256 if (!method_box.instance)
257 {
258 LOGI(">>>>>>> JgrabMethod failed for 'getDeviceOrientation'");
259 return false;
260 }
261
262 ret = (int)env->CallIntMethod(method_box.instance, method_box.method);
263 env->DeleteLocalRef(method_box.instance);
264 env->DeleteLocalRef(method_box.clazz);
265
266 if (env->ExceptionCheck())
267 env->ExceptionClear();
268
269 //LOGI(">>>>>>> getDeviceOrientation: %d", ret);
270 return ret;
271}
272
273
275{
276 int degree = getDeviceOrientation();
277 if (degree == 90 || degree == 270)
278 return true;
279
280 return false;
281}
282
284{
285 bool ret = false;
286 JNIEnv* env = 0;
287 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
288 mApp->activity->vm->AttachCurrentThread(&env, NULL);
289
290 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "isCameraOpened", "()Z");
291 if (!method_box.instance)
292 {
293 LOGI(">>>>>>> JgrabMethod failed for 'isCameraOpened'");
294 return false;
295 }
296
297 ret = (bool)env->CallBooleanMethod(method_box.instance, method_box.method);
298 env->DeleteLocalRef(method_box.instance);
299 env->DeleteLocalRef(method_box.clazz);
300
301 if (env->ExceptionCheck())
302 env->ExceptionClear();
303
304 return ret;
305}
306
308{
309 return mIsOpen;
310}
311
313{
314 cv::Size nSize(0, 0);
315 JNIEnv* env = 0;
316 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
317 mApp->activity->vm->AttachCurrentThread(&env, NULL);
318
319 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "getPreviewSize", "()Landroid/graphics/Point;");
320 if (!method_box.instance)
321 {
322 LOGI(">>>>>>> JgrabMethod failed for 'getPreviewSize'");
323 return nSize;
324 }
325
326 jobject previewSize_object = env->CallObjectMethod(method_box.instance, method_box.method);
327
328 if (env->ExceptionCheck())
329 {
330 env->ExceptionClear();
331 return nSize;
332 }
333
334 if (previewSize_object == 0)
335 return nSize;
336
337 jclass cameraSize_class = env->GetObjectClass(previewSize_object);
338 jfieldID size_width_field = env->GetFieldID(cameraSize_class, "x", "I");
339 jfieldID size_height_field = env->GetFieldID(cameraSize_class, "y", "I");
340
341 int width = env->GetIntField(previewSize_object, size_width_field);
342 int height = env->GetIntField(previewSize_object, size_height_field);
343 nSize = cv::Size(width, height);
344
345 env->DeleteLocalRef(cameraSize_class);
346 env->DeleteLocalRef(previewSize_object);
347 env->DeleteLocalRef(method_box.instance);
348 env->DeleteLocalRef(method_box.clazz);
349 return nSize;
350}
351
353{
354 mUpdated = false;
355
356 JNIEnv* env = 0;
357 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
358 mApp->activity->vm->AttachCurrentThread(&env, NULL);
359
360 // get camera facing
361 CameraBackendAndroid::jmethodbox facing_method_box = grabMethod(env, "isCameraFacing", "()Z");
362 if (!facing_method_box.instance)
363 {
364 LOGI(">>>>>>> JgrabMethod failed for 'isCameraFacing'");
365 return cv::Mat();
366 }
367
368 bool camIsFacing = (bool)env->CallBooleanMethod(facing_method_box.instance, facing_method_box.method);
369
370 if (env->ExceptionCheck())
371 env->ExceptionClear();
372
373 env->DeleteLocalRef(facing_method_box.instance);
374 env->DeleteLocalRef(facing_method_box.clazz);
375
376 // get image buffer
377 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "grabFrame", "()[B");
378 if (!method_box.instance)
379 {
380 LOGI(">>>>>>> JgrabMethod failed for 'grabFrame'");
381 return cv::Mat();
382 }
383
384 jbyteArray frame_buffer_object = (jbyteArray)env->CallObjectMethod(method_box.instance, method_box.method);
385 if (!frame_buffer_object || env->ExceptionCheck())
386 {
387 // read exception
388 env->ExceptionClear();
389 env->DeleteLocalRef(method_box.instance);
390 env->DeleteLocalRef(method_box.clazz);
391 return cv::Mat();
392 }
393
394 int length = env->GetArrayLength((jarray)frame_buffer_object);
395 env->GetByteArrayRegion(frame_buffer_object, 0, length, (jbyte*)mFrameBuffer.data);
396 env->DeleteLocalRef(frame_buffer_object);
397 env->DeleteLocalRef(method_box.instance);
398 env->DeleteLocalRef(method_box.clazz);
399
400 if (mFrameBuffer.data == NULL || env->ExceptionCheck())
401 {
402 // read exception
403 env->ExceptionClear();
404 MMechostr(MSKRUNTIME, "Could not get camera frame");
405 return cv::Mat();
406 }
407
408 if (mFrameBuffer.data)
409 {
410 cv::Mat rgbmat;
411 cv::cvtColor(mFrameBuffer, rgbmat, cv::COLOR_YUV2BGR_NV21, 3);
412
413 if (!rgbmat.empty())
414 {
415 //rotate picture
416
417 int rotation = getDeviceOrientation();
418 //image mirror for front view is managed by user
419 if (rotation == 180)
420 {
421 cv::flip(rgbmat, mFrameRGB, camIsFacing ? 1 : -1);
422 }
423 else if (rotation == 90)
424 {
425 cv::transpose(rgbmat, mFrameRGB);
426 cv::flip(mFrameRGB, mFrameRGB, camIsFacing ? 0 : 1);
427 }
428 else if (rotation == 270)
429 {
430 cv::transpose(rgbmat, mFrameRGB);
431 if (mIndex != 1)
432 cv::flip(mFrameRGB, mFrameRGB, 0);
433 }
434 /*else if(camIsFacing)
435 {
436 cv::flip(rgbmat, mFrameRGB, -1);
437 }*/
438 else
439 {
440 rgbmat.copyTo(mFrameRGB);
441 }
442
443 /*cv::Point center(rgbmat.cols / 2, rgbmat.rows / 2);
444 cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, rotation + 90, 1.0);
445 cv::warpAffine(rgbmat, mFrameRGB, rotationMatrix, (rotation == 0 || rotation == 180) ? cv::Size(rgbmat.cols, rgbmat.rows) : cv::Size(rgbmat.rows, rgbmat.cols));
446 */
447
448 mUpdated = true;
449 }
450
451 if (!mUpdated || mFrameRGB.empty())
452 return cv::Mat();
453 }
454 return mFrameRGB;
455}
456
458{
459 int w = (int)ANativeWindow_getWidth(mApp->window);
460 int h = (int)ANativeWindow_getHeight(mApp->window);
461
462 JNIEnv* env = 0;
463 if (mApp->activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
464 mApp->activity->vm->AttachCurrentThread(&env, NULL);
465
466 CameraBackendAndroid::jmethodbox method_box = grabMethod(env, "doTouchFocus", "(IIII)V");
467 if (!method_box.instance)
468 {
469 LOGI(">>>>>>> JgrabMethod failed for 'setFocusPoint'");
470 return false;
471 }
472
473 env->CallVoidMethod(method_box.instance, method_box.method, (jint)x, (jint)y, (jint)w, (jint)h);
474
475 if (env->ExceptionCheck())
476 env->ExceptionClear();
477
478 // Release references
479 env->DeleteLocalRef(method_box.instance);
480 env->DeleteLocalRef(method_box.clazz);
481 return true;
482}
483
485{
486 return mSize;
487}
488
492
494 : ICameraInput(index)
495{
496 mVI = new CameraBackendAndroid();
497 mBufferSize = cv::Size(640, 480);
498 mInitializing = false;
499 MMechostr(MSKDEBUG, "CameraInputAndroid : Created CameraInputAndroid with size %d:%d", mBufferSize.width, mBufferSize.height);
500}
501
503{
504 if (!mInitializing)
505 {
506 mInitializing = true;
507 MMechostr(MSKDEBUG, "CameraInputAndroid::Initialize : Initializing camera backend with size %d:%d", mBufferSize.width, mBufferSize.height);
508 mVI->startPreview(mBufferSize.width, mBufferSize.height, mIndex);
509 cv::Size nSize = mVI->getSize();
510
511 mInitializing = false;
512 if (nSize.width == 0 || nSize.height == 0)
513 return false;
514
515 mBufferSize = nSize;
516 }
517 return true;
518}
519
521{
522 if (!mInitializing && mVI->isOpen())
523 mVI->stopPreview();
524}
525
527{
528 Close();
529 mFrameCached.release();
530 SAFE_DELETE(mVI);
531}
532
534{
535 return mVI->isOpen();
536}
537
539{
540 if (!IsOpened())
541 throw std::logic_error("CameraInputAndroid::UpdateImage : Device not initialised");
542
543 mVI->grabFrame().copyTo(mFrameCached);
544
545 if (mFrameCached.empty())
546 throw std::logic_error("CameraInputAndroid::UpdateImage : Frame is empty");
547
548 return mFrameCached;
549}
550
552{
553 return mVI->isPortrait() ? mBufferSize.height : mBufferSize.width;
554}
555
557{
558 return mVI->isPortrait() ? mBufferSize.width : mBufferSize.height;
559}
560
561void CameraInputAndroid::SetSize(int width, int height)
562{
563 if (IsOpened())
564 {
565 //MMechostr(MSKDEBUG, "CameraInputAndroid::SetSize : IsOpened : %d x %d\n", width, height);
566 if (width < height)
567 {
568 int pw = height;
569 height = width;
570 width = pw;
571 }
572
573 mVI->stopPreview();
574 mVI->startPreview(width / 2, height / 2, mIndex);
575 cv::Size nSize = mVI->getSize();
576 mBufferSize = nSize;
577 }
578 else
579 {
580 mBufferSize = cv::Size(width, height);
581 }
582
583 MMechostr(MSKDEBUG, "CameraInputAndroid::SetSize : Camera set size : %ix%i orientation : %i\n", mBufferSize.width, mBufferSize.height, mVI->getDeviceOrientation());
584}
585
587{
588 std::cout << "CameraInputAndroid::TakeSnapshot : taking a snapshot of camera" << mIndex << "... ";
589 if (!IsOpened())
590 {
591 std::cout << "CameraInputAndroid::TakeSnapshot : failed. camera not opened yet." << std::endl;
592 return false;
593 }
594
595 cv::Mat frame = UpdateImage();
596 if (frame.empty())
597 {
598 std::cout << "CameraInputAndroid::TakeSnapshot : failed. Frame is empty." << std::endl;
599 return false;
600 }
601
602 if (!cv::imwrite(path, frame))
603 {
604 std::cout << "CameraInputAndroid::TakeSnapshot : failed. Couldn't write to " << path << "." << std::endl;
605 return false;
606 }
607
608 std::cout << "CameraInputAndroid::TakeSnapshot : success. Frame written to " << path << "." << std::endl;
609 return true;
610}
611
613{
614 cv::Mat frame;
615 try
616 {
617 MMechostr(MSKDEBUG, "CameraInputAndroid::RenderToScreen : updating frame");
618 frame = UpdateImage();
619 }
620 catch (std::exception e)
621 {
622 MMechostr(MSKDEBUG, "CameraInputAndroid::RenderToScreen : %s", e.what());
623 return;
624 }
625
626 if (frame.empty())
627 {
628 MMechostr(MSKDEBUG, "CameraInputAndroid::RenderToScreen : frame empty!");
629 return;
630 }
631
632 ANativeWindow* window = (ANativeWindow*)SCgetExtra("hscol");
633 if (!window)
634 {
635 MMechostr(MSKDEBUG, "CameraInputAndroid::RenderToScreen : error: window empty !");
636 return;
637 }
638 ANativeWindow_Buffer buffer;
639
640 // Try lock buffer
641 if (ANativeWindow_lock(window, &buffer, 0) < 0)
642 return;
643
644 //MMechostr(MSKDEBUG, "buffer info :: width = %d height = %d stride = %d format = %d", buffer.width, buffer.height, buffer.stride, buffer.format);
645
646 cv::resize(frame, frame, cv::Size(buffer.width, buffer.height), 0, 0, cv::INTER_CUBIC);
647
648 for (unsigned int y = 0; y < frame.rows; y++)
649 {
650 for (unsigned int x = 0; x < frame.cols; x++)
651 {
652 unsigned long srcByte = (x * 3) + (frame.cols * 3 * y);
653 unsigned long destByte = (x * 4) + (buffer.width * 4 * y);
654
655 ((unsigned char*)buffer.bits)[destByte + 2] = frame.data[srcByte];
656 ((unsigned char*)buffer.bits)[destByte + 1] = frame.data[srcByte + 1];
657 ((unsigned char*)buffer.bits)[destByte + 0] = frame.data[srcByte + 2];
658 ((unsigned char*)buffer.bits)[destByte + 3] = 255;
659 }
660 }
661
662 // Unlock buffer
663 ANativeWindow_unlockAndPost(window);
664}
665
667{
668 return mVI->setFocusPoint(x, y);
669}
670
672{
673 if (state)
674 mVI->torchOn();
675 else
676 mVI->torchOff();
677}
void startPreview(int width, int height, int index, bool recordingHint)
static const int CAMERA_FACING_FRONT
static const int CAMERA_FACING_BACK
jmethodbox grabMethod(JNIEnv *env, const char *name, const char *signature)
static const std::string android_package
virtual bool TakeSnapshot(std::string path)
virtual void SetSize(int width, int height)
virtual void SetTorchState(bool state)
virtual bool SetFocusPoint(int x, int y)
Interface for camera management. Concrete classes are written for Windows, Android and OpenCV native ...