2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <service_app.h>
22 #include <mv_common.h>
24 #include "controller.h"
25 #include "controller_mv.h"
26 #include "controller_image.h"
28 #include "resource_camera.h"
32 #include "servo-type.h"
34 #include "st_thing_master.h"
35 #include "st_thing_resource.h"
37 #define CAMERA_MOVE_INTERVAL_MS 450
38 #define THRESHOLD_EVENT_COUNT 2
39 #define VALID_EVENT_INTERVAL_MS 200
41 #define IMAGE_FILE_PREFIX "CAM_"
42 #define EVENT_INTERVAL_SECOND 0.5f
44 #define TEMP_IMAGE_FILENAME "/tmp/tmp.jpg"
45 #define LATEST_IMAGE_FILENAME "/tmp/latest.jpg"
47 // #define TEST_SERVO_MOTER_CAL 1
48 // #define TEST_DEBUG_MODE 1
49 // #define ENABLE_SMARTTHINGS
50 #define APP_CALLBACK_KEY "controller"
52 typedef struct app_data_s {
53 long long int last_moved_time;
54 long long int last_valid_event_time;
55 double current_servo_x;
56 double current_servo_y;
59 int vision_result_x_sum;
60 int vision_result_y_sum;
62 int valid_event_count;
64 unsigned int latest_image_width;
65 unsigned int latest_image_height;
66 char *latest_image_info;
67 int latest_image_type; // 0: 카메라 이동 시간의 이미지, 1: 유효 이벤트 숫자 아래의 이미지, 2: 유효한 동작 이미지
69 unsigned char *latest_image_buffer;
71 Ecore_Thread *image_writter_thread;
72 pthread_mutex_t mutex;
75 static long long int __get_monotonic_ms(void)
77 long long int ret_time = 0;
78 struct timespec time_s;
80 if (0 == clock_gettime(CLOCK_MONOTONIC, &time_s))
81 ret_time = time_s.tv_sec* 1000 + time_s.tv_nsec / 1000000;
83 _E("Failed to get time");
88 static mv_colorspace_e __convert_colorspace_from_cam_to_mv(camera_pixel_format_e format)
90 mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
92 case CAMERA_PIXEL_FORMAT_NV12:
93 colorspace = MEDIA_VISION_COLORSPACE_NV12;
95 case CAMERA_PIXEL_FORMAT_NV21:
96 colorspace = MEDIA_VISION_COLORSPACE_NV21;
98 case CAMERA_PIXEL_FORMAT_YUYV:
99 colorspace = MEDIA_VISION_COLORSPACE_YUYV;
101 case CAMERA_PIXEL_FORMAT_UYVY:
102 colorspace = MEDIA_VISION_COLORSPACE_UYVY;
104 case CAMERA_PIXEL_FORMAT_422P:
105 colorspace = MEDIA_VISION_COLORSPACE_422P;
107 case CAMERA_PIXEL_FORMAT_I420:
108 colorspace = MEDIA_VISION_COLORSPACE_I420;
110 case CAMERA_PIXEL_FORMAT_YV12:
111 colorspace = MEDIA_VISION_COLORSPACE_YV12;
113 case CAMERA_PIXEL_FORMAT_RGB565:
114 colorspace = MEDIA_VISION_COLORSPACE_RGB565;
116 case CAMERA_PIXEL_FORMAT_RGB888:
117 colorspace = MEDIA_VISION_COLORSPACE_RGB888;
119 case CAMERA_PIXEL_FORMAT_RGBA:
120 colorspace = MEDIA_VISION_COLORSPACE_RGBA;
122 case CAMERA_PIXEL_FORMAT_NV12T:
123 case CAMERA_PIXEL_FORMAT_NV16:
124 case CAMERA_PIXEL_FORMAT_ARGB:
125 case CAMERA_PIXEL_FORMAT_JPEG:
126 case CAMERA_PIXEL_FORMAT_H264:
127 case CAMERA_PIXEL_FORMAT_INVALID:
129 colorspace = MEDIA_VISION_COLORSPACE_INVALID;
130 _E("unsupported format : %d", format);
137 static void __thread_write_image_file(void *data, Ecore_Thread *th)
139 app_data *ad = (app_data *)data;
140 unsigned int width = 0;
141 unsigned int height = 0;
142 unsigned char *buffer = 0;
143 char *image_info = NULL;
146 pthread_mutex_lock(&ad->mutex);
147 width = ad->latest_image_width;
148 height = ad->latest_image_height;
149 buffer = ad->latest_image_buffer;
150 ad->latest_image_buffer = NULL;
151 if (ad->latest_image_info) {
152 image_info = ad->latest_image_info;
153 ad->latest_image_info = NULL;
155 image_info = strdup("00");
157 pthread_mutex_unlock(&ad->mutex);
159 #ifdef TEST_DEBUG_MODE
160 char filename[PATH_MAX] = {'\0', };
161 static long long int captured_time = 0;
162 long long int now = __get_monotonic_ms();
165 int latest_image_type = 0;
167 char *data_path = NULL;
168 data_path = app_get_data_path();
170 pthread_mutex_lock(&ad->mutex);
171 servo_x = (int)ad->current_servo_x;
172 servo_y = (int)ad->current_servo_y;
173 latest_image_type = ad->latest_image_type;
174 pthread_mutex_unlock(&ad->mutex);
176 snprintf(filename, PATH_MAX, "%s%s%lld_H_%d_V_%d_%lld_%d.jpg",
179 __get_monotonic_ms(),
182 __get_monotonic_ms() - captured_time,
188 ret = controller_image_save_image_file(filename, width, height, buffer, image_info, strlen(image_info));
190 _E("failed to save image file");
192 captured_time = __get_monotonic_ms();
194 ret = controller_image_save_image_file(TEMP_IMAGE_FILENAME, width, height, buffer, image_info, strlen(image_info));
196 _E("failed to save image file");
198 ret = rename(TEMP_IMAGE_FILENAME, LATEST_IMAGE_FILENAME);
207 static void __thread_end_cb(void *data, Ecore_Thread *th)
209 app_data *ad = (app_data *)data;
211 // _D("Normal termination for thread %p.\n", th);
212 pthread_mutex_lock(&ad->mutex);
213 ad->image_writter_thread = NULL;
214 pthread_mutex_unlock(&ad->mutex);
217 static void __thread_cancel_cb(void *data, Ecore_Thread *th)
219 app_data *ad = (app_data *)data;
220 unsigned char *buffer = NULL;
222 _E("Thread %p got cancelled.\n", th);
223 pthread_mutex_lock(&ad->mutex);
224 buffer = ad->latest_image_buffer;
225 ad->latest_image_buffer = NULL;
226 ad->image_writter_thread = NULL;
227 pthread_mutex_unlock(&ad->mutex);
232 static void __copy_image_buffer(image_buffer_data_s *image_buffer, app_data *ad)
234 unsigned char *buffer = NULL;
236 pthread_mutex_lock(&ad->mutex);
237 ad->latest_image_height = image_buffer->image_height;
238 ad->latest_image_width = image_buffer->image_width;
240 buffer = ad->latest_image_buffer;
241 ad->latest_image_buffer = image_buffer->buffer;
242 pthread_mutex_unlock(&ad->mutex);
247 static void __preview_image_buffer_created_cb(void *data)
249 image_buffer_data_s *image_buffer = data;
250 app_data *ad = (app_data *)image_buffer->user_data;
251 mv_source_h source = NULL;
252 mv_colorspace_e image_colorspace = MEDIA_VISION_COLORSPACE_INVALID;
253 switch_state_e switch_state = SWITCH_STATE_OFF;
256 ret_if(!image_buffer);
259 #ifdef TEST_DEBUG_MODE
260 static long long int last_time = 0;
262 long long int now = __get_monotonic_ms();
264 _D("BUFFER seq[%d] interval[%lld]", seq, now - last_time);
268 image_colorspace = __convert_colorspace_from_cam_to_mv(image_buffer->format);
269 goto_if(image_colorspace == MEDIA_VISION_COLORSPACE_INVALID, FREE_ALL_BUFFER);
271 __copy_image_buffer(image_buffer, ad);
273 switch_state_get(&switch_state);
274 if (switch_state == SWITCH_STATE_OFF) { /* SWITCH_STATE_OFF means automatic mode */
275 source = controller_mv_create_source(image_buffer->buffer,
276 image_buffer->buffer_size, image_buffer->image_width,
277 image_buffer->image_height, image_colorspace);
280 pthread_mutex_lock(&ad->mutex);
281 info = ad->latest_image_info;
282 ad->latest_image_info = NULL;
283 pthread_mutex_unlock(&ad->mutex);
287 controller_mv_push_source(source);
289 #ifdef TEST_DEBUG_MODE
290 _D("Vision need %lldms", __get_monotonic_ms() - now);
294 motion_state_set(ad->motion_state, APP_CALLBACK_KEY);
295 ad->motion_state = 0;
297 pthread_mutex_lock(&ad->mutex);
298 if (!ad->image_writter_thread) {
299 ad->image_writter_thread = ecore_thread_run(__thread_write_image_file, __thread_end_cb, __thread_cancel_cb, ad);
301 _E("Thread is running NOW");
303 pthread_mutex_unlock(&ad->mutex);
308 free(image_buffer->buffer);
312 // x, y -10 ~ 10 offset
313 static void __move_camera(int x, int y, void *user_data)
315 app_data *ad = (app_data *)user_data;
319 if (x < -10) x = -10;
321 if (y < -10) y = -10;
323 x *= -1; // 카메라는 좌우 반전!!
324 double calculated_x = ad->current_servo_x + x * SERVO_MOTOR_HORIZONTAL_STEP;
325 double calculated_y = ad->current_servo_y + y * SERVO_MOTOR_VERTICAL_STEP;
327 if (calculated_x > SERVO_MOTOR_HORIZONTAL_MAX)
328 calculated_x = SERVO_MOTOR_HORIZONTAL_MAX;
330 if (calculated_x < SERVO_MOTOR_HORIZONTAL_MIN)
331 calculated_x = SERVO_MOTOR_HORIZONTAL_MIN;
333 if (calculated_y > SERVO_MOTOR_VERTICAL_MAX)
334 calculated_y = SERVO_MOTOR_VERTICAL_MAX;
336 if (calculated_y < SERVO_MOTOR_VERTICAL_MIN)
337 calculated_y = SERVO_MOTOR_VERTICAL_MIN;
339 ad->current_servo_x = calculated_x;
340 ad->current_servo_y = calculated_y;
342 servo_h_state_set(calculated_x, APP_CALLBACK_KEY);
343 servo_v_state_set(calculated_y, APP_CALLBACK_KEY);
348 static void __set_result_info(int result[], int result_count, app_data *ad, int image_result_type)
350 char image_info[IMAGE_INFO_MAX + 1] = {'\0', };
351 char *current_position;
352 int current_index = 0;
353 int string_count = 0;
355 char *latest_image_info = NULL;
358 current_position = image_info;
360 current_position += snprintf(current_position, IMAGE_INFO_MAX, "%02d", image_result_type);
363 current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d", result_count);
366 for (i = 0; i < result_count; i++) {
367 current_index = i * 4;
368 #ifdef TEST_DEBUG_MODE
369 _D("RESULT: [%02d] [%02d] [%02d] [%02d]", result[i], result[i + 1], result[i + 2], result[i + 3]);
371 if (IMAGE_INFO_MAX - string_count < 0)
374 current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d%02d%02d%02d"
375 , result[current_index], result[current_index + 1], result[current_index + 2], result[current_index + 3]);
379 #ifdef TEST_DEBUG_MODE
380 _D("RESULT: Count [%02d] String [%s]", result_count, image_info);
382 latest_image_info = strdup(image_info);
383 pthread_mutex_lock(&ad->mutex);
384 info = ad->latest_image_info;
385 ad->latest_image_info = latest_image_info;
386 pthread_mutex_unlock(&ad->mutex);
390 static void __mv_detection_event_cb(int horizontal, int vertical, int result[], int result_count, void *user_data)
392 app_data *ad = (app_data *)user_data;
393 long long int now = __get_monotonic_ms();
395 ad->motion_state = 1;
397 if (now < ad->last_moved_time + CAMERA_MOVE_INTERVAL_MS) {
398 // _D("@@@ CAMERA MOVE TIME @@@");
399 ad->valid_event_count = 0;
400 pthread_mutex_lock(&ad->mutex);
401 ad->latest_image_type = 0;
402 pthread_mutex_unlock(&ad->mutex);
403 __set_result_info(result, result_count, ad, 0);
407 if (now < ad->last_valid_event_time + VALID_EVENT_INTERVAL_MS) {
408 ad->valid_event_count++;
410 ad->valid_event_count = 1;
413 ad->last_valid_event_time = now;
415 if (ad->valid_event_count < THRESHOLD_EVENT_COUNT) {
416 ad->vision_result_x_sum += horizontal;
417 ad->vision_result_y_sum += vertical;
418 pthread_mutex_lock(&ad->mutex);
419 ad->latest_image_type = 1;
420 pthread_mutex_unlock(&ad->mutex);
421 __set_result_info(result, result_count, ad, 1);
425 ad->valid_event_count = 0;
426 ad->vision_result_x_sum += horizontal;
427 ad->vision_result_y_sum += vertical;
429 int x = ad->vision_result_x_sum / THRESHOLD_EVENT_COUNT;
430 int y = ad->vision_result_y_sum / THRESHOLD_EVENT_COUNT;
432 x = 10 * x / (IMAGE_WIDTH / 2);
433 y = 10 * y / (IMAGE_HEIGHT / 2);
435 #ifndef TEST_SERVO_MOTER_CAL
436 __move_camera((int) x, (int) y, ad);
438 ad->last_moved_time = now;
440 ad->vision_result_x_sum = 0;
441 ad->vision_result_y_sum = 0;
442 pthread_mutex_lock(&ad->mutex);
443 ad->latest_image_type = 2;
444 pthread_mutex_unlock(&ad->mutex);
446 __set_result_info(result, result_count, ad, 2);
449 #ifdef TEST_SERVO_MOTER_CAL
450 static Eina_Bool _control_interval_event_cb(void *data)
452 static int h = SERVO_MOTOR_HORIZONTAL_MIN;
453 static int v = SERVO_MOTOR_VERTICAL_MIN;
455 v += SERVO_MOTOR_VERTICAL_STEP;
456 if (v > SERVO_MOTOR_VERTICAL_MAX)
457 v = SERVO_MOTOR_VERTICAL_MIN;
459 h += SERVO_MOTOR_HORIZONTAL_STEP;
460 if (h > SERVO_MOTOR_HORIZONTAL_MAX)
461 h = SERVO_MOTOR_HORIZONTAL_MIN;
463 // servo_h_state_set(h, APP_CALLBACK_KEY);
464 // servo_v_state_set(v, APP_CALLBACK_KEY);
466 _D("SERVO V ONLY v[%d] h[%d]", v, h);
468 return ECORE_CALLBACK_RENEW;
472 static void __switch_changed(switch_state_e state, void* user_data)
474 app_data *ad = (app_data *)user_data;
475 _D("switch changed to - %d", state);
478 /* Move servo motors to initial position */
479 if (state != SWITCH_STATE_ON)
482 ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
483 ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
485 servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
486 servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
489 static void __servo_v_changed(double value, void* user_data)
491 app_data *ad = (app_data *)user_data;
494 _D("servo_v changed to - %lf", value);
495 ad->current_servo_y = value;
498 static void __servo_h_changed(double value, void* user_data)
500 app_data *ad = (app_data *)user_data;
503 _D("servo_h changed to - %lf", value);
504 ad->current_servo_x = value;
507 static void __device_interfaces_fini(void)
515 static int __device_interfaces_init(app_data *ad)
519 if (switch_initialize()) {
520 _E("failed to switch_initialize()");
524 if (servo_v_initialize()) {
525 _E("failed to switch_initialize()");
529 if (servo_h_initialize()) {
530 _E("failed to switch_initialize()");
534 if (motion_initialize()) {
535 _E("failed to motion_initialize()");
539 switch_state_changed_cb_set(APP_CALLBACK_KEY, __switch_changed, ad);
540 servo_v_state_changed_cb_set(APP_CALLBACK_KEY, __servo_v_changed, ad);
541 servo_h_state_changed_cb_set(APP_CALLBACK_KEY, __servo_h_changed, ad);
542 // motion : only set result value, callback is not needed
547 __device_interfaces_fini();
551 static bool service_app_create(void *data)
553 app_data *ad = (app_data *)data;
555 controller_image_initialize();
557 pthread_mutex_init(&ad->mutex, NULL);
559 if (__device_interfaces_init(ad))
562 if (controller_mv_set_movement_detection_event_cb(__mv_detection_event_cb, data) == -1) {
563 _E("Failed to set movement detection event callback");
567 if (resource_camera_init(__preview_image_buffer_created_cb, ad) == -1) {
568 _E("Failed to init camera");
572 #ifdef TEST_SERVO_MOTER_CAL
573 ecore_timer_add(EVENT_INTERVAL_SECOND, _control_interval_event_cb, NULL);
576 if (resource_camera_start_preview() == -1) {
577 _E("Failed to start camera preview");
581 #ifdef ENABLE_SMARTTHINGS
582 /* smartthings APIs should be called after camera start preview, they can't wait to start camera */
583 if (st_thing_master_init())
586 if (st_thing_resource_init())
588 #endif /* ENABLE_SMARTTHINGS */
590 ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
591 ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
593 servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
594 servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
599 __device_interfaces_fini();
601 resource_camera_close();
602 controller_mv_unset_movement_detection_event_cb();
603 controller_image_finalize();
605 #ifdef ENABLE_SMARTTHINGS
606 st_thing_master_fini();
607 st_thing_resource_fini();
608 #endif /* ENABLE_SMARTTHINGS */
610 pthread_mutex_destroy(&ad->mutex);
615 static void service_app_terminate(void *data)
617 app_data *ad = (app_data *)data;
618 Ecore_Thread *thread_id = NULL;
619 unsigned char *buffer = NULL;
621 _D("App Terminated - enter");
623 resource_camera_close();
624 controller_mv_unset_movement_detection_event_cb();
626 pthread_mutex_lock(&ad->mutex);
627 thread_id = ad->image_writter_thread;
628 ad->image_writter_thread = NULL;
629 pthread_mutex_unlock(&ad->mutex);
632 ecore_thread_wait(thread_id, 3.0); // wait for 3 second
634 __device_interfaces_fini();
636 controller_image_finalize();
638 #ifdef ENABLE_SMARTTHINGS
639 st_thing_master_fini();
640 st_thing_resource_fini();
641 #endif /* ENABLE_SMARTTHINGS */
643 pthread_mutex_lock(&ad->mutex);
644 buffer = ad->latest_image_buffer;
645 ad->latest_image_buffer = NULL;
646 info = ad->latest_image_info;
647 ad->latest_image_info = NULL;
648 pthread_mutex_unlock(&ad->mutex);
652 pthread_mutex_destroy(&ad->mutex);
654 _D("App Terminated - leave");
657 static void service_app_control(app_control_h app_control, void *data)
662 int main(int argc, char* argv[])
666 service_app_lifecycle_callback_s event_callback;
668 ad = calloc(1, sizeof(app_data));
671 event_callback.create = service_app_create;
672 event_callback.terminate = service_app_terminate;
673 event_callback.app_control = service_app_control;
675 ret = service_app_main(argc, argv, &event_callback, ad);