initial upload
[apps/native/smart-surveillance-camera.git] / src / controller.c
1  /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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 #include <glib.h>
18 #include <Ecore.h>
19 #include <tizen.h>
20 #include <service_app.h>
21 #include <camera.h>
22 #include <mv_common.h>
23 #include <pthread.h>
24 #include "controller.h"
25 #include "controller_mv.h"
26 #include "controller_image.h"
27 #include "log.h"
28 #include "resource_camera.h"
29 #include "switch.h"
30 #include "servo-h.h"
31 #include "servo-v.h"
32 #include "servo-type.h"
33 #include "motion.h"
34 #include "st_thing_master.h"
35 #include "st_thing_resource.h"
36
37 #define CAMERA_MOVE_INTERVAL_MS 450
38 #define THRESHOLD_EVENT_COUNT 2
39 #define VALID_EVENT_INTERVAL_MS 200
40
41 #define IMAGE_FILE_PREFIX "CAM_"
42 #define EVENT_INTERVAL_SECOND 0.5f
43
44 #define TEMP_IMAGE_FILENAME "/tmp/tmp.jpg"
45 #define LATEST_IMAGE_FILENAME "/tmp/latest.jpg"
46
47 // #define TEST_SERVO_MOTER_CAL 1
48 // #define TEST_DEBUG_MODE 1
49 // #define ENABLE_SMARTTHINGS
50 #define APP_CALLBACK_KEY "controller"
51
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;
57         int motion_state;
58
59         int vision_result_x_sum;
60         int vision_result_y_sum;
61
62         int valid_event_count;
63
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: 유효한 동작 이미지
68
69         unsigned char *latest_image_buffer;
70
71         Ecore_Thread *image_writter_thread;
72         pthread_mutex_t mutex;
73 } app_data;
74
75 static long long int __get_monotonic_ms(void)
76 {
77         long long int ret_time = 0;
78         struct timespec time_s;
79
80         if (0 == clock_gettime(CLOCK_MONOTONIC, &time_s))
81                 ret_time = time_s.tv_sec* 1000 + time_s.tv_nsec / 1000000;
82         else
83                 _E("Failed to get time");
84
85         return ret_time;
86 }
87
88 static mv_colorspace_e __convert_colorspace_from_cam_to_mv(camera_pixel_format_e format)
89 {
90         mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
91         switch (format) {
92         case CAMERA_PIXEL_FORMAT_NV12:
93                 colorspace = MEDIA_VISION_COLORSPACE_NV12;
94                 break;
95         case CAMERA_PIXEL_FORMAT_NV21:
96                 colorspace = MEDIA_VISION_COLORSPACE_NV21;
97                 break;
98         case CAMERA_PIXEL_FORMAT_YUYV:
99                 colorspace = MEDIA_VISION_COLORSPACE_YUYV;
100                 break;
101         case CAMERA_PIXEL_FORMAT_UYVY:
102                 colorspace = MEDIA_VISION_COLORSPACE_UYVY;
103                 break;
104         case CAMERA_PIXEL_FORMAT_422P:
105                 colorspace = MEDIA_VISION_COLORSPACE_422P;
106                 break;
107         case CAMERA_PIXEL_FORMAT_I420:
108                 colorspace = MEDIA_VISION_COLORSPACE_I420;
109                 break;
110         case CAMERA_PIXEL_FORMAT_YV12:
111                 colorspace = MEDIA_VISION_COLORSPACE_YV12;
112                 break;
113         case CAMERA_PIXEL_FORMAT_RGB565:
114                 colorspace = MEDIA_VISION_COLORSPACE_RGB565;
115                 break;
116         case CAMERA_PIXEL_FORMAT_RGB888:
117                 colorspace = MEDIA_VISION_COLORSPACE_RGB888;
118                 break;
119         case CAMERA_PIXEL_FORMAT_RGBA:
120                 colorspace = MEDIA_VISION_COLORSPACE_RGBA;
121                 break;
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:
128         default :
129                 colorspace = MEDIA_VISION_COLORSPACE_INVALID;
130                 _E("unsupported format : %d", format);
131                 break;
132         }
133
134         return colorspace;
135 }
136
137 static void __thread_write_image_file(void *data, Ecore_Thread *th)
138 {
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;
144         int ret = 0;
145
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;
154         } else {
155                 image_info = strdup("00");
156         }
157         pthread_mutex_unlock(&ad->mutex);
158
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();
163         int servo_x = 0;
164         int servo_y = 0;
165         int latest_image_type = 0;
166
167         char *data_path = NULL;
168         data_path = app_get_data_path();
169
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);
175
176         snprintf(filename, PATH_MAX, "%s%s%lld_H_%d_V_%d_%lld_%d.jpg",
177                 data_path,
178                 IMAGE_FILE_PREFIX,
179                 __get_monotonic_ms(),
180                 servo_x,
181                 servo_y,
182                 __get_monotonic_ms() - captured_time,
183                 latest_image_type);
184
185         free(data_path);
186         data_path = NULL;
187
188         ret = controller_image_save_image_file(filename, width, height, buffer, image_info, strlen(image_info));
189         if (ret)
190                 _E("failed to save image file");
191
192         captured_time = __get_monotonic_ms();
193 #else
194         ret = controller_image_save_image_file(TEMP_IMAGE_FILENAME, width, height, buffer, image_info, strlen(image_info));
195         if (ret) {
196                 _E("failed to save image file");
197         } else {
198                 ret = rename(TEMP_IMAGE_FILENAME, LATEST_IMAGE_FILENAME);
199                 if (ret != 0 )
200                         _E("Rename fail");
201         }
202 #endif
203         free(image_info);
204         free(buffer);
205 }
206
207 static void __thread_end_cb(void *data, Ecore_Thread *th)
208 {
209         app_data *ad = (app_data *)data;
210
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);
215 }
216
217 static void __thread_cancel_cb(void *data, Ecore_Thread *th)
218 {
219         app_data *ad = (app_data *)data;
220         unsigned char *buffer = NULL;
221
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);
228
229         free(buffer);
230 }
231
232 static void __copy_image_buffer(image_buffer_data_s *image_buffer, app_data *ad)
233 {
234         unsigned char *buffer = NULL;
235
236         pthread_mutex_lock(&ad->mutex);
237         ad->latest_image_height = image_buffer->image_height;
238         ad->latest_image_width = image_buffer->image_width;
239
240         buffer = ad->latest_image_buffer;
241         ad->latest_image_buffer = image_buffer->buffer;
242         pthread_mutex_unlock(&ad->mutex);
243
244         free(buffer);
245 }
246
247 static void __preview_image_buffer_created_cb(void *data)
248 {
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;
254         char *info = NULL;
255
256         ret_if(!image_buffer);
257         ret_if(!ad);
258
259 #ifdef TEST_DEBUG_MODE
260         static long long int last_time = 0;
261         static int seq = 0;
262         long long int now = __get_monotonic_ms();
263         seq++;
264         _D("BUFFER seq[%d] interval[%lld]", seq, now - last_time);
265         last_time = now;
266 #endif
267
268         image_colorspace = __convert_colorspace_from_cam_to_mv(image_buffer->format);
269         goto_if(image_colorspace == MEDIA_VISION_COLORSPACE_INVALID, FREE_ALL_BUFFER);
270
271         __copy_image_buffer(image_buffer, ad);
272
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);
278         }
279
280         pthread_mutex_lock(&ad->mutex);
281         info = ad->latest_image_info;
282         ad->latest_image_info = NULL;
283         pthread_mutex_unlock(&ad->mutex);
284         free(info);
285
286         if (source)
287                 controller_mv_push_source(source);
288
289 #ifdef TEST_DEBUG_MODE
290         _D("Vision need %lldms", __get_monotonic_ms() - now);
291 #endif
292         free(image_buffer);
293
294         motion_state_set(ad->motion_state, APP_CALLBACK_KEY);
295         ad->motion_state = 0;
296
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);
300         } else {
301                 _E("Thread is running NOW");
302         }
303         pthread_mutex_unlock(&ad->mutex);
304
305         return;
306
307 FREE_ALL_BUFFER:
308         free(image_buffer->buffer);
309         free(image_buffer);
310 }
311
312 // x, y -10 ~ 10 offset
313 static void __move_camera(int x, int y, void *user_data)
314 {
315         app_data *ad = (app_data *)user_data;
316         ret_if(!ad);
317
318         if (x > 10) x = 10;
319         if (x < -10) x = -10;
320         if (y > 10) y = 10;
321         if (y < -10) y = -10;
322
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;
326
327         if (calculated_x > SERVO_MOTOR_HORIZONTAL_MAX)
328                 calculated_x = SERVO_MOTOR_HORIZONTAL_MAX;
329
330         if (calculated_x < SERVO_MOTOR_HORIZONTAL_MIN)
331                 calculated_x = SERVO_MOTOR_HORIZONTAL_MIN;
332
333         if (calculated_y > SERVO_MOTOR_VERTICAL_MAX)
334                 calculated_y = SERVO_MOTOR_VERTICAL_MAX;
335
336         if (calculated_y < SERVO_MOTOR_VERTICAL_MIN)
337                 calculated_y = SERVO_MOTOR_VERTICAL_MIN;
338
339         ad->current_servo_x = calculated_x;
340         ad->current_servo_y = calculated_y;
341
342         servo_h_state_set(calculated_x, APP_CALLBACK_KEY);
343         servo_v_state_set(calculated_y, APP_CALLBACK_KEY);
344
345         return;
346 }
347
348 static void __set_result_info(int result[], int result_count, app_data *ad, int image_result_type)
349 {
350         char image_info[IMAGE_INFO_MAX + 1] = {'\0', };
351         char *current_position;
352         int current_index = 0;
353         int string_count = 0;
354         int i = 0;
355         char *latest_image_info = NULL;
356         char *info = NULL;
357
358         current_position = image_info;
359
360         current_position += snprintf(current_position, IMAGE_INFO_MAX, "%02d", image_result_type);
361         string_count += 2;
362
363         current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d", result_count);
364         string_count += 2;
365
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]);
370 #endif
371                 if (IMAGE_INFO_MAX - string_count < 0)
372                         break;
373
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]);
376                 string_count += 8;
377         }
378
379 #ifdef TEST_DEBUG_MODE
380         _D("RESULT: Count [%02d] String [%s]", result_count, image_info);
381 #endif
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);
387         free(info);
388 }
389
390 static void __mv_detection_event_cb(int horizontal, int vertical, int result[], int result_count, void *user_data)
391 {
392         app_data *ad = (app_data *)user_data;
393         long long int now = __get_monotonic_ms();
394
395         ad->motion_state = 1;
396
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);
404                 return;
405         }
406
407         if (now < ad->last_valid_event_time + VALID_EVENT_INTERVAL_MS) {
408                 ad->valid_event_count++;
409         } else {
410                 ad->valid_event_count = 1;
411         }
412
413         ad->last_valid_event_time = now;
414
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);
422                 return;
423         }
424
425         ad->valid_event_count = 0;
426         ad->vision_result_x_sum += horizontal;
427         ad->vision_result_y_sum += vertical;
428
429         int x = ad->vision_result_x_sum / THRESHOLD_EVENT_COUNT;
430         int y = ad->vision_result_y_sum / THRESHOLD_EVENT_COUNT;
431
432         x = 10 * x / (IMAGE_WIDTH / 2);
433         y = 10 * y / (IMAGE_HEIGHT / 2);
434
435 #ifndef TEST_SERVO_MOTER_CAL
436         __move_camera((int) x, (int) y, ad);
437 #endif
438         ad->last_moved_time = now;
439
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);
445
446         __set_result_info(result, result_count, ad, 2);
447 }
448
449 #ifdef TEST_SERVO_MOTER_CAL
450 static Eina_Bool _control_interval_event_cb(void *data)
451 {
452         static int h = SERVO_MOTOR_HORIZONTAL_MIN;
453         static int v = SERVO_MOTOR_VERTICAL_MIN;
454
455         v += SERVO_MOTOR_VERTICAL_STEP;
456         if (v > SERVO_MOTOR_VERTICAL_MAX)
457                 v = SERVO_MOTOR_VERTICAL_MIN;
458
459         h += SERVO_MOTOR_HORIZONTAL_STEP;
460         if (h > SERVO_MOTOR_HORIZONTAL_MAX)
461                 h = SERVO_MOTOR_HORIZONTAL_MIN;
462
463         // servo_h_state_set(h, APP_CALLBACK_KEY);
464         // servo_v_state_set(v, APP_CALLBACK_KEY);
465
466         _D("SERVO V ONLY v[%d] h[%d]", v, h);
467
468         return ECORE_CALLBACK_RENEW;
469 }
470 #endif
471
472 static void __switch_changed(switch_state_e state, void* user_data)
473 {
474         app_data *ad = (app_data *)user_data;
475         _D("switch changed to - %d", state);
476         ret_if(!ad);
477
478         /* Move servo motors to initial position */
479         if (state != SWITCH_STATE_ON)
480                 return;
481
482         ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
483         ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
484
485         servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
486         servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
487 }
488
489 static void __servo_v_changed(double value, void* user_data)
490 {
491         app_data *ad = (app_data *)user_data;
492         ret_if(!ad);
493
494         _D("servo_v changed to - %lf", value);
495         ad->current_servo_y = value;
496 }
497
498 static void __servo_h_changed(double value, void* user_data)
499 {
500         app_data *ad = (app_data *)user_data;
501         ret_if(!ad);
502
503         _D("servo_h changed to - %lf", value);
504         ad->current_servo_x = value;
505 }
506
507 static void __device_interfaces_fini(void)
508 {
509         switch_finalize();
510         servo_v_finalize();
511         servo_h_finalize();
512         motion_finalize();
513 }
514
515 static int __device_interfaces_init(app_data *ad)
516 {
517         retv_if(!ad, -1);
518
519         if (switch_initialize()) {
520                 _E("failed to switch_initialize()");
521                 return -1;
522         }
523
524         if (servo_v_initialize()) {
525                 _E("failed to switch_initialize()");
526                 goto ERROR;
527         }
528
529         if (servo_h_initialize()) {
530                 _E("failed to switch_initialize()");
531                 goto ERROR;
532         }
533
534         if (motion_initialize()) {
535                 _E("failed to motion_initialize()");
536                 goto ERROR;
537         }
538
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
543
544         return 0;
545
546 ERROR :
547         __device_interfaces_fini();
548         return -1;
549 }
550
551 static bool service_app_create(void *data)
552 {
553         app_data *ad = (app_data *)data;
554
555         controller_image_initialize();
556
557         pthread_mutex_init(&ad->mutex, NULL);
558
559         if (__device_interfaces_init(ad))
560                 goto ERROR;
561
562         if (controller_mv_set_movement_detection_event_cb(__mv_detection_event_cb, data) == -1) {
563                 _E("Failed to set movement detection event callback");
564                 goto ERROR;
565         }
566
567         if (resource_camera_init(__preview_image_buffer_created_cb, ad) == -1) {
568                 _E("Failed to init camera");
569                 goto ERROR;
570         }
571
572 #ifdef TEST_SERVO_MOTER_CAL
573         ecore_timer_add(EVENT_INTERVAL_SECOND, _control_interval_event_cb, NULL);
574 #endif
575
576         if (resource_camera_start_preview() == -1) {
577                 _E("Failed to start camera preview");
578                 goto ERROR;
579         }
580
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())
584                 goto ERROR;
585
586         if (st_thing_resource_init())
587                 goto ERROR;
588 #endif /* ENABLE_SMARTTHINGS */
589
590         ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
591         ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
592
593         servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
594         servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
595
596         return true;
597
598 ERROR:
599         __device_interfaces_fini();
600
601         resource_camera_close();
602         controller_mv_unset_movement_detection_event_cb();
603         controller_image_finalize();
604
605 #ifdef ENABLE_SMARTTHINGS
606         st_thing_master_fini();
607         st_thing_resource_fini();
608 #endif /* ENABLE_SMARTTHINGS */
609
610         pthread_mutex_destroy(&ad->mutex);
611
612         return false;
613 }
614
615 static void service_app_terminate(void *data)
616 {
617         app_data *ad = (app_data *)data;
618         Ecore_Thread *thread_id = NULL;
619         unsigned char *buffer = NULL;
620         char *info = NULL;
621         _D("App Terminated - enter");
622
623         resource_camera_close();
624         controller_mv_unset_movement_detection_event_cb();
625
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);
630
631         if (thread_id)
632                 ecore_thread_wait(thread_id, 3.0); // wait for 3 second
633
634         __device_interfaces_fini();
635
636         controller_image_finalize();
637
638 #ifdef ENABLE_SMARTTHINGS
639         st_thing_master_fini();
640         st_thing_resource_fini();
641 #endif /* ENABLE_SMARTTHINGS */
642
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);
649         free(buffer);
650         free(info);
651
652         pthread_mutex_destroy(&ad->mutex);
653         free(ad);
654         _D("App Terminated - leave");
655 }
656
657 static void service_app_control(app_control_h app_control, void *data)
658 {
659         /* APP_CONTROL */
660 }
661
662 int main(int argc, char* argv[])
663 {
664         app_data *ad = NULL;
665         int ret = 0;
666         service_app_lifecycle_callback_s event_callback;
667
668         ad = calloc(1, sizeof(app_data));
669         retv_if(!ad, -1);
670
671         event_callback.create = service_app_create;
672         event_callback.terminate = service_app_terminate;
673         event_callback.app_control = service_app_control;
674
675         ret = service_app_main(argc, argv, &event_callback, ad);
676
677         return ret;
678 }