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_VALID_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 "/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"
47 // #define ENABLE_SMARTTHINGS
48 #define APP_CALLBACK_KEY "controller"
50 typedef struct app_data_s {
51 double current_servo_x;
52 double current_servo_y;
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;
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;
67 Ecore_Thread *image_writter_thread;
68 pthread_mutex_t mutex;
70 char* temp_image_filename;
71 char* latest_image_filename;
74 static long long int __get_monotonic_ms(void)
76 long long int ret_time = 0;
77 struct timespec time_s;
79 if (0 == clock_gettime(CLOCK_MONOTONIC, &time_s))
80 ret_time = time_s.tv_sec* 1000 + time_s.tv_nsec / 1000000;
82 _E("Failed to get time");
87 static mv_colorspace_e __convert_colorspace_from_cam_to_mv(camera_pixel_format_e format)
89 mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
91 case CAMERA_PIXEL_FORMAT_NV12:
92 colorspace = MEDIA_VISION_COLORSPACE_NV12;
94 case CAMERA_PIXEL_FORMAT_NV21:
95 colorspace = MEDIA_VISION_COLORSPACE_NV21;
97 case CAMERA_PIXEL_FORMAT_YUYV:
98 colorspace = MEDIA_VISION_COLORSPACE_YUYV;
100 case CAMERA_PIXEL_FORMAT_UYVY:
101 colorspace = MEDIA_VISION_COLORSPACE_UYVY;
103 case CAMERA_PIXEL_FORMAT_422P:
104 colorspace = MEDIA_VISION_COLORSPACE_422P;
106 case CAMERA_PIXEL_FORMAT_I420:
107 colorspace = MEDIA_VISION_COLORSPACE_I420;
109 case CAMERA_PIXEL_FORMAT_YV12:
110 colorspace = MEDIA_VISION_COLORSPACE_YV12;
112 case CAMERA_PIXEL_FORMAT_RGB565:
113 colorspace = MEDIA_VISION_COLORSPACE_RGB565;
115 case CAMERA_PIXEL_FORMAT_RGB888:
116 colorspace = MEDIA_VISION_COLORSPACE_RGB888;
118 case CAMERA_PIXEL_FORMAT_RGBA:
119 colorspace = MEDIA_VISION_COLORSPACE_RGBA;
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:
128 colorspace = MEDIA_VISION_COLORSPACE_INVALID;
129 _E("unsupported format : %d", format);
136 static void __thread_write_image_file(void *data, Ecore_Thread *th)
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;
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;
154 image_info = strdup("00");
156 pthread_mutex_unlock(&ad->mutex);
158 ret = controller_image_save_image_file(ad->temp_image_filename, width, height, buffer, image_info, strlen(image_info));
160 _E("failed to save image file");
162 ret = rename(ad->temp_image_filename, ad->latest_image_filename);
170 static void __thread_end_cb(void *data, Ecore_Thread *th)
172 app_data *ad = (app_data *)data;
174 pthread_mutex_lock(&ad->mutex);
175 ad->image_writter_thread = NULL;
176 pthread_mutex_unlock(&ad->mutex);
179 static void __thread_cancel_cb(void *data, Ecore_Thread *th)
181 app_data *ad = (app_data *)data;
182 unsigned char *buffer = NULL;
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);
194 static void __copy_image_buffer(image_buffer_data_s *image_buffer, app_data *ad)
196 unsigned char *buffer = NULL;
198 pthread_mutex_lock(&ad->mutex);
199 ad->latest_image_height = image_buffer->image_height;
200 ad->latest_image_width = image_buffer->image_width;
202 buffer = ad->latest_image_buffer;
203 ad->latest_image_buffer = image_buffer->buffer;
204 pthread_mutex_unlock(&ad->mutex);
209 static void __preview_image_buffer_created_cb(void *data)
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;
218 ret_if(!image_buffer);
221 image_colorspace = __convert_colorspace_from_cam_to_mv(image_buffer->format);
222 goto_if(image_colorspace == MEDIA_VISION_COLORSPACE_INVALID, FREE_ALL_BUFFER);
224 __copy_image_buffer(image_buffer, ad);
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);
233 pthread_mutex_lock(&ad->mutex);
234 info = ad->latest_image_info;
235 ad->latest_image_info = NULL;
236 pthread_mutex_unlock(&ad->mutex);
240 controller_mv_push_source(source);
244 motion_state_set(ad->motion_state, APP_CALLBACK_KEY);
245 ad->motion_state = 0;
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);
251 _E("Thread is running NOW");
253 pthread_mutex_unlock(&ad->mutex);
258 free(image_buffer->buffer);
262 static void __move_camera(int x, int y, void *user_data)
264 app_data *ad = (app_data *)user_data;
267 // x, y Range : -10 ~ 10
269 if (x < -10) x = -10;
271 if (y < -10) y = -10;
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;
277 if (calculated_x > SERVO_MOTOR_HORIZONTAL_MAX)
278 calculated_x = SERVO_MOTOR_HORIZONTAL_MAX;
280 if (calculated_x < SERVO_MOTOR_HORIZONTAL_MIN)
281 calculated_x = SERVO_MOTOR_HORIZONTAL_MIN;
283 if (calculated_y > SERVO_MOTOR_VERTICAL_MAX)
284 calculated_y = SERVO_MOTOR_VERTICAL_MAX;
286 if (calculated_y < SERVO_MOTOR_VERTICAL_MIN)
287 calculated_y = SERVO_MOTOR_VERTICAL_MIN;
289 ad->current_servo_x = calculated_x;
290 ad->current_servo_y = calculated_y;
292 servo_h_state_set(calculated_x, APP_CALLBACK_KEY);
293 servo_v_state_set(calculated_y, APP_CALLBACK_KEY);
298 static void __set_result_info(int result[], int result_count, app_data *ad, int image_result_type)
300 char image_info[IMAGE_INFO_MAX + 1] = {'\0', };
301 char *current_position;
302 int current_index = 0;
303 int string_count = 0;
305 char *latest_image_info = NULL;
308 current_position = image_info;
310 current_position += snprintf(current_position, IMAGE_INFO_MAX, "%02d", image_result_type);
313 current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d", result_count);
316 for (i = 0; i < result_count; i++) {
317 current_index = i * 4;
318 if (IMAGE_INFO_MAX - string_count < 8)
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]);
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);
334 static void __mv_detection_event_cb(int horizontal, int vertical, int result[], int result_count, void *user_data)
336 app_data *ad = (app_data *)user_data;
337 long long int now = __get_monotonic_ms();
339 ad->motion_state = 1;
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);
350 if (now < ad->last_valid_event_time + VALID_EVENT_INTERVAL_MS) {
351 ad->valid_event_count++;
353 ad->valid_event_count = 1;
356 ad->last_valid_event_time = now;
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);
368 ad->valid_event_count = 0;
369 ad->valid_vision_result_x_sum += horizontal;
370 ad->valid_vision_result_y_sum += vertical;
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;
375 x = 10 * x / (IMAGE_WIDTH / 2);
376 y = 10 * y / (IMAGE_HEIGHT / 2);
378 __move_camera((int) x, (int) y, ad);
379 ad->last_moved_time = now;
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);
387 __set_result_info(result, result_count, ad, 2);
390 static void __switch_changed(switch_state_e state, void* user_data)
392 app_data *ad = (app_data *)user_data;
393 _D("switch changed to - %d", state);
396 /* Move servo motors to initial position */
397 if (state != SWITCH_STATE_ON)
400 ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
401 ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
403 servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
404 servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
407 static void __servo_v_changed(double value, void* user_data)
409 app_data *ad = (app_data *)user_data;
412 _D("servo_v changed to - %lf", value);
413 ad->current_servo_y = value;
416 static void __servo_h_changed(double value, void* user_data)
418 app_data *ad = (app_data *)user_data;
421 _D("servo_h changed to - %lf", value);
422 ad->current_servo_x = value;
425 static void __device_interfaces_fini(void)
433 static int __device_interfaces_init(app_data *ad)
437 if (switch_initialize()) {
438 _E("failed to switch_initialize()");
442 if (servo_v_initialize()) {
443 _E("failed to servo_v_initialize()");
447 if (servo_h_initialize()) {
448 _E("failed to servo_h_initialize()");
452 if (motion_initialize()) {
453 _E("failed to motion_initialize()");
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
465 __device_interfaces_fini();
469 static bool service_app_create(void *data)
471 app_data *ad = (app_data *)data;
473 char* shared_data_path = app_get_shared_data_path();
474 if (shared_data_path == NULL) {
475 _E("Failed to get shared data path");
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);
482 _D("%s", ad->temp_image_filename);
483 _D("%s", ad->latest_image_filename);
485 controller_image_initialize();
487 pthread_mutex_init(&ad->mutex, NULL);
489 if (__device_interfaces_init(ad))
492 if (controller_mv_set_movement_detection_event_cb(__mv_detection_event_cb, data) == -1) {
493 _E("Failed to set movement detection event callback");
497 if (resource_camera_init(__preview_image_buffer_created_cb, ad) == -1) {
498 _E("Failed to init camera");
502 if (resource_camera_start_preview() == -1) {
503 _E("Failed to start camera preview");
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())
512 if (st_thing_resource_init())
514 #endif /* ENABLE_SMARTTHINGS */
516 ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
517 ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
519 servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
520 servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
525 __device_interfaces_fini();
527 resource_camera_close();
528 controller_mv_unset_movement_detection_event_cb();
529 controller_image_finalize();
531 #ifdef ENABLE_SMARTTHINGS
532 st_thing_master_fini();
533 st_thing_resource_fini();
534 #endif /* ENABLE_SMARTTHINGS */
536 pthread_mutex_destroy(&ad->mutex);
541 static void service_app_terminate(void *data)
543 app_data *ad = (app_data *)data;
544 Ecore_Thread *thread_id = NULL;
545 unsigned char *buffer = NULL;
547 gchar *temp_image_filename;
548 gchar *latest_image_filename;
549 _D("App Terminated - enter");
551 resource_camera_close();
552 controller_mv_unset_movement_detection_event_cb();
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);
560 ecore_thread_wait(thread_id, 3.0); // wait for 3 second
562 __device_interfaces_fini();
564 controller_image_finalize();
566 #ifdef ENABLE_SMARTTHINGS
567 st_thing_master_fini();
568 st_thing_resource_fini();
569 #endif /* ENABLE_SMARTTHINGS */
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);
583 g_free(temp_image_filename);
584 g_free(latest_image_filename);
586 pthread_mutex_destroy(&ad->mutex);
588 _D("App Terminated - leave");
591 static void service_app_control(app_control_h app_control, void *data)
596 int main(int argc, char* argv[])
600 service_app_lifecycle_callback_s event_callback;
602 ad = calloc(1, sizeof(app_data));
605 event_callback.create = service_app_create;
606 event_callback.terminate = service_app_terminate;
607 event_callback.app_control = service_app_control;
609 ret = service_app_main(argc, argv, &event_callback, ad);