Make it into Tizen Studio project
[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_VALID_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 "/opt/usr/home/owner/apps_rw/org.tizen.smart-surveillance-camera/shared/data/tmp.jpg"
45 //#define LATEST_IMAGE_FILENAME "/opt/usr/home/owner/apps_rw/org.tizen.smart-surveillance-camera/shared/data/latest.jpg"
46
47 // #define ENABLE_SMARTTHINGS
48 #define APP_CALLBACK_KEY "controller"
49
50 typedef struct app_data_s {
51         double current_servo_x;
52         double current_servo_y;
53         int motion_state;
54
55         long long int last_moved_time;
56         long long int last_valid_event_time;
57         int valid_vision_result_x_sum;
58         int valid_vision_result_y_sum;
59         int valid_event_count;
60
61         unsigned int latest_image_width;
62         unsigned int latest_image_height;
63         char *latest_image_info;
64         int latest_image_type; // 0: image during camera repositioning, 1: single valid image but not completed, 2: fully validated image
65         unsigned char *latest_image_buffer;
66
67         Ecore_Thread *image_writter_thread;
68         pthread_mutex_t mutex;
69
70         char* temp_image_filename;
71         char* latest_image_filename;
72 } app_data;
73
74 static long long int __get_monotonic_ms(void)
75 {
76         long long int ret_time = 0;
77         struct timespec time_s;
78
79         if (0 == clock_gettime(CLOCK_MONOTONIC, &time_s))
80                 ret_time = time_s.tv_sec* 1000 + time_s.tv_nsec / 1000000;
81         else
82                 _E("Failed to get time");
83
84         return ret_time;
85 }
86
87 static mv_colorspace_e __convert_colorspace_from_cam_to_mv(camera_pixel_format_e format)
88 {
89         mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
90         switch (format) {
91         case CAMERA_PIXEL_FORMAT_NV12:
92                 colorspace = MEDIA_VISION_COLORSPACE_NV12;
93                 break;
94         case CAMERA_PIXEL_FORMAT_NV21:
95                 colorspace = MEDIA_VISION_COLORSPACE_NV21;
96                 break;
97         case CAMERA_PIXEL_FORMAT_YUYV:
98                 colorspace = MEDIA_VISION_COLORSPACE_YUYV;
99                 break;
100         case CAMERA_PIXEL_FORMAT_UYVY:
101                 colorspace = MEDIA_VISION_COLORSPACE_UYVY;
102                 break;
103         case CAMERA_PIXEL_FORMAT_422P:
104                 colorspace = MEDIA_VISION_COLORSPACE_422P;
105                 break;
106         case CAMERA_PIXEL_FORMAT_I420:
107                 colorspace = MEDIA_VISION_COLORSPACE_I420;
108                 break;
109         case CAMERA_PIXEL_FORMAT_YV12:
110                 colorspace = MEDIA_VISION_COLORSPACE_YV12;
111                 break;
112         case CAMERA_PIXEL_FORMAT_RGB565:
113                 colorspace = MEDIA_VISION_COLORSPACE_RGB565;
114                 break;
115         case CAMERA_PIXEL_FORMAT_RGB888:
116                 colorspace = MEDIA_VISION_COLORSPACE_RGB888;
117                 break;
118         case CAMERA_PIXEL_FORMAT_RGBA:
119                 colorspace = MEDIA_VISION_COLORSPACE_RGBA;
120                 break;
121         case CAMERA_PIXEL_FORMAT_NV12T:
122         case CAMERA_PIXEL_FORMAT_NV16:
123         case CAMERA_PIXEL_FORMAT_ARGB:
124         case CAMERA_PIXEL_FORMAT_JPEG:
125         case CAMERA_PIXEL_FORMAT_H264:
126         case CAMERA_PIXEL_FORMAT_INVALID:
127         default :
128                 colorspace = MEDIA_VISION_COLORSPACE_INVALID;
129                 _E("unsupported format : %d", format);
130                 break;
131         }
132
133         return colorspace;
134 }
135
136 static void __thread_write_image_file(void *data, Ecore_Thread *th)
137 {
138         app_data *ad = (app_data *)data;
139         unsigned int width = 0;
140         unsigned int height = 0;
141         unsigned char *buffer = 0;
142         char *image_info = NULL;
143         int ret = 0;
144
145         pthread_mutex_lock(&ad->mutex);
146         width = ad->latest_image_width;
147         height = ad->latest_image_height;
148         buffer = ad->latest_image_buffer;
149         ad->latest_image_buffer = NULL;
150         if (ad->latest_image_info) {
151                 image_info = ad->latest_image_info;
152                 ad->latest_image_info = NULL;
153         } else {
154                 image_info = strdup("00");
155         }
156         pthread_mutex_unlock(&ad->mutex);
157
158         ret = controller_image_save_image_file(ad->temp_image_filename, width, height, buffer, image_info, strlen(image_info));
159         if (ret) {
160                 _E("failed to save image file");
161         } else {
162                 ret = rename(ad->temp_image_filename, ad->latest_image_filename);
163                 if (ret != 0 )
164                         _E("Rename fail");
165         }
166         free(image_info);
167         free(buffer);
168 }
169
170 static void __thread_end_cb(void *data, Ecore_Thread *th)
171 {
172         app_data *ad = (app_data *)data;
173
174         pthread_mutex_lock(&ad->mutex);
175         ad->image_writter_thread = NULL;
176         pthread_mutex_unlock(&ad->mutex);
177 }
178
179 static void __thread_cancel_cb(void *data, Ecore_Thread *th)
180 {
181         app_data *ad = (app_data *)data;
182         unsigned char *buffer = NULL;
183
184         _E("Thread %p got cancelled.\n", th);
185         pthread_mutex_lock(&ad->mutex);
186         buffer = ad->latest_image_buffer;
187         ad->latest_image_buffer = NULL;
188         ad->image_writter_thread = NULL;
189         pthread_mutex_unlock(&ad->mutex);
190
191         free(buffer);
192 }
193
194 static void __copy_image_buffer(image_buffer_data_s *image_buffer, app_data *ad)
195 {
196         unsigned char *buffer = NULL;
197
198         pthread_mutex_lock(&ad->mutex);
199         ad->latest_image_height = image_buffer->image_height;
200         ad->latest_image_width = image_buffer->image_width;
201
202         buffer = ad->latest_image_buffer;
203         ad->latest_image_buffer = image_buffer->buffer;
204         pthread_mutex_unlock(&ad->mutex);
205
206         free(buffer);
207 }
208
209 static void __preview_image_buffer_created_cb(void *data)
210 {
211         image_buffer_data_s *image_buffer = data;
212         app_data *ad = (app_data *)image_buffer->user_data;
213         mv_source_h source = NULL;
214         mv_colorspace_e image_colorspace = MEDIA_VISION_COLORSPACE_INVALID;
215         switch_state_e switch_state = SWITCH_STATE_OFF;
216         char *info = NULL;
217
218         ret_if(!image_buffer);
219         ret_if(!ad);
220
221         image_colorspace = __convert_colorspace_from_cam_to_mv(image_buffer->format);
222         goto_if(image_colorspace == MEDIA_VISION_COLORSPACE_INVALID, FREE_ALL_BUFFER);
223
224         __copy_image_buffer(image_buffer, ad);
225
226         switch_state_get(&switch_state);
227         if (switch_state == SWITCH_STATE_OFF) { /* SWITCH_STATE_OFF means automatic mode */
228                 source = controller_mv_create_source(image_buffer->buffer,
229                                         image_buffer->buffer_size, image_buffer->image_width,
230                                         image_buffer->image_height, image_colorspace);
231         }
232
233         pthread_mutex_lock(&ad->mutex);
234         info = ad->latest_image_info;
235         ad->latest_image_info = NULL;
236         pthread_mutex_unlock(&ad->mutex);
237         free(info);
238
239         if (source)
240                 controller_mv_push_source(source);
241
242         free(image_buffer);
243
244         motion_state_set(ad->motion_state, APP_CALLBACK_KEY);
245         ad->motion_state = 0;
246
247         pthread_mutex_lock(&ad->mutex);
248         if (!ad->image_writter_thread) {
249                 ad->image_writter_thread = ecore_thread_run(__thread_write_image_file, __thread_end_cb, __thread_cancel_cb, ad);
250         } else {
251                 _E("Thread is running NOW");
252         }
253         pthread_mutex_unlock(&ad->mutex);
254
255         return;
256
257 FREE_ALL_BUFFER:
258         free(image_buffer->buffer);
259         free(image_buffer);
260 }
261
262 static void __move_camera(int x, int y, void *user_data)
263 {
264         app_data *ad = (app_data *)user_data;
265         ret_if(!ad);
266
267         // x, y Range : -10 ~ 10
268         if (x > 10) x = 10;
269         if (x < -10) x = -10;
270         if (y > 10) y = 10;
271         if (y < -10) y = -10;
272
273         x *= -1; // The camera image is flipped left and right.
274         double calculated_x = ad->current_servo_x + x * SERVO_MOTOR_HORIZONTAL_STEP;
275         double calculated_y = ad->current_servo_y + y * SERVO_MOTOR_VERTICAL_STEP;
276
277         if (calculated_x > SERVO_MOTOR_HORIZONTAL_MAX)
278                 calculated_x = SERVO_MOTOR_HORIZONTAL_MAX;
279
280         if (calculated_x < SERVO_MOTOR_HORIZONTAL_MIN)
281                 calculated_x = SERVO_MOTOR_HORIZONTAL_MIN;
282
283         if (calculated_y > SERVO_MOTOR_VERTICAL_MAX)
284                 calculated_y = SERVO_MOTOR_VERTICAL_MAX;
285
286         if (calculated_y < SERVO_MOTOR_VERTICAL_MIN)
287                 calculated_y = SERVO_MOTOR_VERTICAL_MIN;
288
289         ad->current_servo_x = calculated_x;
290         ad->current_servo_y = calculated_y;
291
292         servo_h_state_set(calculated_x, APP_CALLBACK_KEY);
293         servo_v_state_set(calculated_y, APP_CALLBACK_KEY);
294
295         return;
296 }
297
298 static void __set_result_info(int result[], int result_count, app_data *ad, int image_result_type)
299 {
300         char image_info[IMAGE_INFO_MAX + 1] = {'\0', };
301         char *current_position;
302         int current_index = 0;
303         int string_count = 0;
304         int i = 0;
305         char *latest_image_info = NULL;
306         char *info = NULL;
307
308         current_position = image_info;
309
310         current_position += snprintf(current_position, IMAGE_INFO_MAX, "%02d", image_result_type);
311         string_count += 2;
312
313         current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d", result_count);
314         string_count += 2;
315
316         for (i = 0; i < result_count; i++) {
317                 current_index = i * 4;
318                 if (IMAGE_INFO_MAX - string_count < 8)
319                         break;
320
321                 current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d%02d%02d%02d"
322                         , result[current_index], result[current_index + 1], result[current_index + 2], result[current_index + 3]);
323                 string_count += 8;
324         }
325
326         latest_image_info = strdup(image_info);
327         pthread_mutex_lock(&ad->mutex);
328         info = ad->latest_image_info;
329         ad->latest_image_info = latest_image_info;
330         pthread_mutex_unlock(&ad->mutex);
331         free(info);
332 }
333
334 static void __mv_detection_event_cb(int horizontal, int vertical, int result[], int result_count, void *user_data)
335 {
336         app_data *ad = (app_data *)user_data;
337         long long int now = __get_monotonic_ms();
338
339         ad->motion_state = 1;
340
341         if (now < ad->last_moved_time + CAMERA_MOVE_INTERVAL_MS) {
342                 ad->valid_event_count = 0;
343                 pthread_mutex_lock(&ad->mutex);
344                 ad->latest_image_type = 0; // 0: image during camera repositioning
345                 pthread_mutex_unlock(&ad->mutex);
346                 __set_result_info(result, result_count, ad, 0);
347                 return;
348         }
349
350         if (now < ad->last_valid_event_time + VALID_EVENT_INTERVAL_MS) {
351                 ad->valid_event_count++;
352         } else {
353                 ad->valid_event_count = 1;
354         }
355
356         ad->last_valid_event_time = now;
357
358         if (ad->valid_event_count < THRESHOLD_VALID_EVENT_COUNT) {
359                 ad->valid_vision_result_x_sum += horizontal;
360                 ad->valid_vision_result_y_sum += vertical;
361                 pthread_mutex_lock(&ad->mutex);
362                 ad->latest_image_type = 1; // 1: single valid image but not completed
363                 pthread_mutex_unlock(&ad->mutex);
364                 __set_result_info(result, result_count, ad, 1);
365                 return;
366         }
367
368         ad->valid_event_count = 0;
369         ad->valid_vision_result_x_sum += horizontal;
370         ad->valid_vision_result_y_sum += vertical;
371
372         int x = ad->valid_vision_result_x_sum / THRESHOLD_VALID_EVENT_COUNT;
373         int y = ad->valid_vision_result_y_sum / THRESHOLD_VALID_EVENT_COUNT;
374
375         x = 10 * x / (IMAGE_WIDTH / 2);
376         y = 10 * y / (IMAGE_HEIGHT / 2);
377
378         __move_camera((int) x, (int) y, ad);
379         ad->last_moved_time = now;
380
381         ad->valid_vision_result_x_sum = 0;
382         ad->valid_vision_result_y_sum = 0;
383         pthread_mutex_lock(&ad->mutex);
384         ad->latest_image_type = 2; // 2: fully validated image
385         pthread_mutex_unlock(&ad->mutex);
386
387         __set_result_info(result, result_count, ad, 2);
388 }
389
390 static void __switch_changed(switch_state_e state, void* user_data)
391 {
392         app_data *ad = (app_data *)user_data;
393         _D("switch changed to - %d", state);
394         ret_if(!ad);
395
396         /* Move servo motors to initial position */
397         if (state != SWITCH_STATE_ON)
398                 return;
399
400         ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
401         ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
402
403         servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
404         servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
405 }
406
407 static void __servo_v_changed(double value, void* user_data)
408 {
409         app_data *ad = (app_data *)user_data;
410         ret_if(!ad);
411
412         _D("servo_v changed to - %lf", value);
413         ad->current_servo_y = value;
414 }
415
416 static void __servo_h_changed(double value, void* user_data)
417 {
418         app_data *ad = (app_data *)user_data;
419         ret_if(!ad);
420
421         _D("servo_h changed to - %lf", value);
422         ad->current_servo_x = value;
423 }
424
425 static void __device_interfaces_fini(void)
426 {
427         switch_finalize();
428         servo_v_finalize();
429         servo_h_finalize();
430         motion_finalize();
431 }
432
433 static int __device_interfaces_init(app_data *ad)
434 {
435         retv_if(!ad, -1);
436
437         if (switch_initialize()) {
438                 _E("failed to switch_initialize()");
439                 return -1;
440         }
441
442         if (servo_v_initialize()) {
443                 _E("failed to servo_v_initialize()");
444                 goto ERROR;
445         }
446
447         if (servo_h_initialize()) {
448                 _E("failed to servo_h_initialize()");
449                 goto ERROR;
450         }
451
452         if (motion_initialize()) {
453                 _E("failed to motion_initialize()");
454                 goto ERROR;
455         }
456
457         switch_state_changed_cb_set(APP_CALLBACK_KEY, __switch_changed, ad);
458         servo_v_state_changed_cb_set(APP_CALLBACK_KEY, __servo_v_changed, ad);
459         servo_h_state_changed_cb_set(APP_CALLBACK_KEY, __servo_h_changed, ad);
460         // motion : only set result value, callback is not needed
461
462         return 0;
463
464 ERROR :
465         __device_interfaces_fini();
466         return -1;
467 }
468
469 static bool service_app_create(void *data)
470 {
471         app_data *ad = (app_data *)data;
472
473         char* shared_data_path = app_get_shared_data_path();
474         if (shared_data_path == NULL) {
475                 _E("Failed to get shared data path");
476                 goto ERROR;
477         }
478         ad->temp_image_filename = g_strconcat(shared_data_path, "tmp.jpg", NULL);
479         ad->latest_image_filename = g_strconcat(shared_data_path, "latest.jpg", NULL);
480         free(shared_data_path);
481
482         _D("%s", ad->temp_image_filename);
483         _D("%s", ad->latest_image_filename);
484
485         controller_image_initialize();
486
487         pthread_mutex_init(&ad->mutex, NULL);
488
489         if (__device_interfaces_init(ad))
490                 goto ERROR;
491
492         if (controller_mv_set_movement_detection_event_cb(__mv_detection_event_cb, data) == -1) {
493                 _E("Failed to set movement detection event callback");
494                 goto ERROR;
495         }
496
497         if (resource_camera_init(__preview_image_buffer_created_cb, ad) == -1) {
498                 _E("Failed to init camera");
499                 goto ERROR;
500         }
501
502         if (resource_camera_start_preview() == -1) {
503                 _E("Failed to start camera preview");
504                 goto ERROR;
505         }
506
507 #ifdef ENABLE_SMARTTHINGS
508         /* smartthings APIs should be called after camera start preview, they can't wait to start camera */
509         if (st_thing_master_init())
510                 goto ERROR;
511
512         if (st_thing_resource_init())
513                 goto ERROR;
514 #endif /* ENABLE_SMARTTHINGS */
515
516         ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
517         ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
518
519         servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
520         servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
521
522         return true;
523
524 ERROR:
525         __device_interfaces_fini();
526
527         resource_camera_close();
528         controller_mv_unset_movement_detection_event_cb();
529         controller_image_finalize();
530
531 #ifdef ENABLE_SMARTTHINGS
532         st_thing_master_fini();
533         st_thing_resource_fini();
534 #endif /* ENABLE_SMARTTHINGS */
535
536         pthread_mutex_destroy(&ad->mutex);
537
538         return false;
539 }
540
541 static void service_app_terminate(void *data)
542 {
543         app_data *ad = (app_data *)data;
544         Ecore_Thread *thread_id = NULL;
545         unsigned char *buffer = NULL;
546         char *info = NULL;
547         gchar *temp_image_filename;
548         gchar *latest_image_filename;
549         _D("App Terminated - enter");
550
551         resource_camera_close();
552         controller_mv_unset_movement_detection_event_cb();
553
554         pthread_mutex_lock(&ad->mutex);
555         thread_id = ad->image_writter_thread;
556         ad->image_writter_thread = NULL;
557         pthread_mutex_unlock(&ad->mutex);
558
559         if (thread_id)
560                 ecore_thread_wait(thread_id, 3.0); // wait for 3 second
561
562         __device_interfaces_fini();
563
564         controller_image_finalize();
565
566 #ifdef ENABLE_SMARTTHINGS
567         st_thing_master_fini();
568         st_thing_resource_fini();
569 #endif /* ENABLE_SMARTTHINGS */
570
571         pthread_mutex_lock(&ad->mutex);
572         buffer = ad->latest_image_buffer;
573         ad->latest_image_buffer = NULL;
574         info  = ad->latest_image_info;
575         ad->latest_image_info = NULL;
576         temp_image_filename = ad->temp_image_filename;
577         ad->temp_image_filename = NULL;
578         latest_image_filename = ad->latest_image_filename;
579         ad->latest_image_filename = NULL;
580         pthread_mutex_unlock(&ad->mutex);
581         free(buffer);
582         free(info);
583         g_free(temp_image_filename);
584         g_free(latest_image_filename);
585
586         pthread_mutex_destroy(&ad->mutex);
587         free(ad);
588         _D("App Terminated - leave");
589 }
590
591 static void service_app_control(app_control_h app_control, void *data)
592 {
593         /* APP_CONTROL */
594 }
595
596 int main(int argc, char* argv[])
597 {
598         app_data *ad = NULL;
599         int ret = 0;
600         service_app_lifecycle_callback_s event_callback;
601
602         ad = calloc(1, sizeof(app_data));
603         retv_if(!ad, -1);
604
605         event_callback.create = service_app_create;
606         event_callback.terminate = service_app_terminate;
607         event_callback.app_control = service_app_control;
608
609         ret = service_app_main(argc, argv, &event_callback, ad);
610
611         return ret;
612 }