1 /* motion.c
2 *
3 * Detect changes in a video stream.
4 * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
5 * This software is distributed under the GNU public license version 2
6 * See also the file 'COPYING'.
7 *
8 */
9 #include "translate.h"
10 #include "motion.h"
11 #include "ffmpeg.h"
12 #include "video_common.h"
13 #include "video_v4l2.h"
14 #include "video_loopback.h"
15 #include "conf.h"
16 #include "alg.h"
17 #include "track.h"
18 #include "event.h"
19 #include "picture.h"
20 #include "rotate.h"
21 #include "webu.h"
22
23
24 #define IMAGE_BUFFER_FLUSH ((unsigned int)-1)
25
26 /**
27 * tls_key_threadnr
28 *
29 * TLS key for storing thread number in thread-local storage.
30 */
31 pthread_key_t tls_key_threadnr;
32
33 /**
34 * global_lock
35 *
36 * Protects any global variables (like 'threads_running') during updates,
37 * to prevent problems with multiple threads updating at the same time.
38 */
39 //pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;
40 pthread_mutex_t global_lock;
41
42 /**
43 * cnt_list
44 *
45 * List of context structures, one for each main Motion thread.
46 */
47 struct context **cnt_list = NULL;
48
49 /**
50 * threads_running
51 *
52 * Keeps track of number of Motion threads currently running. Also used
53 * by 'main' to know when all threads have exited.
54 */
55 volatile int threads_running = 0;
56
57 /* Set this when we want main to end or restart */
58 volatile unsigned int finish = 0;
59
60 /* Log file used instead of stderr and syslog */
61 FILE *ptr_logfile = NULL;
62
63 /**
64 * restart
65 *
66 * Differentiates between a quit and a restart. When all threads have
67 * finished running, 'main' checks if 'restart' is true and if so starts
68 * up again (instead of just quitting).
69 */
70 unsigned int restart = 0;
71
72
73 /**
74 * image_ring_resize
75 *
76 * This routine is called from motion_loop to resize the image precapture ringbuffer
77 * NOTE: This function clears all images in the old ring buffer
78
79 * Parameters:
80 *
81 * cnt Pointer to the motion context structure
82 * new_size The new size of the ring buffer
83 *
84 * Returns: nothing
85 */
image_ring_resize(struct context * cnt,int new_size)86 static void image_ring_resize(struct context *cnt, int new_size)
87 {
88 /*
89 * Only resize if :
90 * Not in an event and
91 * decreasing at last position in new buffer
92 * increasing at last position in old buffer
93 * e.g. at end of smallest buffer
94 */
95 if (cnt->event_nr != cnt->prev_event) {
96 int smallest;
97
98 if (new_size < cnt->imgs.image_ring_size) /* Decreasing */
99 smallest = new_size;
100 else /* Increasing */
101 smallest = cnt->imgs.image_ring_size;
102
103 if (cnt->imgs.image_ring_in == smallest - 1 || smallest == 0) {
104 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
105 ,_("Resizing pre_capture buffer to %d items"), new_size);
106
107 /* Create memory for new ring buffer */
108 struct image_data *tmp;
109 tmp = mymalloc(new_size * sizeof(struct image_data));
110
111 /*
112 * Copy all information from old to new
113 * Smallest is 0 at initial init
114 */
115 if (smallest > 0)
116 memcpy(tmp, cnt->imgs.image_ring, sizeof(struct image_data) * smallest);
117
118
119 /* In the new buffers, allocate image memory */
120 {
121 int i;
122 for(i = smallest; i < new_size; i++) {
123 tmp[i].image_norm = mymalloc(cnt->imgs.size_norm);
124 memset(tmp[i].image_norm, 0x80, cnt->imgs.size_norm); /* initialize to grey */
125 if (cnt->imgs.size_high > 0){
126 tmp[i].image_high = mymalloc(cnt->imgs.size_high);
127 memset(tmp[i].image_high, 0x80, cnt->imgs.size_high);
128 }
129 }
130 }
131
132 /* Free the old ring */
133 free(cnt->imgs.image_ring);
134
135 /* Point to the new ring */
136 cnt->imgs.image_ring = tmp;
137 cnt->current_image = NULL;
138
139 cnt->imgs.image_ring_size = new_size;
140
141 cnt->imgs.image_ring_in = 0;
142 cnt->imgs.image_ring_out = 0;
143 }
144 }
145 }
146
147 /**
148 * image_ring_destroy
149 *
150 * This routine is called when we want to free the ring
151 *
152 * Parameters:
153 *
154 * cnt Pointer to the motion context structure
155 *
156 * Returns: nothing
157 */
image_ring_destroy(struct context * cnt)158 static void image_ring_destroy(struct context *cnt)
159 {
160 int i;
161
162 /* Exit if don't have any ring */
163 if (cnt->imgs.image_ring == NULL)
164 return;
165
166 /* Free all image buffers */
167 for (i = 0; i < cnt->imgs.image_ring_size; i++){
168 free(cnt->imgs.image_ring[i].image_norm);
169 if (cnt->imgs.size_high >0 ) free(cnt->imgs.image_ring[i].image_high);
170 }
171
172 /* Free the ring */
173 free(cnt->imgs.image_ring);
174
175 cnt->imgs.image_ring = NULL;
176 cnt->current_image = NULL;
177 cnt->imgs.image_ring_size = 0;
178 }
179
180 /**
181 * image_save_as_preview
182 *
183 * This routine is called when we detect motion and want to save an image in the preview buffer
184 *
185 * Parameters:
186 *
187 * cnt Pointer to the motion context structure
188 * img Pointer to the image_data structure we want to set as preview image
189 *
190 * Returns: nothing
191 */
image_save_as_preview(struct context * cnt,struct image_data * img)192 static void image_save_as_preview(struct context *cnt, struct image_data *img)
193 {
194 void *image_norm, *image_high;
195
196 /* Save our pointers to our memory locations for images*/
197 image_norm = cnt->imgs.preview_image.image_norm;
198 image_high = cnt->imgs.preview_image.image_high;
199
200 /* Copy over the meta data from the img into preview */
201 memcpy(&cnt->imgs.preview_image, img, sizeof(struct image_data));
202
203 /* Restore the pointers to the memory locations for images*/
204 cnt->imgs.preview_image.image_norm = image_norm;
205 cnt->imgs.preview_image.image_high = image_high;
206
207 /* Copy the actual images for norm and high */
208 memcpy(cnt->imgs.preview_image.image_norm, img->image_norm, cnt->imgs.size_norm);
209 if (cnt->imgs.size_high > 0){
210 memcpy(cnt->imgs.preview_image.image_high, img->image_high, cnt->imgs.size_high);
211 }
212
213 /*
214 * If we set output_all to yes and during the event
215 * there is no image with motion, diffs is 0, we are not going to save the preview event
216 */
217 if (cnt->imgs.preview_image.diffs == 0)
218 cnt->imgs.preview_image.diffs = 1;
219
220 /* draw locate box here when mode = LOCATE_PREVIEW */
221 if (cnt->locate_motion_mode == LOCATE_PREVIEW) {
222
223 if (cnt->locate_motion_style == LOCATE_BOX) {
224 alg_draw_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image_norm,
225 LOCATE_BOX, LOCATE_NORMAL, cnt->process_thisframe);
226 } else if (cnt->locate_motion_style == LOCATE_REDBOX) {
227 alg_draw_red_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image_norm,
228 LOCATE_REDBOX, LOCATE_NORMAL, cnt->process_thisframe);
229 } else if (cnt->locate_motion_style == LOCATE_CROSS) {
230 alg_draw_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image_norm,
231 LOCATE_CROSS, LOCATE_NORMAL, cnt->process_thisframe);
232 } else if (cnt->locate_motion_style == LOCATE_REDCROSS) {
233 alg_draw_red_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image_norm,
234 LOCATE_REDCROSS, LOCATE_NORMAL, cnt->process_thisframe);
235 }
236 }
237 }
238
239 /**
240 * context_init
241 *
242 * Initializes a context struct with the default values for all the
243 * variables.
244 *
245 * Parameters:
246 *
247 * cnt - the context struct to destroy
248 *
249 * Returns: nothing
250 */
context_init(struct context * cnt)251 static void context_init(struct context *cnt)
252 {
253 /*
254 * We first clear the entire structure to zero, then fill in any
255 * values which have non-zero default values. Note that this
256 * assumes that a NULL address pointer has a value of binary 0
257 * (this is also assumed at other places within the code, i.e.
258 * there are instances of "if (ptr)"). Just for possible future
259 * changes to this assumption, any pointers which are intended
260 * to be initialised to NULL are listed within a comment.
261 */
262
263 memset(cnt, 0, sizeof(struct context));
264 cnt->noise = 255;
265 cnt->lastrate = 25;
266
267 memcpy(&cnt->track, &track_template, sizeof(struct trackoptions));
268
269 cnt->pipe = -1;
270 cnt->mpipe = -1;
271
272 cnt->vdev = NULL; /*Init to NULL to check loading parms vs web updates*/
273 cnt->netcam = NULL;
274 cnt->rtsp = NULL;
275 cnt->rtsp_high = NULL;
276
277 }
278
279 /**
280 * context_destroy
281 *
282 * Destroys a context struct by freeing allocated memory, calling the
283 * appropriate cleanup functions and finally freeing the struct itself.
284 *
285 * Parameters:
286 *
287 * cnt - the context struct to destroy
288 *
289 * Returns: nothing
290 */
context_destroy(struct context * cnt)291 static void context_destroy(struct context *cnt)
292 {
293 unsigned int j;
294
295 /* Free memory allocated for config parameters */
296 for (j = 0; config_params[j].param_name != NULL; j++) {
297 if (config_params[j].copy == copy_string ||
298 config_params[j].copy == copy_uri ||
299 config_params[j].copy == read_camera_dir) {
300 void **val;
301 val = (void *)((char *)cnt+(int)config_params[j].conf_value);
302 if (*val) {
303 free(*val);
304 *val = NULL;
305 }
306 }
307 }
308
309 free(cnt);
310 }
311
312 /**
313 * sig_handler
314 *
315 * Our SIGNAL-Handler. We need this to handle alarms and external signals.
316 */
sig_handler(int signo)317 static void sig_handler(int signo)
318 {
319 int i;
320
321 /*The FALLTHROUGH is a special comment required by compiler. Do not edit it*/
322 switch(signo) {
323 case SIGALRM:
324 /*
325 * Somebody (maybe we ourself) wants us to make a snapshot
326 * This feature triggers snapshots on ALL threads that have
327 * snapshot_interval different from 0.
328 */
329 if (cnt_list) {
330 i = -1;
331 while (cnt_list[++i]) {
332 if (cnt_list[i]->conf.snapshot_interval)
333 cnt_list[i]->snapshot = 1;
334
335 }
336 }
337 break;
338 case SIGUSR1:
339 /* Trigger the end of a event */
340 if (cnt_list) {
341 i = -1;
342 while (cnt_list[++i]){
343 cnt_list[i]->event_stop = TRUE;
344 }
345 }
346 break;
347 case SIGHUP:
348 restart = 1;
349 /*
350 * Fall through, as the value of 'restart' is the only difference
351 * between SIGHUP and the ones below.
352 */
353 /*FALLTHROUGH*/
354 case SIGINT:
355 /*FALLTHROUGH*/
356 case SIGQUIT:
357 /*FALLTHROUGH*/
358 case SIGTERM:
359 /*
360 * Somebody wants us to quit! We should finish the actual
361 * movie and end up!
362 */
363
364 if (cnt_list) {
365 i = -1;
366 while (cnt_list[++i]) {
367 cnt_list[i]->webcontrol_finish = TRUE;
368 cnt_list[i]->event_stop = TRUE;
369 cnt_list[i]->finish = 1;
370 /*
371 * Don't restart thread when it ends,
372 * all threads restarts if global restart is set
373 */
374 cnt_list[i]->restart = 0;
375 }
376 }
377 /*
378 * Set flag we want to quit main check threads loop
379 * if restart is set (above) we start up again
380 */
381 finish = 1;
382 break;
383 case SIGSEGV:
384 exit(0);
385 case SIGVTALRM:
386 printf("SIGVTALRM went off\n");
387 break;
388 }
389 }
390
391 /**
392 * sigchild_handler
393 *
394 * This function is a POSIX compliant replacement of the commonly used
395 * signal(SIGCHLD, SIG_IGN).
396 */
sigchild_handler(int signo ATTRIBUTE_UNUSED)397 static void sigchild_handler(int signo ATTRIBUTE_UNUSED)
398 {
399 #ifdef WNOHANG
400 while (waitpid(-1, NULL, WNOHANG) > 0) {};
401 #endif /* WNOHANG */
402 return;
403 }
404
405 /**
406 * setup_signals
407 * Attaches handlers to a number of signals that Motion need to catch.
408 */
setup_signals(void)409 static void setup_signals(void){
410 /*
411 * Setup signals and do some initialization. 1 in the call to
412 * 'motion_startup' means that Motion will become a daemon if so has been
413 * requested, and argc and argc are necessary for reading the command
414 * line options.
415 */
416 struct sigaction sig_handler_action;
417 struct sigaction sigchild_action;
418
419 #ifdef SA_NOCLDWAIT
420 sigchild_action.sa_flags = SA_NOCLDWAIT;
421 #else
422 sigchild_action.sa_flags = 0;
423 #endif
424 sigchild_action.sa_handler = sigchild_handler;
425 sigemptyset(&sigchild_action.sa_mask);
426 #ifdef SA_RESTART
427 sig_handler_action.sa_flags = SA_RESTART;
428 #else
429 sig_handler_action.sa_flags = 0;
430 #endif
431 sig_handler_action.sa_handler = sig_handler;
432 sigemptyset(&sig_handler_action.sa_mask);
433
434 /* Enable automatic zombie reaping */
435 sigaction(SIGCHLD, &sigchild_action, NULL);
436 sigaction(SIGPIPE, &sigchild_action, NULL);
437 sigaction(SIGALRM, &sig_handler_action, NULL);
438 sigaction(SIGHUP, &sig_handler_action, NULL);
439 sigaction(SIGINT, &sig_handler_action, NULL);
440 sigaction(SIGQUIT, &sig_handler_action, NULL);
441 sigaction(SIGTERM, &sig_handler_action, NULL);
442 sigaction(SIGUSR1, &sig_handler_action, NULL);
443
444 /* use SIGVTALRM as a way to break out of the ioctl, don't restart */
445 sig_handler_action.sa_flags = 0;
446 sigaction(SIGVTALRM, &sig_handler_action, NULL);
447 }
448
449 /**
450 * motion_remove_pid
451 * This function remove the process id file ( pid file ) before motion exit.
452 */
motion_remove_pid(void)453 static void motion_remove_pid(void)
454 {
455 if ((cnt_list[0]->daemon) && (cnt_list[0]->conf.pid_file) && (restart == 0)) {
456 if (!unlink(cnt_list[0]->conf.pid_file))
457 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Removed process id file (pid file)."));
458 else
459 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, _("Error removing pid file"));
460 }
461
462 if (ptr_logfile) {
463 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Closing logfile (%s)."),
464 cnt_list[0]->conf.log_file);
465 myfclose(ptr_logfile);
466 set_log_mode(LOGMODE_NONE);
467 ptr_logfile = NULL;
468 }
469
470 }
471
472 /**
473 * motion_detected
474 *
475 * Called from 'motion_loop' when motion is detected
476 * Can be called when no motion if emulate_motion is set!
477 *
478 * Parameters:
479 *
480 * cnt - current thread's context struct
481 * dev - video device file descriptor
482 * img - pointer to the captured image_data with detected motion
483 */
motion_detected(struct context * cnt,int dev,struct image_data * img)484 static void motion_detected(struct context *cnt, int dev, struct image_data *img)
485 {
486 struct config *conf = &cnt->conf;
487 struct images *imgs = &cnt->imgs;
488 struct coord *location = &img->location;
489 int indx;
490
491 /* Draw location */
492 if (cnt->locate_motion_mode == LOCATE_ON) {
493
494 if (cnt->locate_motion_style == LOCATE_BOX) {
495 alg_draw_location(location, imgs, imgs->width, img->image_norm, LOCATE_BOX,
496 LOCATE_BOTH, cnt->process_thisframe);
497 } else if (cnt->locate_motion_style == LOCATE_REDBOX) {
498 alg_draw_red_location(location, imgs, imgs->width, img->image_norm, LOCATE_REDBOX,
499 LOCATE_BOTH, cnt->process_thisframe);
500 } else if (cnt->locate_motion_style == LOCATE_CROSS) {
501 alg_draw_location(location, imgs, imgs->width, img->image_norm, LOCATE_CROSS,
502 LOCATE_BOTH, cnt->process_thisframe);
503 } else if (cnt->locate_motion_style == LOCATE_REDCROSS) {
504 alg_draw_red_location(location, imgs, imgs->width, img->image_norm, LOCATE_REDCROSS,
505 LOCATE_BOTH, cnt->process_thisframe);
506 }
507 }
508
509 /* Calculate how centric motion is if configured preview center*/
510 if (cnt->new_img & NEWIMG_CENTER) {
511 unsigned int distX = abs((imgs->width / 2) - location->x);
512 unsigned int distY = abs((imgs->height / 2) - location->y);
513
514 img->cent_dist = distX * distX + distY * distY;
515 }
516
517
518 /* Do things only if we have got minimum_motion_frames */
519 if (img->flags & IMAGE_TRIGGER) {
520 /* Take action if this is a new event and we have a trigger image */
521 if (cnt->event_nr != cnt->prev_event) {
522 /*
523 * Reset prev_event number to current event and save event time
524 * in both time_t and struct tm format.
525 */
526 cnt->prev_event = cnt->event_nr;
527 cnt->eventtime = img->timestamp_tv.tv_sec;
528 localtime_r(&cnt->eventtime, cnt->eventtime_tm);
529
530 /*
531 * Since this is a new event we create the event_text_string used for
532 * the %C conversion specifier. We may already need it for
533 * on_motion_detected_commend so it must be done now.
534 */
535 mystrftime(cnt, cnt->text_event_string, sizeof(cnt->text_event_string),
536 cnt->conf.text_event, &img->timestamp_tv, NULL, 0);
537
538 /* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */
539
540 indx = cnt->imgs.image_ring_out-1;
541 do {
542 indx++;
543 if (indx == cnt->imgs.image_ring_size) indx = 0;
544 if ((cnt->imgs.image_ring[indx].flags & (IMAGE_SAVE | IMAGE_SAVED)) == IMAGE_SAVE){
545 event(cnt, EVENT_FIRSTMOTION, img, NULL, NULL, &cnt->imgs.image_ring[indx].timestamp_tv);
546 indx = cnt->imgs.image_ring_in;
547 }
548 } while (indx != cnt->imgs.image_ring_in);
549
550 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Motion detected - starting event %d"),
551 cnt->event_nr);
552
553 /* always save first motion frame as preview-shot, may be changed to an other one later */
554 if (cnt->new_img & (NEWIMG_FIRST | NEWIMG_BEST | NEWIMG_CENTER))
555 image_save_as_preview(cnt, img);
556
557 }
558
559 /* EVENT_MOTION triggers event_beep and on_motion_detected_command */
560 event(cnt, EVENT_MOTION, NULL, NULL, NULL, &img->timestamp_tv);
561 }
562
563 /* Limit framerate */
564 if (img->shot < conf->framerate) {
565 /*
566 * If config option stream_motion is enabled, send the latest motion detected image
567 * to the stream but only if it is not the first shot within a second. This is to
568 * avoid double frames since we already have sent a frame to the stream.
569 * We also disable this in setup_mode.
570 */
571 if (conf->stream_motion && !conf->setup_mode && img->shot != 1)
572 event(cnt, EVENT_STREAM, img, NULL, NULL, &img->timestamp_tv);
573
574 /*
575 * Save motion jpeg, if configured
576 * Output the image_out (motion) picture.
577 */
578 if (conf->picture_output_motion)
579 event(cnt, EVENT_IMAGEM_DETECTED, NULL, NULL, NULL, &img->timestamp_tv);
580 }
581
582 /* if track enabled and auto track on */
583 if (cnt->track.type && cnt->track.active)
584 cnt->moved = track_move(cnt, dev, location, imgs, 0);
585
586 }
587
588 /**
589 * process_image_ring
590 *
591 * Called from 'motion_loop' to save images / send images to movie
592 *
593 * Parameters:
594 *
595 * cnt - current thread's context struct
596 * max_images - Max number of images to process
597 * Set to IMAGE_BUFFER_FLUSH to send/save all images in buffer
598 */
599
process_image_ring(struct context * cnt,unsigned int max_images)600 static void process_image_ring(struct context *cnt, unsigned int max_images)
601 {
602 /*
603 * We are going to send an event, in the events there is still
604 * some code that use cnt->current_image
605 * so set it temporary to our image
606 */
607 struct image_data *saved_current_image = cnt->current_image;
608
609 /* If image is flaged to be saved and not saved yet, process it */
610 do {
611 /* Check if we should save/send this image, breakout if not */
612 assert(cnt->imgs.image_ring_out < cnt->imgs.image_ring_size);
613 if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & (IMAGE_SAVE | IMAGE_SAVED)) != IMAGE_SAVE)
614 break;
615
616 /* Set inte global context that we are working with this image */
617 cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_out];
618
619 if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot < cnt->conf.framerate) {
620 if (cnt->log_level >= DBG) {
621 char tmp[32];
622 const char *t;
623
624 if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_TRIGGER)
625 t = "Trigger";
626 else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION)
627 t = "Motion";
628 else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_PRECAP)
629 t = "Precap";
630 else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_POSTCAP)
631 t = "Postcap";
632 else
633 t = "Other";
634
635 mystrftime(cnt, tmp, sizeof(tmp), "%H%M%S-%q",
636 &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tv, NULL, 0);
637 draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image_norm,
638 cnt->imgs.width, cnt->imgs.height, 10, 20, tmp, cnt->text_scale);
639 draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image_norm,
640 cnt->imgs.width, cnt->imgs.height, 10, 30, t, cnt->text_scale);
641 }
642
643 /* Output the picture to jpegs and ffmpeg */
644 event(cnt, EVENT_IMAGE_DETECTED,
645 &cnt->imgs.image_ring[cnt->imgs.image_ring_out], NULL, NULL,
646 &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tv);
647
648
649 /*
650 * Check if we must add any "filler" frames into movie to keep up fps
651 * Only if we are recording videos ( ffmpeg or extenal pipe )
652 * While the overall elapsed time might be correct, if there are
653 * many duplicated frames, say 10 fps, 5 duplicated, the video will
654 * look like it is frozen every second for half a second.
655 */
656 if (!cnt->conf.movie_duplicate_frames) {
657 /* don't duplicate frames */
658 } else if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) &&
659 (cnt->ffmpeg_output || (cnt->conf.movie_extpipe_use && cnt->extpipe))) {
660 /*
661 * movie_last_shoot is -1 when file is created,
662 * we don't know how many frames there is in first sec
663 */
664 if (cnt->movie_last_shot >= 0) {
665 if (cnt_list[0]->log_level >= DBG) {
666 int frames = cnt->movie_fps - (cnt->movie_last_shot + 1);
667 if (frames > 0) {
668 char tmp[25];
669 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO
670 ,_("Added %d fillerframes into movie"), frames);
671 sprintf(tmp, "Fillerframes %d", frames);
672 draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image_norm,
673 cnt->imgs.width, cnt->imgs.height, 10, 40, tmp, cnt->text_scale);
674 }
675 }
676 /* Check how many frames it was last sec */
677 while ((cnt->movie_last_shot + 1) < cnt->movie_fps) {
678 /* Add a filler frame into encoder */
679 event(cnt, EVENT_FFMPEG_PUT,
680 &cnt->imgs.image_ring[cnt->imgs.image_ring_out], NULL, NULL,
681 &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tv);
682
683 cnt->movie_last_shot++;
684 }
685 }
686 cnt->movie_last_shot = 0;
687 } else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot != (cnt->movie_last_shot + 1)) {
688 /* We are out of sync! Propably we got motion - no motion - motion */
689 cnt->movie_last_shot = -1;
690 }
691
692 /*
693 * Save last shot added to movie
694 * only when we not are within first sec
695 */
696 if (cnt->movie_last_shot >= 0)
697 cnt->movie_last_shot = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot;
698 }
699
700 /* Mark the image as saved */
701 cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags |= IMAGE_SAVED;
702
703 /* Store it as a preview image, only if it has motion */
704 if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) {
705 /* Check for most significant preview-shot when picture_output=best */
706 if (cnt->new_img & NEWIMG_BEST) {
707 if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].diffs > cnt->imgs.preview_image.diffs) {
708 image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]);
709 }
710 }
711 /* Check for most significant preview-shot when picture_output=center */
712 if (cnt->new_img & NEWIMG_CENTER) {
713 if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].cent_dist < cnt->imgs.preview_image.cent_dist) {
714 image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]);
715 }
716 }
717 }
718
719 /* Increment to image after last sended */
720 if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size)
721 cnt->imgs.image_ring_out = 0;
722
723 if (max_images != IMAGE_BUFFER_FLUSH) {
724 max_images--;
725 /* breakout if we have done max_images */
726 if (max_images == 0)
727 break;
728 }
729
730 /* loop until out and in is same e.g. buffer empty */
731 } while (cnt->imgs.image_ring_out != cnt->imgs.image_ring_in);
732
733 /* restore global context values */
734 cnt->current_image = saved_current_image;
735 }
736
init_camera_type(struct context * cnt)737 static int init_camera_type(struct context *cnt){
738
739 cnt->camera_type = CAMERA_TYPE_UNKNOWN;
740
741 #ifdef HAVE_MMAL
742 if (cnt->conf.mmalcam_name) {
743 cnt->camera_type = CAMERA_TYPE_MMAL;
744 return 0;
745 }
746 #endif // HAVE_MMAL
747
748 if (cnt->conf.netcam_url) {
749 if ((strncmp(cnt->conf.netcam_url,"mjpeg",5) == 0) ||
750 (strncmp(cnt->conf.netcam_url,"v4l2" ,4) == 0) ||
751 (strncmp(cnt->conf.netcam_url,"file" ,4) == 0) ||
752 (strncmp(cnt->conf.netcam_url,"rtmp" ,4) == 0) ||
753 (strncmp(cnt->conf.netcam_url,"rtsp" ,4) == 0)) {
754 cnt->camera_type = CAMERA_TYPE_RTSP;
755 } else {
756 cnt->camera_type = CAMERA_TYPE_NETCAM;
757 }
758 return 0;
759 }
760
761 #ifdef HAVE_BKTR
762 if (strncmp(cnt->conf.video_device,"/dev/bktr",9) == 0) {
763 cnt->camera_type = CAMERA_TYPE_BKTR;
764 return 0;
765 }
766 #endif // HAVE_BKTR
767
768 #ifdef HAVE_V4L2
769 if (cnt->conf.video_device) {
770 cnt->camera_type = CAMERA_TYPE_V4L2;
771 return 0;
772 }
773 #endif // HAVE_V4L2
774
775
776 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
777 , _("Unable to determine camera type (MMAL, Netcam, V4L2, BKTR)"));
778 return -1;
779
780 }
781
init_mask_privacy(struct context * cnt)782 static void init_mask_privacy(struct context *cnt){
783
784 int indxrow, indxcol;
785 int start_cr, offset_cb, start_cb;
786 int y_index, uv_index;
787 int indx_img, indx_max; /* Counter and max for norm/high */
788 int indx_width, indx_height;
789 unsigned char *img_temp, *img_temp_uv;
790
791
792 FILE *picture;
793
794 /* Load the privacy file if any */
795 cnt->imgs.mask_privacy = NULL;
796 cnt->imgs.mask_privacy_uv = NULL;
797 cnt->imgs.mask_privacy_high = NULL;
798 cnt->imgs.mask_privacy_high_uv = NULL;
799
800 if (cnt->conf.mask_privacy) {
801 if ((picture = myfopen(cnt->conf.mask_privacy, "r"))) {
802 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Opening privacy mask file"));
803 /*
804 * NOTE: The mask is expected to have the output dimensions. I.e., the mask
805 * applies to the already rotated image, not the capture image. Thus, use
806 * width and height from imgs.
807 */
808 cnt->imgs.mask_privacy = get_pgm(picture, cnt->imgs.width, cnt->imgs.height);
809
810 /* We only need the "or" mask for the U & V chrominance area. */
811 cnt->imgs.mask_privacy_uv = mymalloc((cnt->imgs.height * cnt->imgs.width) / 2);
812 if (cnt->imgs.size_high > 0){
813 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO
814 ,_("Opening high resolution privacy mask file"));
815 rewind(picture);
816 cnt->imgs.mask_privacy_high = get_pgm(picture, cnt->imgs.width_high, cnt->imgs.height_high);
817 cnt->imgs.mask_privacy_high_uv = mymalloc((cnt->imgs.height_high * cnt->imgs.width_high) / 2);
818 }
819
820 myfclose(picture);
821 } else {
822 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
823 ,_("Error opening mask file %s"), cnt->conf.mask_privacy);
824 /* Try to write an empty mask file to make it easier for the user to edit it */
825 put_fixed_mask(cnt, cnt->conf.mask_privacy);
826 }
827
828 if (!cnt->imgs.mask_privacy) {
829 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
830 ,_("Failed to read mask privacy image. Mask privacy feature disabled."));
831 } else {
832 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO
833 ,_("Mask privacy file \"%s\" loaded."), cnt->conf.mask_privacy);
834
835 indx_img = 1;
836 indx_max = 1;
837 if (cnt->imgs.size_high > 0) indx_max = 2;
838
839 while (indx_img <= indx_max){
840 if (indx_img == 1){
841 start_cr = (cnt->imgs.height * cnt->imgs.width);
842 offset_cb = ((cnt->imgs.height * cnt->imgs.width)/4);
843 start_cb = start_cr + offset_cb;
844 indx_width = cnt->imgs.width;
845 indx_height = cnt->imgs.height;
846 img_temp = cnt->imgs.mask_privacy;
847 img_temp_uv = cnt->imgs.mask_privacy_uv;
848 } else {
849 start_cr = (cnt->imgs.height_high * cnt->imgs.width_high);
850 offset_cb = ((cnt->imgs.height_high * cnt->imgs.width_high)/4);
851 start_cb = start_cr + offset_cb;
852 indx_width = cnt->imgs.width_high;
853 indx_height = cnt->imgs.height_high;
854 img_temp = cnt->imgs.mask_privacy_high;
855 img_temp_uv = cnt->imgs.mask_privacy_high_uv;
856 }
857
858 for (indxrow = 0; indxrow < indx_height; indxrow++) {
859 for (indxcol = 0; indxcol < indx_width; indxcol++) {
860 y_index = indxcol + (indxrow * indx_width);
861 if (img_temp[y_index] == 0xff) {
862 if ((indxcol % 2 == 0) && (indxrow % 2 == 0) ){
863 uv_index = (indxcol/2) + ((indxrow * indx_width)/4);
864 img_temp[start_cr + uv_index] = 0xff;
865 img_temp[start_cb + uv_index] = 0xff;
866 img_temp_uv[uv_index] = 0x00;
867 img_temp_uv[offset_cb + uv_index] = 0x00;
868 }
869 } else {
870 img_temp[y_index] = 0x00;
871 if ((indxcol % 2 == 0) && (indxrow % 2 == 0) ){
872 uv_index = (indxcol/2) + ((indxrow * indx_width)/4);
873 img_temp[start_cr + uv_index] = 0x00;
874 img_temp[start_cb + uv_index] = 0x00;
875 img_temp_uv[uv_index] = 0x80;
876 img_temp_uv[offset_cb + uv_index] = 0x80;
877 }
878 }
879 }
880 }
881 indx_img++;
882 }
883 }
884 }
885
886 }
887
init_text_scale(struct context * cnt)888 static void init_text_scale(struct context *cnt){
889
890 /* Consider that web interface may change conf values at any moment.
891 * The below can put two sections in the image so make sure that after
892 * scaling does not occupy more than 1/4 of image (10 pixels * 2 lines)
893 */
894
895 cnt->text_scale = cnt->conf.text_scale;
896 if (cnt->text_scale <= 0) cnt->text_scale = 1;
897
898 if ((cnt->text_scale * 10 * 2) > (cnt->imgs.width / 4)) {
899 cnt->text_scale = (cnt->imgs.width / (4 * 10 * 2));
900 if (cnt->text_scale <= 0) cnt->text_scale = 1;
901 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
902 ,_("Invalid text scale. Adjusted to %d"), cnt->text_scale);
903 }
904
905 if ((cnt->text_scale * 10 * 2) > (cnt->imgs.height / 4)) {
906 cnt->text_scale = (cnt->imgs.height / (4 * 10 * 2));
907 if (cnt->text_scale <= 0) cnt->text_scale = 1;
908 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
909 ,_("Invalid text scale. Adjusted to %d"), cnt->text_scale);
910 }
911
912 /* If we had to modify the scale, change conf so we don't get another message */
913 cnt->conf.text_scale = cnt->text_scale;
914
915 }
916
mot_stream_init(struct context * cnt)917 static void mot_stream_init(struct context *cnt){
918
919 /* The image buffers are allocated in event_stream_put if needed*/
920 pthread_mutex_init(&cnt->mutex_stream, NULL);
921
922 cnt->imgs.substream_image = NULL;
923
924 cnt->stream_norm.jpeg_size = 0;
925 cnt->stream_norm.jpeg_data = NULL;
926 cnt->stream_norm.cnct_count = 0;
927
928 cnt->stream_sub.jpeg_size = 0;
929 cnt->stream_sub.jpeg_data = NULL;
930 cnt->stream_sub.cnct_count = 0;
931
932 cnt->stream_motion.jpeg_size = 0;
933 cnt->stream_motion.jpeg_data = NULL;
934 cnt->stream_motion.cnct_count = 0;
935
936 cnt->stream_source.jpeg_size = 0;
937 cnt->stream_source.jpeg_data = NULL;
938 cnt->stream_source.cnct_count = 0;
939
940 }
941
mot_stream_deinit(struct context * cnt)942 static void mot_stream_deinit(struct context *cnt){
943
944 /* Need to check whether buffers were allocated since init
945 * function defers the allocations to event_stream_put
946 */
947
948 pthread_mutex_destroy(&cnt->mutex_stream);
949
950 if (cnt->imgs.substream_image != NULL){
951 free(cnt->imgs.substream_image);
952 cnt->imgs.substream_image = NULL;
953 }
954
955 if (cnt->stream_norm.jpeg_data != NULL){
956 free(cnt->stream_norm.jpeg_data);
957 cnt->stream_norm.jpeg_data = NULL;
958 }
959
960 if (cnt->stream_sub.jpeg_data != NULL){
961 free(cnt->stream_sub.jpeg_data);
962 cnt->stream_sub.jpeg_data = NULL;
963 }
964
965 if (cnt->stream_motion.jpeg_data != NULL){
966 free(cnt->stream_motion.jpeg_data);
967 cnt->stream_motion.jpeg_data = NULL;
968 }
969
970 if (cnt->stream_source.jpeg_data != NULL){
971 free(cnt->stream_source.jpeg_data);
972 cnt->stream_source.jpeg_data = NULL;
973 }
974 }
975
976 /* TODO: dbse functions are to be moved to separate module in future change*/
dbse_global_deinit(void)977 static void dbse_global_deinit(void){
978 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, _("Closing MYSQL"));
979 #if defined(HAVE_MYSQL) || defined(HAVE_MARIADB)
980 mysql_library_end();
981 #endif /* HAVE_MYSQL HAVE_MARIADB */
982
983 }
984
dbse_global_init(void)985 static void dbse_global_init(void){
986
987 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("Initializing database"));
988 /* Initialize all the database items */
989 #if defined(HAVE_MYSQL) || defined(HAVE_MARIADB)
990 if (mysql_library_init(0, NULL, NULL)) {
991 fprintf(stderr, "could not initialize MySQL library\n");
992 exit(1);
993 }
994 #endif /* HAVE_MYSQL HAVE_MARIADB */
995
996 #ifdef HAVE_SQLITE3
997 int indx;
998 /* database_sqlite3 == NULL if not changed causes each thread to create their own
999 * sqlite3 connection this will only happens when using a non-threaded sqlite version */
1000 cnt_list[0]->database_sqlite3=NULL;
1001 if (cnt_list[0]->conf.database_type && ((!strcmp(cnt_list[0]->conf.database_type, "sqlite3")) && cnt_list[0]->conf.database_dbname)) {
1002 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO
1003 ,_("SQLite3 Database filename %s")
1004 ,cnt_list[0]->conf.database_dbname);
1005
1006 int thread_safe = sqlite3_threadsafe();
1007 if (thread_safe > 0) {
1008 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, _("SQLite3 is threadsafe"));
1009 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, _("SQLite3 serialized %s")
1010 ,(sqlite3_config(SQLITE_CONFIG_SERIALIZED)?_("FAILED"):_("SUCCESS")));
1011 if (sqlite3_open( cnt_list[0]->conf.database_dbname, &cnt_list[0]->database_sqlite3) != SQLITE_OK) {
1012 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
1013 ,_("Can't open database %s : %s")
1014 ,cnt_list[0]->conf.database_dbname
1015 ,sqlite3_errmsg( cnt_list[0]->database_sqlite3));
1016 sqlite3_close( cnt_list[0]->database_sqlite3);
1017 exit(1);
1018 }
1019 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO,_("database_busy_timeout %d msec"),
1020 cnt_list[0]->conf.database_busy_timeout);
1021 if (sqlite3_busy_timeout( cnt_list[0]->database_sqlite3, cnt_list[0]->conf.database_busy_timeout) != SQLITE_OK)
1022 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO,_("database_busy_timeout failed %s")
1023 ,sqlite3_errmsg( cnt_list[0]->database_sqlite3));
1024 }
1025 }
1026 /* Cascade to all threads */
1027 indx = 1;
1028 while (cnt_list[indx] != NULL) {
1029 cnt_list[indx]->database_sqlite3 = cnt_list[0]->database_sqlite3;
1030 indx++;
1031 }
1032
1033 #endif /* HAVE_SQLITE3 */
1034
1035 }
1036
dbse_init_mysql(struct context * cnt)1037 static int dbse_init_mysql(struct context *cnt){
1038
1039 #if defined(HAVE_MYSQL) || defined(HAVE_MARIADB)
1040 int dbport;
1041 if ((!strcmp(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) {
1042 cnt->database_event_id = 0;
1043 cnt->database = mymalloc(sizeof(MYSQL));
1044 mysql_init(cnt->database);
1045 if ((cnt->conf.database_port < 0) || (cnt->conf.database_port > 65535)){
1046 dbport = 0;
1047 } else {
1048 dbport = cnt->conf.database_port;
1049 }
1050 if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user,
1051 cnt->conf.database_password, cnt->conf.database_dbname, dbport, NULL, 0)) {
1052 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
1053 ,_("Cannot connect to MySQL database %s on host %s with user %s")
1054 ,cnt->conf.database_dbname, cnt->conf.database_host
1055 ,cnt->conf.database_user);
1056 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
1057 ,_("MySQL error was %s"), mysql_error(cnt->database));
1058 return -2;
1059 }
1060 #if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID > 50012)
1061 int my_true = TRUE;
1062 mysql_options(cnt->database, MYSQL_OPT_RECONNECT, &my_true);
1063 #endif
1064 }
1065 #else
1066 (void)cnt; /* Avoid compiler warnings */
1067 #endif /* HAVE_MYSQL HAVE_MARIADB */
1068
1069 return 0;
1070
1071 }
1072
dbse_init_sqlite3(struct context * cnt)1073 static int dbse_init_sqlite3(struct context *cnt){
1074 #ifdef HAVE_SQLITE3
1075 if (cnt_list[0]->database_sqlite3 != 0) {
1076 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO,_("SQLite3 using shared handle"));
1077 cnt->database_sqlite3 = cnt_list[0]->database_sqlite3;
1078
1079 } else if ((!strcmp(cnt->conf.database_type, "sqlite3")) && cnt->conf.database_dbname) {
1080 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO
1081 ,_("SQLite3 Database filename %s"), cnt->conf.database_dbname);
1082 if (sqlite3_open(cnt->conf.database_dbname, &cnt->database_sqlite3) != SQLITE_OK) {
1083 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
1084 ,_("Can't open database %s : %s")
1085 ,cnt->conf.database_dbname, sqlite3_errmsg(cnt->database_sqlite3));
1086 sqlite3_close(cnt->database_sqlite3);
1087 return -2;
1088 }
1089 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO
1090 ,_("database_busy_timeout %d msec"), cnt->conf.database_busy_timeout);
1091 if (sqlite3_busy_timeout(cnt->database_sqlite3, cnt->conf.database_busy_timeout) != SQLITE_OK)
1092 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
1093 ,_("database_busy_timeout failed %s")
1094 ,sqlite3_errmsg(cnt->database_sqlite3));
1095 }
1096 #else
1097 (void)cnt; /* Avoid compiler warnings */
1098 #endif /* HAVE_SQLITE3 */
1099
1100 return 0;
1101
1102 }
1103
dbse_init_pgsql(struct context * cnt)1104 static int dbse_init_pgsql(struct context *cnt){
1105 #ifdef HAVE_PGSQL
1106 if ((!strcmp(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) {
1107 char connstring[255];
1108
1109 /*
1110 * Create the connection string.
1111 * Quote the values so we can have null values (blank)
1112 */
1113 snprintf(connstring, 255,
1114 "dbname='%s' host='%s' user='%s' password='%s' port='%d'",
1115 cnt->conf.database_dbname, /* dbname */
1116 (cnt->conf.database_host ? cnt->conf.database_host : ""), /* host (may be blank) */
1117 (cnt->conf.database_user ? cnt->conf.database_user : ""), /* user (may be blank) */
1118 (cnt->conf.database_password ? cnt->conf.database_password : ""), /* password (may be blank) */
1119 cnt->conf.database_port
1120 );
1121
1122 cnt->database_pg = PQconnectdb(connstring);
1123 if (PQstatus(cnt->database_pg) == CONNECTION_BAD) {
1124 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
1125 ,_("Connection to PostgreSQL database '%s' failed: %s")
1126 ,cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg));
1127 return -2;
1128 }
1129 }
1130 #else
1131 (void)cnt; /* Avoid compiler warnings */
1132 #endif /* HAVE_PGSQL */
1133
1134 return 0;
1135 }
1136
dbse_init(struct context * cnt)1137 static int dbse_init(struct context *cnt){
1138 int retcd = 0;
1139
1140 if (cnt->conf.database_type) {
1141 MOTION_LOG(NTC, TYPE_DB, NO_ERRNO
1142 ,_("Database backend %s"), cnt->conf.database_type);
1143
1144 retcd = dbse_init_mysql(cnt);
1145 if (retcd != 0) return retcd;
1146
1147 retcd = dbse_init_sqlite3(cnt);
1148 if (retcd != 0) return retcd;
1149
1150 retcd = dbse_init_pgsql(cnt);
1151 if (retcd != 0) return retcd;
1152
1153 /* Set the sql mask file according to the SQL config options*/
1154 cnt->sql_mask = cnt->conf.sql_log_picture * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) +
1155 cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT +
1156 cnt->conf.sql_log_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) +
1157 cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE;
1158 }
1159
1160 return retcd;
1161 }
1162
dbse_deinit(struct context * cnt)1163 static void dbse_deinit(struct context *cnt){
1164 if (cnt->conf.database_type) {
1165 #if defined(HAVE_MYSQL) || defined(HAVE_MARIADB)
1166 if ( (!strcmp(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) {
1167 mysql_thread_end();
1168 mysql_close(cnt->database);
1169 cnt->database_event_id = 0;
1170 }
1171 #endif /* HAVE_MYSQL HAVE_MARIADB */
1172
1173 #ifdef HAVE_PGSQL
1174 if ((!strcmp(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) {
1175 PQfinish(cnt->database_pg);
1176 }
1177 #endif /* HAVE_PGSQL */
1178
1179 #ifdef HAVE_SQLITE3
1180 /* Close the SQLite database */
1181 if ((!strcmp(cnt->conf.database_type, "sqlite3")) && (cnt->conf.database_dbname)) {
1182 sqlite3_close(cnt->database_sqlite3);
1183 cnt->database_sqlite3 = NULL;
1184 }
1185 #endif /* HAVE_SQLITE3 */
1186 (void)cnt;
1187 }
1188 }
1189
dbse_sqlmask_update(struct context * cnt)1190 static void dbse_sqlmask_update(struct context *cnt){
1191 /*
1192 * Set the sql mask file according to the SQL config options
1193 * We update it for every frame in case the config was updated
1194 * via remote control.
1195 */
1196 cnt->sql_mask = cnt->conf.sql_log_picture * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) +
1197 cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT +
1198 cnt->conf.sql_log_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) +
1199 cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE;
1200 }
1201
1202 /**
1203 * motion_init
1204 *
1205 * This routine is called from motion_loop (the main thread of the program) to do
1206 * all of the initialization required before starting the actual run.
1207 *
1208 * Parameters:
1209 *
1210 * cnt Pointer to the motion context structure
1211 *
1212 * Returns: 0 OK
1213 * -1 Fatal error, open loopback error
1214 * -2 Fatal error, open SQL database error
1215 * -3 Fatal error, image dimensions are not modulo 8
1216 */
motion_init(struct context * cnt)1217 static int motion_init(struct context *cnt)
1218 {
1219 FILE *picture;
1220 int indx, retcd;
1221
1222 util_threadname_set("ml",cnt->threadnr,cnt->conf.camera_name);
1223
1224 /* Store thread number in TLS. */
1225 pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr));
1226
1227 cnt->currenttime_tm = mymalloc(sizeof(struct tm));
1228 cnt->eventtime_tm = mymalloc(sizeof(struct tm));
1229 /* Init frame time */
1230 cnt->currenttime = time(NULL);
1231 localtime_r(&cnt->currenttime, cnt->currenttime_tm);
1232
1233 cnt->smartmask_speed = 0;
1234
1235 /*
1236 * We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so
1237 * that certain code below does not run until motion has been detected the first time
1238 */
1239 cnt->event_nr = 1;
1240 cnt->prev_event = 0;
1241 cnt->lightswitch_framecounter = 0;
1242 cnt->detecting_motion = 0;
1243 cnt->event_user = FALSE;
1244 cnt->event_stop = FALSE;
1245
1246 /* Make sure to default the high res to zero */
1247 cnt->imgs.width_high = 0;
1248 cnt->imgs.height_high = 0;
1249 cnt->imgs.size_high = 0;
1250 cnt->movie_passthrough = cnt->conf.movie_passthrough;
1251
1252 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
1253 ,_("Camera %d started: motion detection %s"),
1254 cnt->camera_id, cnt->pause ? _("Disabled"):_("Enabled"));
1255
1256 if (!cnt->conf.target_dir)
1257 cnt->conf.target_dir = mystrdup(".");
1258
1259 if (init_camera_type(cnt) != 0 ) return -3;
1260
1261 if ((cnt->camera_type != CAMERA_TYPE_RTSP) &&
1262 (cnt->movie_passthrough)) {
1263 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO,_("Pass-through processing disabled."));
1264 cnt->movie_passthrough = FALSE;
1265 }
1266
1267 if ((cnt->conf.height == 0) || (cnt->conf.width == 0)) {
1268 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
1269 ,_("Invalid configuration dimensions %dx%d"),cnt->conf.height,cnt->conf.width);
1270 cnt->conf.height = DEF_HEIGHT;
1271 cnt->conf.width = DEF_WIDTH;
1272 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
1273 ,_("Using default dimensions %dx%d"),cnt->conf.height,cnt->conf.width);
1274 }
1275 if (cnt->conf.width % 8) {
1276 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1277 ,_("Image width (%d) requested is not modulo 8."), cnt->conf.width);
1278 cnt->conf.width = cnt->conf.width - (cnt->conf.width % 8) + 8;
1279 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1280 ,_("Adjusting width to next higher multiple of 8 (%d)."), cnt->conf.width);
1281 }
1282 if (cnt->conf.height % 8) {
1283 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1284 ,_("Image height (%d) requested is not modulo 8."), cnt->conf.height);
1285 cnt->conf.height = cnt->conf.height - (cnt->conf.height % 8) + 8;
1286 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1287 ,_("Adjusting height to next higher multiple of 8 (%d)."), cnt->conf.height);
1288 }
1289 if (cnt->conf.width < 64) cnt->conf.width = 64;
1290 if (cnt->conf.height < 64) cnt->conf.height = 64;
1291
1292 if (cnt->conf.netcam_decoder != NULL){
1293 cnt->netcam_decoder = mymalloc(strlen(cnt->conf.netcam_decoder)+1);
1294 retcd = snprintf(cnt->netcam_decoder,strlen(cnt->conf.netcam_decoder)+1
1295 ,"%s",cnt->conf.netcam_decoder);
1296 if (retcd < 0){
1297 free(cnt->netcam_decoder);
1298 cnt->netcam_decoder = NULL;
1299 }
1300 } else {
1301 cnt->netcam_decoder = NULL;
1302 }
1303
1304
1305 /* set the device settings */
1306 cnt->video_dev = vid_start(cnt);
1307
1308 /*
1309 * We failed to get an initial image from a camera
1310 * So we need to guess height and width based on the config
1311 * file options.
1312 */
1313 if (cnt->video_dev == -1) {
1314 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
1315 ,_("Could not fetch initial image from camera "));
1316 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
1317 ,_("Motion continues using width and height from config file(s)"));
1318 cnt->imgs.width = cnt->conf.width;
1319 cnt->imgs.height = cnt->conf.height;
1320 cnt->imgs.size_norm = cnt->conf.width * cnt->conf.height * 3 / 2;
1321 cnt->imgs.motionsize = cnt->conf.width * cnt->conf.height;
1322 } else if (cnt->video_dev == -2) {
1323 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1324 ,_("Could not fetch initial image from camera "));
1325 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1326 ,_("Motion only supports width and height modulo 8"));
1327 return -3;
1328 }
1329 /* Revalidate we got a valid image size */
1330 if ((cnt->imgs.width % 8) || (cnt->imgs.height % 8)) {
1331 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1332 ,_("Image width (%d) or height(%d) requested is not modulo 8.")
1333 ,cnt->imgs.width, cnt->imgs.height);
1334 return -3;
1335 }
1336 if ((cnt->imgs.width < 64) || (cnt->imgs.height < 64)){
1337 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1338 ,_("Motion only supports width and height greater than or equal to 64 %dx%d")
1339 ,cnt->imgs.width, cnt->imgs.height);
1340 return -3;
1341 }
1342 /* Substream size notification*/
1343 if ((cnt->imgs.width % 16) || (cnt->imgs.height % 16)) {
1344 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1345 ,_("Substream not available. Image sizes not modulo 16."));
1346 }
1347
1348
1349 /* We set size_high here so that it can be used in the retry function to determine whether
1350 * we need to break and reallocate buffers
1351 */
1352 cnt->imgs.size_high = (cnt->imgs.width_high * cnt->imgs.height_high * 3) / 2;
1353
1354 image_ring_resize(cnt, 1); /* Create a initial precapture ring buffer with 1 frame */
1355
1356 cnt->imgs.ref = mymalloc(cnt->imgs.size_norm);
1357 cnt->imgs.img_motion.image_norm = mymalloc(cnt->imgs.size_norm);
1358
1359 /* contains the moving objects of ref. frame */
1360 cnt->imgs.ref_dyn = mymalloc(cnt->imgs.motionsize * sizeof(*cnt->imgs.ref_dyn));
1361 cnt->imgs.image_virgin.image_norm = mymalloc(cnt->imgs.size_norm);
1362 cnt->imgs.image_vprvcy.image_norm = mymalloc(cnt->imgs.size_norm);
1363 cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize);
1364 cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize);
1365 cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(*cnt->imgs.smartmask_buffer));
1366 cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(*cnt->imgs.labels));
1367 cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(*cnt->imgs.labelsize));
1368 cnt->imgs.preview_image.image_norm = mymalloc(cnt->imgs.size_norm);
1369 cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width * cnt->imgs.height);
1370 if (cnt->imgs.size_high > 0){
1371 cnt->imgs.image_virgin.image_high = mymalloc(cnt->imgs.size_high);
1372 cnt->imgs.preview_image.image_high = mymalloc(cnt->imgs.size_high);
1373 }
1374
1375 mot_stream_init(cnt);
1376
1377 /* Set output picture type */
1378 if (!strcmp(cnt->conf.picture_type, "ppm"))
1379 cnt->imgs.picture_type = IMAGE_TYPE_PPM;
1380 else if (!strcmp(cnt->conf.picture_type, "webp")) {
1381 #ifdef HAVE_WEBP
1382 cnt->imgs.picture_type = IMAGE_TYPE_WEBP;
1383 #else
1384 /* Fallback to jpeg if webp was selected in the config file, but the support for it was not compiled in */
1385 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1386 ,_("webp image format is not available, failing back to jpeg"));
1387 cnt->imgs.picture_type = IMAGE_TYPE_JPEG;
1388 #endif /* HAVE_WEBP */
1389 }
1390 else
1391 cnt->imgs.picture_type = IMAGE_TYPE_JPEG;
1392
1393 /*
1394 * Now is a good time to init rotation data. Since vid_start has been
1395 * called, we know that we have imgs.width and imgs.height. When capturing
1396 * from a V4L device, these are copied from the corresponding conf values
1397 * in vid_start. When capturing from a netcam, they get set in netcam_start,
1398 * which is called from vid_start.
1399 *
1400 * rotate_init will set cap_width and cap_height in cnt->rotate_data.
1401 */
1402 rotate_init(cnt); /* rotate_deinit is called in main */
1403
1404 init_text_scale(cnt); /*Initialize and validate the text_scale */
1405
1406 /* Capture first image, or we will get an alarm on start */
1407 if (cnt->video_dev >= 0) {
1408 int i;
1409
1410 for (i = 0; i < 5; i++) {
1411 if (vid_next(cnt, &cnt->imgs.image_virgin) == 0)
1412 break;
1413 SLEEP(2, 0);
1414 }
1415
1416 if (i >= 5) {
1417 memset(cnt->imgs.image_virgin.image_norm, 0x80, cnt->imgs.size_norm); /* initialize to grey */
1418 draw_text(cnt->imgs.image_virgin.image_norm, cnt->imgs.width, cnt->imgs.height,
1419 10, 20, "Error capturing first image", cnt->text_scale);
1420 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Error capturing first image"));
1421 }
1422 }
1423 cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_in];
1424
1425 /* create a reference frame */
1426 alg_update_reference_frame(cnt, RESET_REF_FRAME);
1427
1428 #if defined(HAVE_V4L2) && !defined(BSD)
1429 /* open video loopback devices if enabled */
1430 if (cnt->conf.video_pipe) {
1431 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
1432 ,_("Opening video loopback device for normal pictures"));
1433
1434 /* vid_startpipe should get the output dimensions */
1435 cnt->pipe = vlp_startpipe(cnt->conf.video_pipe, cnt->imgs.width, cnt->imgs.height);
1436
1437 if (cnt->pipe < 0) {
1438 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1439 ,_("Failed to open video loopback for normal pictures"));
1440 return -1;
1441 }
1442 }
1443
1444 if (cnt->conf.video_pipe_motion) {
1445 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
1446 ,_("Opening video loopback device for motion pictures"));
1447
1448 /* vid_startpipe should get the output dimensions */
1449 cnt->mpipe = vlp_startpipe(cnt->conf.video_pipe_motion, cnt->imgs.width, cnt->imgs.height);
1450
1451 if (cnt->mpipe < 0) {
1452 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1453 ,_("Failed to open video loopback for motion pictures"));
1454 return -1;
1455 }
1456 }
1457 #endif /* HAVE_V4L2 && !BSD */
1458
1459 retcd = dbse_init(cnt);
1460 if (retcd != 0) return retcd;
1461
1462 /* Load the mask file if any */
1463 if (cnt->conf.mask_file) {
1464 if ((picture = myfopen(cnt->conf.mask_file, "r"))) {
1465 /*
1466 * NOTE: The mask is expected to have the output dimensions. I.e., the mask
1467 * applies to the already rotated image, not the capture image. Thus, use
1468 * width and height from imgs.
1469 */
1470 cnt->imgs.mask = get_pgm(picture, cnt->imgs.width, cnt->imgs.height);
1471 myfclose(picture);
1472 } else {
1473 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
1474 ,_("Error opening mask file %s")
1475 ,cnt->conf.mask_file);
1476 /*
1477 * Try to write an empty mask file to make it easier
1478 * for the user to edit it
1479 */
1480 put_fixed_mask(cnt, cnt->conf.mask_file);
1481 }
1482
1483 if (!cnt->imgs.mask) {
1484 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
1485 ,_("Failed to read mask image. Mask feature disabled."));
1486 } else {
1487 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO
1488 ,_("Maskfile \"%s\" loaded.")
1489 ,cnt->conf.mask_file);
1490 }
1491 } else {
1492 cnt->imgs.mask = NULL;
1493 }
1494
1495 init_mask_privacy(cnt);
1496
1497 /* Always initialize smart_mask - someone could turn it on later... */
1498 memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize);
1499 memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize);
1500 memset(cnt->imgs.smartmask_buffer, 0, cnt->imgs.motionsize * sizeof(*cnt->imgs.smartmask_buffer));
1501
1502 /* Set noise level */
1503 cnt->noise = cnt->conf.noise_level;
1504
1505 /* Set threshold value */
1506 cnt->threshold = cnt->conf.threshold;
1507 if (cnt->conf.threshold_maximum > cnt->conf.threshold ){
1508 cnt->threshold_maximum = cnt->conf.threshold_maximum;
1509 } else {
1510 cnt->threshold_maximum = (cnt->imgs.height * cnt->imgs.width * 3) / 2;
1511 }
1512
1513 if (cnt->conf.stream_preview_method == 99){
1514 /* This is the depreciated Stop stream process */
1515
1516 /* Initialize stream server if stream port is specified to not 0 */
1517
1518 if (cnt->conf.stream_port) {
1519 if (stream_init (&(cnt->stream), cnt->conf.stream_port, cnt->conf.stream_localhost,
1520 cnt->conf.webcontrol_ipv6, cnt->conf.stream_cors_header) == -1) {
1521 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
1522 ,_("Problem enabling motion-stream server in port %d")
1523 ,cnt->conf.stream_port);
1524 cnt->conf.stream_port = 0;
1525 cnt->finish = 1;
1526 } else {
1527 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
1528 ,_("Started motion-stream server on port %d (auth %s)")
1529 ,cnt->conf.stream_port
1530 ,cnt->conf.stream_auth_method ? _("Enabled"):_("Disabled"));
1531 }
1532 }
1533
1534 } /* End of legacy stream methods*/
1535
1536
1537 /* Prevent first few frames from triggering motion... */
1538 cnt->moved = 8;
1539
1540 /* Work out expected frame rate based on config setting */
1541 if (cnt->conf.framerate < 2)
1542 cnt->conf.framerate = 2;
1543
1544 /* 2 sec startup delay so FPS is calculated correct */
1545 cnt->startup_frames = (cnt->conf.framerate * 2) + cnt->conf.pre_capture + cnt->conf.minimum_motion_frames;
1546
1547 cnt->required_frame_time = 1000000L / cnt->conf.framerate;
1548
1549 cnt->frame_delay = cnt->required_frame_time;
1550
1551 /*
1552 * Reserve enough space for a 10 second timing history buffer. Note that,
1553 * if there is any problem on the allocation, mymalloc does not return.
1554 */
1555 cnt->rolling_average_data = NULL;
1556 cnt->rolling_average_limit = 10 * cnt->conf.framerate;
1557 cnt->rolling_average_data = mymalloc(sizeof(cnt->rolling_average_data) * cnt->rolling_average_limit);
1558
1559 /* Preset history buffer with expected frame rate */
1560 for (indx = 0; indx < cnt->rolling_average_limit; indx++)
1561 cnt->rolling_average_data[indx] = cnt->required_frame_time;
1562
1563
1564 cnt->track_posx = 0;
1565 cnt->track_posy = 0;
1566 if (cnt->track.type)
1567 cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0);
1568
1569 /* Initialize area detection */
1570 cnt->area_minx[0] = cnt->area_minx[3] = cnt->area_minx[6] = 0;
1571 cnt->area_miny[0] = cnt->area_miny[1] = cnt->area_miny[2] = 0;
1572
1573 cnt->area_minx[1] = cnt->area_minx[4] = cnt->area_minx[7] = cnt->imgs.width / 3;
1574 cnt->area_maxx[0] = cnt->area_maxx[3] = cnt->area_maxx[6] = cnt->imgs.width / 3;
1575
1576 cnt->area_minx[2] = cnt->area_minx[5] = cnt->area_minx[8] = cnt->imgs.width / 3 * 2;
1577 cnt->area_maxx[1] = cnt->area_maxx[4] = cnt->area_maxx[7] = cnt->imgs.width / 3 * 2;
1578
1579 cnt->area_miny[3] = cnt->area_miny[4] = cnt->area_miny[5] = cnt->imgs.height / 3;
1580 cnt->area_maxy[0] = cnt->area_maxy[1] = cnt->area_maxy[2] = cnt->imgs.height / 3;
1581
1582 cnt->area_miny[6] = cnt->area_miny[7] = cnt->area_miny[8] = cnt->imgs.height / 3 * 2;
1583 cnt->area_maxy[3] = cnt->area_maxy[4] = cnt->area_maxy[5] = cnt->imgs.height / 3 * 2;
1584
1585 cnt->area_maxx[2] = cnt->area_maxx[5] = cnt->area_maxx[8] = cnt->imgs.width;
1586 cnt->area_maxy[6] = cnt->area_maxy[7] = cnt->area_maxy[8] = cnt->imgs.height;
1587
1588 cnt->areadetect_eventnbr = 0;
1589
1590 cnt->timenow = 0;
1591 cnt->timebefore = 0;
1592 cnt->rate_limit = 0;
1593 cnt->lastframetime = 0;
1594 cnt->minimum_frame_time_downcounter = cnt->conf.minimum_frame_time;
1595 cnt->get_image = 1;
1596
1597 cnt->olddiffs = 0;
1598 cnt->smartmask_ratio = 0;
1599 cnt->smartmask_count = 20;
1600
1601 cnt->previous_diffs = 0;
1602 cnt->previous_location_x = 0;
1603 cnt->previous_location_y = 0;
1604
1605 cnt->time_last_frame = 1;
1606 cnt->time_current_frame = 0;
1607
1608 cnt->smartmask_lastrate = 0;
1609
1610 cnt->passflag = 0; //only purpose to flag first frame
1611 cnt->rolling_frame = 0;
1612
1613 if (cnt->conf.emulate_motion) {
1614 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Emulating motion"));
1615 }
1616
1617 return 0;
1618 }
1619
1620 /**
1621 * motion_cleanup
1622 *
1623 * This routine is called from motion_loop when thread ends to
1624 * cleanup all memory etc. that motion_init did.
1625 *
1626 * Parameters:
1627 *
1628 * cnt Pointer to the motion context structure
1629 *
1630 * Returns: nothing
1631 */
motion_cleanup(struct context * cnt)1632 static void motion_cleanup(struct context *cnt) {
1633
1634 if (cnt->conf.stream_preview_method == 99){
1635 /* This is the depreciated Stop stream process */
1636 if ((cnt->conf.stream_port) && (cnt->stream.socket != -1))
1637 stream_stop(&cnt->stream);
1638 }
1639
1640 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, NULL);
1641 event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, NULL);
1642
1643 mot_stream_deinit(cnt);
1644
1645 if (cnt->video_dev >= 0) {
1646 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Calling vid_close() from motion_cleanup"));
1647 vid_close(cnt);
1648 }
1649
1650 free(cnt->imgs.img_motion.image_norm);
1651 cnt->imgs.img_motion.image_norm = NULL;
1652
1653 free(cnt->imgs.ref);
1654 cnt->imgs.ref = NULL;
1655
1656 free(cnt->imgs.ref_dyn);
1657 cnt->imgs.ref_dyn = NULL;
1658
1659 free(cnt->imgs.image_virgin.image_norm);
1660 cnt->imgs.image_virgin.image_norm = NULL;
1661
1662 free(cnt->imgs.image_vprvcy.image_norm);
1663 cnt->imgs.image_vprvcy.image_norm = NULL;
1664
1665 free(cnt->imgs.labels);
1666 cnt->imgs.labels = NULL;
1667
1668 free(cnt->imgs.labelsize);
1669 cnt->imgs.labelsize = NULL;
1670
1671 free(cnt->imgs.smartmask);
1672 cnt->imgs.smartmask = NULL;
1673
1674 free(cnt->imgs.smartmask_final);
1675 cnt->imgs.smartmask_final = NULL;
1676
1677 free(cnt->imgs.smartmask_buffer);
1678 cnt->imgs.smartmask_buffer = NULL;
1679
1680 if (cnt->imgs.mask) free(cnt->imgs.mask);
1681 cnt->imgs.mask = NULL;
1682
1683 if (cnt->imgs.mask_privacy) free(cnt->imgs.mask_privacy);
1684 cnt->imgs.mask_privacy = NULL;
1685
1686 if (cnt->imgs.mask_privacy_uv) free(cnt->imgs.mask_privacy_uv);
1687 cnt->imgs.mask_privacy_uv = NULL;
1688
1689 if (cnt->imgs.mask_privacy_high) free(cnt->imgs.mask_privacy_high);
1690 cnt->imgs.mask_privacy_high = NULL;
1691
1692 if (cnt->imgs.mask_privacy_high_uv) free(cnt->imgs.mask_privacy_high_uv);
1693 cnt->imgs.mask_privacy_high_uv = NULL;
1694
1695 free(cnt->imgs.common_buffer);
1696 cnt->imgs.common_buffer = NULL;
1697
1698 free(cnt->imgs.preview_image.image_norm);
1699 cnt->imgs.preview_image.image_norm = NULL;
1700
1701 if (cnt->imgs.size_high > 0){
1702 free(cnt->imgs.image_virgin.image_high);
1703 cnt->imgs.image_virgin.image_high = NULL;
1704
1705 free(cnt->imgs.preview_image.image_high);
1706 cnt->imgs.preview_image.image_high = NULL;
1707 }
1708
1709 image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */
1710
1711 rotate_deinit(cnt); /* cleanup image rotation data */
1712
1713 if (cnt->pipe != -1) {
1714 close(cnt->pipe);
1715 cnt->pipe = -1;
1716 }
1717
1718 if (cnt->mpipe != -1) {
1719 close(cnt->mpipe);
1720 cnt->mpipe = -1;
1721 }
1722
1723 if (cnt->rolling_average_data != NULL) free(cnt->rolling_average_data);
1724
1725
1726 /* Cleanup the current time structure */
1727 free(cnt->currenttime_tm);
1728 cnt->currenttime_tm = NULL;
1729
1730 /* Cleanup the event time structure */
1731 free(cnt->eventtime_tm);
1732 cnt->eventtime_tm = NULL;
1733
1734 dbse_deinit(cnt);
1735
1736 if (cnt->netcam_decoder){
1737 free(cnt->netcam_decoder);
1738 cnt->netcam_decoder = NULL;
1739 }
1740
1741 }
1742
mlp_mask_privacy(struct context * cnt)1743 static void mlp_mask_privacy(struct context *cnt){
1744
1745 if (cnt->imgs.mask_privacy == NULL) return;
1746
1747 /*
1748 * This function uses long operations to process 4 (32 bit) or 8 (64 bit)
1749 * bytes at a time, providing a significant boost in performance.
1750 * Then a trailer loop takes care of any remaining bytes.
1751 */
1752 unsigned char *image;
1753 const unsigned char *mask;
1754 const unsigned char *maskuv;
1755
1756 int index_y;
1757 int index_crcb;
1758 int increment;
1759 int indx_img; /* Counter for how many images we need to apply the mask to */
1760 int indx_max; /* 1 if we are only doing norm, 2 if we are doing both norm and high */
1761
1762 indx_img = 1;
1763 indx_max = 1;
1764 if (cnt->imgs.size_high > 0) indx_max = 2;
1765 increment = sizeof(unsigned long);
1766
1767 while (indx_img <= indx_max){
1768 if (indx_img == 1) {
1769 /* Normal Resolution */
1770 index_y = cnt->imgs.height * cnt->imgs.width;
1771 image = cnt->current_image->image_norm;
1772 mask = cnt->imgs.mask_privacy;
1773 index_crcb = cnt->imgs.size_norm - index_y;
1774 maskuv = cnt->imgs.mask_privacy_uv;
1775 } else {
1776 /* High Resolution */
1777 index_y = cnt->imgs.height_high * cnt->imgs.width_high;
1778 image = cnt->current_image->image_high;
1779 mask = cnt->imgs.mask_privacy_high;
1780 index_crcb = cnt->imgs.size_high - index_y;
1781 maskuv = cnt->imgs.mask_privacy_high_uv;
1782 }
1783
1784 while (index_y >= increment) {
1785 *((unsigned long *)image) &= *((unsigned long *)mask);
1786 image += increment;
1787 mask += increment;
1788 index_y -= increment;
1789 }
1790 while (--index_y >= 0) {
1791 *(image++) &= *(mask++);
1792 }
1793
1794 /* Mask chrominance. */
1795 while (index_crcb >= increment) {
1796 index_crcb -= increment;
1797 /*
1798 * Replace the masked bytes with 0x080. This is done using two masks:
1799 * the normal privacy mask is used to clear the masked bits, the
1800 * "or" privacy mask is used to write 0x80. The benefit of that method
1801 * is that we process 4 or 8 bytes in just two operations.
1802 */
1803 *((unsigned long *)image) &= *((unsigned long *)mask);
1804 mask += increment;
1805 *((unsigned long *)image) |= *((unsigned long *)maskuv);
1806 maskuv += increment;
1807 image += increment;
1808 }
1809
1810 while (--index_crcb >= 0) {
1811 if (*(mask++) == 0x00) *image = 0x80; // Mask last remaining bytes.
1812 image += 1;
1813 }
1814
1815 indx_img++;
1816 }
1817 }
1818
mlp_areadetect(struct context * cnt)1819 static void mlp_areadetect(struct context *cnt){
1820 int i, j, z = 0;
1821 /*
1822 * Simple hack to recognize motion in a specific area
1823 * Do we need a new coversion specifier as well??
1824 */
1825 if ((cnt->conf.area_detect) &&
1826 (cnt->event_nr != cnt->areadetect_eventnbr) &&
1827 (cnt->current_image->flags & IMAGE_TRIGGER)) {
1828 j = strlen(cnt->conf.area_detect);
1829 for (i = 0; i < j; i++) {
1830 z = cnt->conf.area_detect[i] - 49; /* characters are stored as ascii 48-57 (0-9) */
1831 if ((z >= 0) && (z < 9)) {
1832 if (cnt->current_image->location.x > cnt->area_minx[z] &&
1833 cnt->current_image->location.x < cnt->area_maxx[z] &&
1834 cnt->current_image->location.y > cnt->area_miny[z] &&
1835 cnt->current_image->location.y < cnt->area_maxy[z]) {
1836 event(cnt, EVENT_AREA_DETECTED, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
1837 cnt->areadetect_eventnbr = cnt->event_nr; /* Fire script only once per event */
1838 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO
1839 ,_("Motion in area %d detected."), z + 1);
1840 break;
1841 }
1842 }
1843 }
1844 }
1845
1846 }
1847
mlp_prepare(struct context * cnt)1848 static void mlp_prepare(struct context *cnt){
1849
1850 int frame_buffer_size;
1851 struct timeval tv1;
1852
1853 /***** MOTION LOOP - PREPARE FOR NEW FRAME SECTION *****/
1854 cnt->watchdog = WATCHDOG_TMO;
1855
1856 /* Get current time and preserver last time for frame interval calc. */
1857
1858 /* This may be better at the end of the loop or moving the part in
1859 * the end doing elapsed time calc in here
1860 */
1861 cnt->timebefore = cnt->timenow;
1862 gettimeofday(&tv1, NULL);
1863 cnt->timenow = tv1.tv_usec + 1000000L * tv1.tv_sec;
1864
1865 /*
1866 * Calculate detection rate limit. Above 5fps we limit the detection
1867 * rate to 3fps to reduce load at higher framerates.
1868 */
1869 cnt->process_thisframe = 0;
1870 cnt->rate_limit++;
1871 if (cnt->rate_limit >= (cnt->lastrate / 3)) {
1872 cnt->rate_limit = 0;
1873 cnt->process_thisframe = 1;
1874 }
1875
1876 /*
1877 * Since we don't have sanity checks done when options are set,
1878 * this sanity check must go in the main loop :(, before pre_captures
1879 * are attempted.
1880 */
1881 if (cnt->conf.minimum_motion_frames < 1)
1882 cnt->conf.minimum_motion_frames = 1;
1883
1884 if (cnt->conf.pre_capture < 0)
1885 cnt->conf.pre_capture = 0;
1886
1887 /*
1888 * Check if our buffer is still the right size
1889 * If pre_capture or minimum_motion_frames has been changed
1890 * via the http remote control we need to re-size the ring buffer
1891 */
1892 frame_buffer_size = cnt->conf.pre_capture + cnt->conf.minimum_motion_frames;
1893
1894 if (cnt->imgs.image_ring_size != frame_buffer_size)
1895 image_ring_resize(cnt, frame_buffer_size);
1896
1897 /* Get time for current frame */
1898 cnt->currenttime = time(NULL);
1899
1900 /*
1901 * localtime returns static data and is not threadsafe
1902 * so we use localtime_r which is reentrant and threadsafe
1903 */
1904 localtime_r(&cnt->currenttime, cnt->currenttime_tm);
1905
1906 /*
1907 * If we have started on a new second we reset the shots variable
1908 * lastrate is updated to be the number of the last frame. last rate
1909 * is used as the ffmpeg framerate when motion is detected.
1910 */
1911 if (cnt->lastframetime != cnt->currenttime) {
1912 cnt->lastrate = cnt->shots + 1;
1913 cnt->shots = -1;
1914 cnt->lastframetime = cnt->currenttime;
1915
1916 if (cnt->conf.minimum_frame_time) {
1917 cnt->minimum_frame_time_downcounter--;
1918 if (cnt->minimum_frame_time_downcounter == 0)
1919 cnt->get_image = 1;
1920 } else {
1921 cnt->get_image = 1;
1922 }
1923 }
1924
1925
1926 /* Increase the shots variable for each frame captured within this second */
1927 cnt->shots++;
1928
1929 if (cnt->startup_frames > 0)
1930 cnt->startup_frames--;
1931
1932
1933 }
1934
mlp_resetimages(struct context * cnt)1935 static void mlp_resetimages(struct context *cnt){
1936
1937 struct image_data *old_image;
1938
1939 if (cnt->conf.minimum_frame_time) {
1940 cnt->minimum_frame_time_downcounter = cnt->conf.minimum_frame_time;
1941 cnt->get_image = 0;
1942 }
1943
1944 /* ring_buffer_in is pointing to current pos, update before put in a new image */
1945 if (++cnt->imgs.image_ring_in >= cnt->imgs.image_ring_size)
1946 cnt->imgs.image_ring_in = 0;
1947
1948 /* Check if we have filled the ring buffer, throw away last image */
1949 if (cnt->imgs.image_ring_in == cnt->imgs.image_ring_out) {
1950 if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size)
1951 cnt->imgs.image_ring_out = 0;
1952 }
1953
1954 /* cnt->current_image points to position in ring where to store image, diffs etc. */
1955 old_image = cnt->current_image;
1956 cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_in];
1957
1958 /* Init/clear current_image */
1959 if (cnt->process_thisframe) {
1960 /* set diffs to 0 now, will be written after we calculated diffs in new image */
1961 cnt->current_image->diffs = 0;
1962
1963 /* Set flags to 0 */
1964 cnt->current_image->flags = 0;
1965 cnt->current_image->cent_dist = 0;
1966
1967 /* Clear location data */
1968 memset(&cnt->current_image->location, 0, sizeof(cnt->current_image->location));
1969 cnt->current_image->total_labels = 0;
1970 } else if (cnt->current_image && old_image) {
1971 /* not processing this frame: save some important values for next image */
1972 cnt->current_image->diffs = old_image->diffs;
1973 cnt->current_image->timestamp_tv = old_image->timestamp_tv;
1974 cnt->current_image->shot = old_image->shot;
1975 cnt->current_image->cent_dist = old_image->cent_dist;
1976 cnt->current_image->flags = old_image->flags & (~IMAGE_SAVED);
1977 cnt->current_image->location = old_image->location;
1978 cnt->current_image->total_labels = old_image->total_labels;
1979 }
1980
1981 /* Store time with pre_captured image */
1982 gettimeofday(&cnt->current_image->timestamp_tv, NULL);
1983
1984 /* Store shot number with pre_captured image */
1985 cnt->current_image->shot = cnt->shots;
1986
1987 }
1988
mlp_retry(struct context * cnt)1989 static int mlp_retry(struct context *cnt){
1990
1991 /*
1992 * If a camera is not available we keep on retrying every 10 seconds
1993 * until it shows up.
1994 */
1995 int size_high;
1996
1997 if (cnt->video_dev < 0 &&
1998 cnt->currenttime % 10 == 0 && cnt->shots == 0) {
1999 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
2000 ,_("Retrying until successful connection with camera"));
2001 cnt->video_dev = vid_start(cnt);
2002
2003 if (cnt->video_dev < 0) {
2004 return 1;
2005 }
2006
2007 if ((cnt->imgs.width % 8) || (cnt->imgs.height % 8)) {
2008 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
2009 ,_("Image width (%d) or height(%d) requested is not modulo 8.")
2010 ,cnt->imgs.width, cnt->imgs.height);
2011 return 1;
2012 }
2013
2014 if ((cnt->imgs.width < 64) || (cnt->imgs.height < 64)){
2015 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
2016 ,_("Motion only supports width and height greater than or equal to 64 %dx%d")
2017 ,cnt->imgs.width, cnt->imgs.height);
2018 return 1;
2019 }
2020
2021 /*
2022 * If the netcam has different dimensions than in the config file
2023 * we need to restart Motion to re-allocate all the buffers
2024 */
2025 if (cnt->imgs.width != cnt->conf.width || cnt->imgs.height != cnt->conf.height) {
2026 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Camera has finally become available\n"
2027 "Camera image has different width and height"
2028 "from what is in the config file. You should fix that\n"
2029 "Restarting Motion thread to reinitialize all "
2030 "image buffers to new picture dimensions"));
2031 cnt->conf.width = cnt->imgs.width;
2032 cnt->conf.height = cnt->imgs.height;
2033 /*
2034 * Break out of main loop terminating thread
2035 * watchdog will start us again
2036 */
2037 return 1;
2038 }
2039 /*
2040 * For high res, we check the size of buffer to determine whether to break out
2041 * the init_motion function allocated the buffer for high using the cnt->imgs.size_high
2042 * and the vid_start ONLY re-populates the height/width so we can check the size here.
2043 */
2044 size_high = (cnt->imgs.width_high * cnt->imgs.height_high * 3) / 2;
2045 if (cnt->imgs.size_high != size_high) return 1;
2046 }
2047 return 0;
2048 }
2049
mlp_capture(struct context * cnt)2050 static int mlp_capture(struct context *cnt){
2051
2052 const char *tmpin;
2053 char tmpout[80];
2054 int vid_return_code = 0; /* Return code used when calling vid_next */
2055 struct timeval tv1;
2056
2057 /***** MOTION LOOP - IMAGE CAPTURE SECTION *****/
2058 /*
2059 * Fetch next frame from camera
2060 * If vid_next returns 0 all is well and we got a new picture
2061 * Any non zero value is an error.
2062 * 0 = OK, valid picture
2063 * <0 = fatal error - leave the thread by breaking out of the main loop
2064 * >0 = non fatal error - copy last image or show grey image with message
2065 */
2066 if (cnt->video_dev >= 0)
2067 vid_return_code = vid_next(cnt, cnt->current_image);
2068 else
2069 vid_return_code = 1; /* Non fatal error */
2070
2071 // VALID PICTURE
2072 if (vid_return_code == 0) {
2073 cnt->lost_connection = 0;
2074 cnt->connectionlosttime = 0;
2075
2076 /* If all is well reset missing_frame_counter */
2077 if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.framerate) {
2078 /* If we previously logged starting a grey image, now log video re-start */
2079 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Video signal re-acquired"));
2080 // event for re-acquired video signal can be called here
2081 event(cnt, EVENT_CAMERA_FOUND, NULL, NULL, NULL, NULL);
2082 }
2083 cnt->missing_frame_counter = 0;
2084
2085 /*
2086 * Save the newly captured still virgin image to a buffer
2087 * which we will not alter with text and location graphics
2088 */
2089 memcpy(cnt->imgs.image_virgin.image_norm, cnt->current_image->image_norm, cnt->imgs.size_norm);
2090
2091 mlp_mask_privacy(cnt);
2092
2093 memcpy(cnt->imgs.image_vprvcy.image_norm, cnt->current_image->image_norm, cnt->imgs.size_norm);
2094
2095 /*
2096 * If the camera is a netcam we let the camera decide the pace.
2097 * Otherwise we will keep on adding duplicate frames.
2098 * By resetting the timer the framerate becomes maximum the rate
2099 * of the Netcam.
2100 */
2101 if (cnt->conf.netcam_url) {
2102 gettimeofday(&tv1, NULL);
2103 cnt->timenow = tv1.tv_usec + 1000000L * tv1.tv_sec;
2104 }
2105 // FATAL ERROR - leave the thread by breaking out of the main loop
2106 } else if (vid_return_code < 0) {
2107 /* Fatal error - Close video device */
2108 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
2109 ,_("Video device fatal error - Closing video device"));
2110 vid_close(cnt);
2111 /*
2112 * Use virgin image, if we are not able to open it again next loop
2113 * a gray image with message is applied
2114 * flag lost_connection
2115 */
2116 memcpy(cnt->current_image->image_norm, cnt->imgs.image_virgin.image_norm, cnt->imgs.size_norm);
2117 cnt->lost_connection = 1;
2118 /* NO FATAL ERROR -
2119 * copy last image or show grey image with message
2120 * flag on lost_connection if :
2121 * vid_return_code == NETCAM_RESTART_ERROR
2122 * cnt->video_dev < 0
2123 * cnt->missing_frame_counter > (MISSING_FRAMES_TIMEOUT * cnt->conf.framerate)
2124 */
2125 } else {
2126
2127 //MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "vid_return_code %d",vid_return_code);
2128
2129 /*
2130 * Netcams that change dimensions while Motion is running will
2131 * require that Motion restarts to reinitialize all the many
2132 * buffers inside Motion. It will be a mess to try and recover any
2133 * other way
2134 */
2135 if (vid_return_code == NETCAM_RESTART_ERROR) {
2136 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
2137 ,_("Restarting Motion thread to reinitialize all "
2138 "image buffers"));
2139 /*
2140 * Break out of main loop terminating thread
2141 * watchdog will start us again
2142 * Set lost_connection flag on
2143 */
2144 cnt->lost_connection = 1;
2145 return 1;
2146 }
2147
2148 /*
2149 * First missed frame - store timestamp
2150 * Don't reset time when thread restarts
2151 */
2152 if (cnt->connectionlosttime == 0){
2153 cnt->connectionlosttime = cnt->currenttime;
2154 }
2155
2156
2157 /*
2158 * Increase missing_frame_counter
2159 * The first MISSING_FRAMES_TIMEOUT seconds we copy previous virgin image
2160 * After MISSING_FRAMES_TIMEOUT seconds we put a grey error image in the buffer
2161 * If we still have not yet received the initial image from a camera
2162 * we go straight for the grey error image.
2163 */
2164 ++cnt->missing_frame_counter;
2165
2166 if (cnt->video_dev >= 0 &&
2167 cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.framerate)) {
2168 memcpy(cnt->current_image->image_norm, cnt->imgs.image_vprvcy.image_norm, cnt->imgs.size_norm);
2169 } else {
2170 cnt->lost_connection = 1;
2171
2172 if (cnt->video_dev >= 0)
2173 tmpin = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T";
2174 else
2175 tmpin = "UNABLE TO OPEN VIDEO DEVICE\\nSINCE %Y-%m-%d %T";
2176
2177 tv1.tv_sec=cnt->connectionlosttime;
2178 tv1.tv_usec = 0;
2179 memset(cnt->current_image->image_norm, 0x80, cnt->imgs.size_norm);
2180 mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tv1, NULL, 0);
2181 draw_text(cnt->current_image->image_norm, cnt->imgs.width, cnt->imgs.height,
2182 10, 20 * cnt->text_scale, tmpout, cnt->text_scale);
2183
2184 /* Write error message only once */
2185 if (cnt->missing_frame_counter == MISSING_FRAMES_TIMEOUT * cnt->conf.framerate) {
2186 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
2187 ,_("Video signal lost - Adding grey image"));
2188 // Event for lost video signal can be called from here
2189 event(cnt, EVENT_CAMERA_LOST, NULL, NULL, NULL, &tv1);
2190 }
2191
2192 /*
2193 * If we don't get a valid frame for a long time, try to close/reopen device
2194 * Only try this when a device is open
2195 */
2196 if ((cnt->video_dev > 0) &&
2197 (cnt->missing_frame_counter == (MISSING_FRAMES_TIMEOUT * 4) * cnt->conf.framerate)) {
2198 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
2199 ,_("Video signal still lost - "
2200 "Trying to close video device"));
2201 vid_close(cnt);
2202 }
2203 }
2204 }
2205 return 0;
2206
2207 }
2208
mlp_detection(struct context * cnt)2209 static void mlp_detection(struct context *cnt){
2210
2211
2212 /***** MOTION LOOP - MOTION DETECTION SECTION *****/
2213 /*
2214 * The actual motion detection takes place in the following
2215 * diffs is the number of pixels detected as changed
2216 * Make a differences picture in image_out
2217 *
2218 * alg_diff_standard is the slower full feature motion detection algorithm
2219 * alg_diff first calls a fast detection algorithm which only looks at a
2220 * fraction of the pixels. If this detects possible motion alg_diff_standard
2221 * is called.
2222 */
2223 if (cnt->process_thisframe) {
2224 if (cnt->threshold && !cnt->pause) {
2225 /*
2226 * If we've already detected motion and we want to see if there's
2227 * still motion, don't bother trying the fast one first. IF there's
2228 * motion, the alg_diff will trigger alg_diff_standard
2229 * anyway
2230 */
2231 if (cnt->detecting_motion || cnt->conf.setup_mode)
2232 cnt->current_image->diffs = alg_diff_standard(cnt, cnt->imgs.image_vprvcy.image_norm);
2233 else
2234 cnt->current_image->diffs = alg_diff(cnt, cnt->imgs.image_vprvcy.image_norm);
2235
2236 /* Lightswitch feature - has light intensity changed?
2237 * This can happen due to change of light conditions or due to a sudden change of the camera
2238 * sensitivity. If alg_lightswitch detects lightswitch we suspend motion detection the next
2239 * 'lightswitch_frames' frames to allow the camera to settle.
2240 * Don't check if we have lost connection, we detect "Lost signal" frame as lightswitch
2241 */
2242 if (cnt->conf.lightswitch_percent > 1 && !cnt->lost_connection) {
2243 if (alg_lightswitch(cnt, cnt->current_image->diffs)) {
2244 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Lightswitch detected"));
2245
2246 if (cnt->conf.lightswitch_frames < 1)
2247 cnt->conf.lightswitch_frames = 1;
2248 else if (cnt->conf.lightswitch_frames > 1000)
2249 cnt->conf.lightswitch_frames = 1000;
2250
2251 if (cnt->moved < (unsigned int)cnt->conf.lightswitch_frames)
2252 cnt->moved = (unsigned int)cnt->conf.lightswitch_frames;
2253
2254 cnt->current_image->diffs = 0;
2255 alg_update_reference_frame(cnt, RESET_REF_FRAME);
2256 }
2257 }
2258
2259 /*
2260 * Switchfilter feature tries to detect a change in the video signal
2261 * from one camera to the next. This is normally used in the Round
2262 * Robin feature. The algorithm is not very safe.
2263 * The algorithm takes a little time so we only call it when needed
2264 * ie. when feature is enabled and diffs>threshold.
2265 * We do not suspend motion detection like we did for lightswitch
2266 * because with Round Robin this is controlled by roundrobin_skip.
2267 */
2268 if (cnt->conf.roundrobin_switchfilter && cnt->current_image->diffs > cnt->threshold) {
2269 cnt->current_image->diffs = alg_switchfilter(cnt, cnt->current_image->diffs,
2270 cnt->current_image->image_norm);
2271
2272 if ((cnt->current_image->diffs <= cnt->threshold) ||
2273 (cnt->current_image->diffs > cnt->threshold_maximum)) {
2274
2275 cnt->current_image->diffs = 0;
2276 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Switchfilter detected"));
2277 }
2278 }
2279
2280 /*
2281 * Despeckle feature
2282 * First we run (as given by the despeckle_filter option iterations
2283 * of erode and dilate algorithms.
2284 * Finally we run the labelling feature.
2285 * All this is done in the alg_despeckle code.
2286 */
2287 cnt->current_image->total_labels = 0;
2288 cnt->imgs.largest_label = 0;
2289 cnt->olddiffs = 0;
2290
2291 if (cnt->conf.despeckle_filter && cnt->current_image->diffs > 0) {
2292 cnt->olddiffs = cnt->current_image->diffs;
2293 cnt->current_image->diffs = alg_despeckle(cnt, cnt->olddiffs);
2294 } else if (cnt->imgs.labelsize_max) {
2295 cnt->imgs.labelsize_max = 0; /* Disable labeling if enabled */
2296 }
2297
2298 } else if (!cnt->conf.setup_mode) {
2299 cnt->current_image->diffs = 0;
2300 }
2301 }
2302
2303 //TODO: This section needs investigation for purpose, cause and effect
2304 /* Manipulate smart_mask sensitivity (only every smartmask_ratio seconds) */
2305 if ((cnt->smartmask_speed && (cnt->event_nr != cnt->prev_event)) &&
2306 (!--cnt->smartmask_count)) {
2307 alg_tune_smartmask(cnt);
2308 cnt->smartmask_count = cnt->smartmask_ratio;
2309 }
2310
2311 /*
2312 * cnt->moved is set by the tracking code when camera has been asked to move.
2313 * When camera is moving we do not want motion to detect motion or we will
2314 * get our camera chasing itself like crazy and we will get motion detected
2315 * which is not really motion. So we pretend there is no motion by setting
2316 * cnt->diffs = 0.
2317 * We also pretend to have a moving camera when we start Motion and when light
2318 * switch has been detected to allow camera to settle.
2319 */
2320 if (cnt->moved) {
2321 cnt->moved--;
2322 cnt->current_image->diffs = 0;
2323 }
2324
2325 }
2326
mlp_tuning(struct context * cnt)2327 static void mlp_tuning(struct context *cnt){
2328
2329 /***** MOTION LOOP - TUNING SECTION *****/
2330
2331 /*
2332 * If noise tuning was selected, do it now. but only when
2333 * no frames have been recorded and only once per second
2334 */
2335 if ((cnt->conf.noise_tune && cnt->shots == 0) &&
2336 (!cnt->detecting_motion && (cnt->current_image->diffs <= cnt->threshold)))
2337 alg_noise_tune(cnt, cnt->imgs.image_vprvcy.image_norm);
2338
2339
2340 /*
2341 * If we are not noise tuning lets make sure that remote controlled
2342 * changes of noise_level are used.
2343 */
2344 if (cnt->process_thisframe) {
2345 /*
2346 * threshold tuning if enabled
2347 * if we are not threshold tuning lets make sure that remote controlled
2348 * changes of threshold are used.
2349 */
2350 if (cnt->conf.threshold_tune){
2351 alg_threshold_tune(cnt, cnt->current_image->diffs, cnt->detecting_motion);
2352 }
2353
2354 /*
2355 * If motion is detected (cnt->current_image->diffs > cnt->threshold) and before we add text to the pictures
2356 * we find the center and size coordinates of the motion to be used for text overlays and later
2357 * for adding the locate rectangle
2358 */
2359 if ((cnt->current_image->diffs > cnt->threshold) &&
2360 (cnt->current_image->diffs < cnt->threshold_maximum)){
2361
2362 alg_locate_center_size(&cnt->imgs
2363 , cnt->imgs.width
2364 , cnt->imgs.height
2365 , &cnt->current_image->location);
2366 }
2367
2368 /*
2369 * Update reference frame.
2370 * micro-lighswitch: trying to auto-detect lightswitch events.
2371 * frontdoor illumination. Updates are rate-limited to 3 per second at
2372 * framerates above 5fps to save CPU resources and to keep sensitivity
2373 * at a constant level.
2374 */
2375
2376 if ((cnt->current_image->diffs > cnt->threshold) &&
2377 (cnt->current_image->diffs < cnt->threshold_maximum) &&
2378 (cnt->conf.lightswitch_percent >= 1) &&
2379 (cnt->lightswitch_framecounter < (cnt->lastrate * 2)) && /* two seconds window only */
2380 /* number of changed pixels almost the same in two consecutive frames and */
2381 ((abs(cnt->previous_diffs - cnt->current_image->diffs)) < (cnt->previous_diffs / 15)) &&
2382 /* center of motion in about the same place ? */
2383 ((abs(cnt->current_image->location.x - cnt->previous_location_x)) <= (cnt->imgs.width / 150)) &&
2384 ((abs(cnt->current_image->location.y - cnt->previous_location_y)) <= (cnt->imgs.height / 150))) {
2385 alg_update_reference_frame(cnt, RESET_REF_FRAME);
2386 cnt->current_image->diffs = 0;
2387 cnt->lightswitch_framecounter = 0;
2388
2389 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("micro-lightswitch!"));
2390 } else {
2391 alg_update_reference_frame(cnt, UPDATE_REF_FRAME);
2392 }
2393 cnt->previous_diffs = cnt->current_image->diffs;
2394 cnt->previous_location_x = cnt->current_image->location.x;
2395 cnt->previous_location_y = cnt->current_image->location.y;
2396 }
2397
2398
2399 }
2400
mlp_overlay(struct context * cnt)2401 static void mlp_overlay(struct context *cnt){
2402
2403 char tmp[PATH_MAX];
2404
2405 /***** MOTION LOOP - TEXT AND GRAPHICS OVERLAY SECTION *****/
2406 /*
2407 * Some overlays on top of the motion image
2408 * Note that these now modifies the cnt->imgs.out so this buffer
2409 * can no longer be used for motion detection features until next
2410 * picture frame is captured.
2411 */
2412
2413 /* Smartmask overlay */
2414 if (cnt->smartmask_speed &&
2415 (cnt->conf.picture_output_motion || cnt->conf.movie_output_motion ||
2416 cnt->conf.setup_mode || (cnt->stream_motion.cnct_count > 0)))
2417 overlay_smartmask(cnt, cnt->imgs.img_motion.image_norm);
2418
2419 /* Largest labels overlay */
2420 if (cnt->imgs.largest_label && (cnt->conf.picture_output_motion || cnt->conf.movie_output_motion ||
2421 cnt->conf.setup_mode || (cnt->stream_motion.cnct_count > 0)))
2422 overlay_largest_label(cnt, cnt->imgs.img_motion.image_norm);
2423
2424 /* Fixed mask overlay */
2425 if (cnt->imgs.mask && (cnt->conf.picture_output_motion || cnt->conf.movie_output_motion ||
2426 cnt->conf.setup_mode || (cnt->stream_motion.cnct_count > 0)))
2427 overlay_fixed_mask(cnt, cnt->imgs.img_motion.image_norm);
2428
2429 /* Add changed pixels in upper right corner of the pictures */
2430 if (cnt->conf.text_changes) {
2431 if (!cnt->pause)
2432 sprintf(tmp, "%d", cnt->current_image->diffs);
2433 else
2434 sprintf(tmp, "-");
2435
2436 draw_text(cnt->current_image->image_norm, cnt->imgs.width, cnt->imgs.height,
2437 cnt->imgs.width - 10, 10, tmp, cnt->text_scale);
2438 }
2439
2440 /*
2441 * Add changed pixels to motion-images (for stream) in setup_mode
2442 * and always overlay smartmask (not only when motion is detected)
2443 */
2444 if (cnt->conf.setup_mode || (cnt->stream_motion.cnct_count > 0)) {
2445 sprintf(tmp, "D:%5d L:%3d N:%3d", cnt->current_image->diffs,
2446 cnt->current_image->total_labels, cnt->noise);
2447 draw_text(cnt->imgs.img_motion.image_norm, cnt->imgs.width, cnt->imgs.height,
2448 cnt->imgs.width - 10, cnt->imgs.height - (30 * cnt->text_scale),
2449 tmp, cnt->text_scale);
2450 sprintf(tmp, "THREAD %d SETUP", cnt->threadnr);
2451 draw_text(cnt->imgs.img_motion.image_norm, cnt->imgs.width, cnt->imgs.height,
2452 cnt->imgs.width - 10, cnt->imgs.height - (10 * cnt->text_scale),
2453 tmp, cnt->text_scale);
2454 }
2455
2456 /* Add text in lower left corner of the pictures */
2457 if (cnt->conf.text_left) {
2458 mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_left,
2459 &cnt->current_image->timestamp_tv, NULL, 0);
2460 draw_text(cnt->current_image->image_norm, cnt->imgs.width, cnt->imgs.height,
2461 10, cnt->imgs.height - (10 * cnt->text_scale), tmp, cnt->text_scale);
2462 }
2463
2464 /* Add text in lower right corner of the pictures */
2465 if (cnt->conf.text_right) {
2466 mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_right,
2467 &cnt->current_image->timestamp_tv, NULL, 0);
2468 draw_text(cnt->current_image->image_norm, cnt->imgs.width, cnt->imgs.height,
2469 cnt->imgs.width - 10, cnt->imgs.height - (10 * cnt->text_scale),
2470 tmp, cnt->text_scale);
2471 }
2472
2473 }
2474
mlp_actions(struct context * cnt)2475 static void mlp_actions(struct context *cnt){
2476
2477 int indx;
2478
2479 /***** MOTION LOOP - ACTIONS AND EVENT CONTROL SECTION *****/
2480
2481 if ((cnt->current_image->diffs > cnt->threshold) &&
2482 (cnt->current_image->diffs < cnt->threshold_maximum)) {
2483 /* flag this image, it have motion */
2484 cnt->current_image->flags |= IMAGE_MOTION;
2485 cnt->lightswitch_framecounter++; /* micro lightswitch */
2486 } else {
2487 cnt->lightswitch_framecounter = 0;
2488 }
2489
2490 /*
2491 * If motion has been detected we take action and start saving
2492 * pictures and movies etc by calling motion_detected().
2493 * Is emulate_motion enabled we always call motion_detected()
2494 * If post_capture is enabled we also take care of this in the this
2495 * code section.
2496 */
2497 if ((cnt->conf.emulate_motion || cnt->event_user) && (cnt->startup_frames == 0)) {
2498 /* If we were previously detecting motion, started a movie, then got
2499 * no motion then we reset the start movie time so that we do not
2500 * get a pause in the movie.
2501 */
2502 if ( (cnt->detecting_motion == 0) && (cnt->ffmpeg_output != NULL) )
2503 ffmpeg_reset_movie_start_time(cnt->ffmpeg_output, &cnt->current_image->timestamp_tv);
2504 cnt->detecting_motion = 1;
2505 if (cnt->conf.post_capture > 0) {
2506 /* Setup the postcap counter */
2507 cnt->postcap = cnt->conf.post_capture;
2508 // MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "(Em) Init post capture %d", cnt->postcap);
2509 }
2510
2511 cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE);
2512 /* Mark all images in image_ring to be saved */
2513 for (indx = 0; indx < cnt->imgs.image_ring_size; indx++){
2514 cnt->imgs.image_ring[indx].flags |= IMAGE_SAVE;
2515 }
2516
2517 motion_detected(cnt, cnt->video_dev, cnt->current_image);
2518 } else if ((cnt->current_image->flags & IMAGE_MOTION) && (cnt->startup_frames == 0)) {
2519 /*
2520 * Did we detect motion (like the cat just walked in :) )?
2521 * If so, ensure the motion is sustained if minimum_motion_frames
2522 */
2523
2524 /* Count how many frames with motion there is in the last minimum_motion_frames in precap buffer */
2525 int frame_count = 0;
2526 int pos = cnt->imgs.image_ring_in;
2527
2528 for (indx = 0; indx < cnt->conf.minimum_motion_frames; indx++) {
2529 if (cnt->imgs.image_ring[pos].flags & IMAGE_MOTION)
2530 frame_count++;
2531
2532 if (pos == 0)
2533 pos = cnt->imgs.image_ring_size-1;
2534 else
2535 pos--;
2536 }
2537
2538 if (frame_count >= cnt->conf.minimum_motion_frames) {
2539
2540 cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE);
2541 /* If we were previously detecting motion, started a movie, then got
2542 * no motion then we reset the start movie time so that we do not
2543 * get a pause in the movie.
2544 */
2545 if ( (cnt->detecting_motion == 0) && (cnt->ffmpeg_output != NULL) )
2546 ffmpeg_reset_movie_start_time(cnt->ffmpeg_output, &cnt->current_image->timestamp_tv);
2547
2548 cnt->detecting_motion = 1;
2549
2550 /* Setup the postcap counter */
2551 cnt->postcap = cnt->conf.post_capture;
2552 //MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "Setup post capture %d", cnt->postcap);
2553
2554 /* Mark all images in image_ring to be saved */
2555 for (indx = 0; indx < cnt->imgs.image_ring_size; indx++)
2556 cnt->imgs.image_ring[indx].flags |= IMAGE_SAVE;
2557
2558 } else if (cnt->postcap > 0) {
2559 /* we have motion in this frame, but not enought frames for trigger. Check postcap */
2560 cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE);
2561 cnt->postcap--;
2562 //MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "post capture %d", cnt->postcap);
2563 } else {
2564 cnt->current_image->flags |= IMAGE_PRECAP;
2565 }
2566
2567 /* Always call motion_detected when we have a motion image */
2568 motion_detected(cnt, cnt->video_dev, cnt->current_image);
2569 } else if (cnt->postcap > 0) {
2570 /* No motion, doing postcap */
2571 cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE);
2572 cnt->postcap--;
2573 //MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "post capture %d", cnt->postcap);
2574 } else {
2575 /* Done with postcap, so just have the image in the precap buffer */
2576 cnt->current_image->flags |= IMAGE_PRECAP;
2577 /* gapless movie feature */
2578 if ((cnt->conf.event_gap == 0) && (cnt->detecting_motion == 1))
2579 cnt->event_stop = TRUE;
2580 cnt->detecting_motion = 0;
2581 }
2582
2583 /* Update last frame saved time, so we can end event after gap time */
2584 if (cnt->current_image->flags & IMAGE_SAVE)
2585 cnt->lasttime = cnt->current_image->timestamp_tv.tv_sec;
2586
2587
2588 mlp_areadetect(cnt);
2589
2590 /*
2591 * Is the movie too long? Then make movies
2592 * First test for movie_max_time
2593 */
2594 if ((cnt->conf.movie_max_time && cnt->event_nr == cnt->prev_event) &&
2595 (cnt->currenttime - cnt->eventtime >= cnt->conf.movie_max_time))
2596 cnt->event_stop = TRUE;
2597
2598 /*
2599 * Now test for quiet longer than 'gap' OR make movie as decided in
2600 * previous statement.
2601 */
2602 if (((cnt->currenttime - cnt->lasttime >= cnt->conf.event_gap) && cnt->conf.event_gap > 0) ||
2603 cnt->event_stop) {
2604 if (cnt->event_nr == cnt->prev_event || cnt->event_stop) {
2605
2606 /* Flush image buffer */
2607 process_image_ring(cnt, IMAGE_BUFFER_FLUSH);
2608
2609 /* Save preview_shot here at the end of event */
2610 if (cnt->imgs.preview_image.diffs) {
2611 event(cnt, EVENT_IMAGE_PREVIEW, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2612 cnt->imgs.preview_image.diffs = 0;
2613 }
2614
2615 event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2616
2617 /*
2618 * If tracking is enabled we center our camera so it does not
2619 * point to a place where it will miss the next action
2620 */
2621 if (cnt->track.type)
2622 cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0);
2623
2624 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("End of event %d"), cnt->event_nr);
2625
2626 cnt->event_stop = FALSE;
2627 cnt->event_user = FALSE;
2628
2629 /* Reset post capture */
2630 cnt->postcap = 0;
2631
2632 /* Finally we increase the event number */
2633 cnt->event_nr++;
2634 cnt->lightswitch_framecounter = 0;
2635
2636 /*
2637 * And we unset the text_event_string to avoid that buffered
2638 * images get a timestamp from previous event.
2639 */
2640 cnt->text_event_string[0] = '\0';
2641 }
2642 }
2643
2644 /* Save/send to movie some images */
2645 process_image_ring(cnt, 2);
2646
2647
2648 }
2649
mlp_setupmode(struct context * cnt)2650 static void mlp_setupmode(struct context *cnt){
2651 /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/
2652
2653 /* If CAMERA_VERBOSE enabled output some numbers to console */
2654 if (cnt->conf.setup_mode) {
2655 char msg[1024] = "\0";
2656 char part[100];
2657
2658 if (cnt->conf.despeckle_filter) {
2659 snprintf(part, 99, _("Raw changes: %5d - changes after '%s': %5d"),
2660 cnt->olddiffs, cnt->conf.despeckle_filter, cnt->current_image->diffs);
2661 strcat(msg, part);
2662 if (strchr(cnt->conf.despeckle_filter, 'l')) {
2663 snprintf(part, 99,_(" - labels: %3d"), cnt->current_image->total_labels);
2664 strcat(msg, part);
2665 }
2666 } else {
2667 snprintf(part, 99,_("Changes: %5d"), cnt->current_image->diffs);
2668 strcat(msg, part);
2669 }
2670
2671 if (cnt->conf.noise_tune) {
2672 snprintf(part, 99,_(" - noise level: %2d"), cnt->noise);
2673 strcat(msg, part);
2674 }
2675
2676 if (cnt->conf.threshold_tune) {
2677 snprintf(part, 99, _(" - threshold: %d"), cnt->threshold);
2678 strcat(msg, part);
2679 }
2680
2681 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s", msg);
2682 }
2683
2684 }
2685
mlp_snapshot(struct context * cnt)2686 static void mlp_snapshot(struct context *cnt){
2687 /***** MOTION LOOP - SNAPSHOT FEATURE SECTION *****/
2688 /*
2689 * Did we get triggered to make a snapshot from control http? Then shoot a snap
2690 * If snapshot_interval is not zero and time since epoch MOD snapshot_interval = 0 then snap
2691 * We actually allow the time to run over the interval in case we have a delay
2692 * from slow camera.
2693 * Note: Negative value means SIGALRM snaps are enabled
2694 * httpd-control snaps are always enabled.
2695 */
2696
2697 /* time_current_frame is used both for snapshot and timelapse features */
2698 cnt->time_current_frame = cnt->currenttime;
2699
2700 if ((cnt->conf.snapshot_interval > 0 && cnt->shots == 0 &&
2701 cnt->time_current_frame % cnt->conf.snapshot_interval <= cnt->time_last_frame % cnt->conf.snapshot_interval) ||
2702 cnt->snapshot) {
2703 event(cnt, EVENT_IMAGE_SNAPSHOT, cnt->current_image, NULL, NULL, &cnt->current_image->timestamp_tv);
2704 cnt->snapshot = 0;
2705 }
2706
2707 }
2708
mlp_timelapse(struct context * cnt)2709 static void mlp_timelapse(struct context *cnt){
2710
2711 struct tm timestamp_tm;
2712
2713 if (cnt->conf.timelapse_interval) {
2714 localtime_r(&cnt->current_image->timestamp_tv.tv_sec, ×tamp_tm);
2715
2716 /*
2717 * Check to see if we should start a new timelapse file. We start one when
2718 * we are on the first shot, and and the seconds are zero. We must use the seconds
2719 * to prevent the timelapse file from getting reset multiple times during the minute.
2720 */
2721 if (timestamp_tm.tm_min == 0 &&
2722 (cnt->time_current_frame % 60 < cnt->time_last_frame % 60) &&
2723 cnt->shots == 0) {
2724
2725 if (strcasecmp(cnt->conf.timelapse_mode, "manual") == 0) {
2726 ;/* No action */
2727
2728 /* If we are daily, raise timelapseend event at midnight */
2729 } else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) {
2730 if (timestamp_tm.tm_hour == 0)
2731 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2732
2733 /* handle the hourly case */
2734 } else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) {
2735 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2736
2737 /* If we are weekly-sunday, raise timelapseend event at midnight on sunday */
2738 } else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-sunday") == 0) {
2739 if (timestamp_tm.tm_wday == 0 &&
2740 timestamp_tm.tm_hour == 0)
2741 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2742 /* If we are weekly-monday, raise timelapseend event at midnight on monday */
2743 } else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-monday") == 0) {
2744 if (timestamp_tm.tm_wday == 1 &&
2745 timestamp_tm.tm_hour == 0)
2746 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2747 /* If we are monthly, raise timelapseend event at midnight on first day of month */
2748 } else if (strcasecmp(cnt->conf.timelapse_mode, "monthly") == 0) {
2749 if (timestamp_tm.tm_mday == 1 &&
2750 timestamp_tm.tm_hour == 0)
2751 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2752 /* If invalid we report in syslog once and continue in manual mode */
2753 } else {
2754 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
2755 ,_("Invalid timelapse_mode argument '%s'"), cnt->conf.timelapse_mode);
2756 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
2757 ,_("%:s Defaulting to manual timelapse mode"));
2758 conf_cmdparse(&cnt, (char *)"ffmpeg_timelapse_mode",(char *)"manual");
2759 }
2760 }
2761
2762 /*
2763 * If ffmpeg timelapse is enabled and time since epoch MOD ffmpeg_timelaps = 0
2764 * add a timelapse frame to the timelapse movie.
2765 */
2766 if (cnt->shots == 0 && cnt->time_current_frame % cnt->conf.timelapse_interval <=
2767 cnt->time_last_frame % cnt->conf.timelapse_interval) {
2768 event(cnt, EVENT_TIMELAPSE, cnt->current_image, NULL, NULL,
2769 &cnt->current_image->timestamp_tv);
2770 }
2771 } else if (cnt->ffmpeg_timelapse) {
2772 /*
2773 * If timelapse movie is in progress but conf.timelapse_interval is zero then close timelapse file
2774 * This is an important feature that allows manual roll-over of timelapse file using the http
2775 * remote control via a cron job.
2776 */
2777 event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tv);
2778 }
2779
2780 cnt->time_last_frame = cnt->time_current_frame;
2781
2782
2783 }
2784
mlp_loopback(struct context * cnt)2785 static void mlp_loopback(struct context *cnt){
2786 /*
2787 * Feed last image and motion image to video device pipes and the stream clients
2788 * In setup mode we send the special setup mode image to both stream and vloopback pipe
2789 * In normal mode we feed the latest image to vloopback device and we send
2790 * the image to the stream. We always send the first image in a second to the stream.
2791 * Other image are sent only when the config option stream_motion is off
2792 * The result is that with stream_motion on the stream stream is normally at the minimal
2793 * 1 frame per second but the minute motion is detected the motion_detected() function
2794 * sends all detected pictures to the stream except the 1st per second which is already sent.
2795 */
2796 if (cnt->conf.setup_mode) {
2797
2798 event(cnt, EVENT_IMAGE, &cnt->imgs.img_motion, NULL, &cnt->pipe, &cnt->current_image->timestamp_tv);
2799 event(cnt, EVENT_STREAM, &cnt->imgs.img_motion, NULL, NULL, &cnt->current_image->timestamp_tv);
2800 } else {
2801 event(cnt, EVENT_IMAGE, cnt->current_image, NULL,
2802 &cnt->pipe, &cnt->current_image->timestamp_tv);
2803
2804 if (!cnt->conf.stream_motion || cnt->shots == 1)
2805 event(cnt, EVENT_STREAM, cnt->current_image, NULL, NULL,
2806 &cnt->current_image->timestamp_tv);
2807 }
2808
2809 event(cnt, EVENT_IMAGEM, &cnt->imgs.img_motion, NULL, &cnt->mpipe, &cnt->current_image->timestamp_tv);
2810
2811 }
2812
mlp_parmsupdate(struct context * cnt)2813 static void mlp_parmsupdate(struct context *cnt){
2814 /***** MOTION LOOP - ONCE PER SECOND PARAMETER UPDATE SECTION *****/
2815
2816 /* Check for some config parameter changes but only every second */
2817 if (cnt->shots != 0) return;
2818
2819 init_text_scale(cnt); /* Initialize and validate text_scale */
2820
2821 if (strcasecmp(cnt->conf.picture_output, "on") == 0)
2822 cnt->new_img = NEWIMG_ON;
2823 else if (strcasecmp(cnt->conf.picture_output, "first") == 0)
2824 cnt->new_img = NEWIMG_FIRST;
2825 else if (strcasecmp(cnt->conf.picture_output, "best") == 0)
2826 cnt->new_img = NEWIMG_BEST;
2827 else if (strcasecmp(cnt->conf.picture_output, "center") == 0)
2828 cnt->new_img = NEWIMG_CENTER;
2829 else
2830 cnt->new_img = NEWIMG_OFF;
2831
2832 if (strcasecmp(cnt->conf.locate_motion_mode, "on") == 0)
2833 cnt->locate_motion_mode = LOCATE_ON;
2834 else if (strcasecmp(cnt->conf.locate_motion_mode, "preview") == 0)
2835 cnt->locate_motion_mode = LOCATE_PREVIEW;
2836 else
2837 cnt->locate_motion_mode = LOCATE_OFF;
2838
2839 if (strcasecmp(cnt->conf.locate_motion_style, "box") == 0)
2840 cnt->locate_motion_style = LOCATE_BOX;
2841 else if (strcasecmp(cnt->conf.locate_motion_style, "redbox") == 0)
2842 cnt->locate_motion_style = LOCATE_REDBOX;
2843 else if (strcasecmp(cnt->conf.locate_motion_style, "cross") == 0)
2844 cnt->locate_motion_style = LOCATE_CROSS;
2845 else if (strcasecmp(cnt->conf.locate_motion_style, "redcross") == 0)
2846 cnt->locate_motion_style = LOCATE_REDCROSS;
2847 else
2848 cnt->locate_motion_style = LOCATE_BOX;
2849
2850 /* Sanity check for smart_mask_speed, silly value disables smart mask */
2851 if (cnt->conf.smart_mask_speed < 0 || cnt->conf.smart_mask_speed > 10)
2852 cnt->conf.smart_mask_speed = 0;
2853
2854 /* Has someone changed smart_mask_speed or framerate? */
2855 if (cnt->conf.smart_mask_speed != cnt->smartmask_speed ||
2856 cnt->smartmask_lastrate != cnt->lastrate) {
2857 if (cnt->conf.smart_mask_speed == 0) {
2858 memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize);
2859 memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize);
2860 }
2861
2862 cnt->smartmask_lastrate = cnt->lastrate;
2863 cnt->smartmask_speed = cnt->conf.smart_mask_speed;
2864 /*
2865 * Decay delay - based on smart_mask_speed (framerate independent)
2866 * This is always 5*smartmask_speed seconds
2867 */
2868 cnt->smartmask_ratio = 5 * cnt->lastrate * (11 - cnt->smartmask_speed);
2869 }
2870
2871 dbse_sqlmask_update(cnt);
2872
2873 cnt->threshold = cnt->conf.threshold;
2874 if (cnt->conf.threshold_maximum > cnt->conf.threshold ){
2875 cnt->threshold_maximum = cnt->conf.threshold_maximum;
2876 } else {
2877 cnt->threshold_maximum = (cnt->imgs.height * cnt->imgs.width * 3) / 2;
2878 }
2879
2880 if (!cnt->conf.noise_tune){
2881 cnt->noise = cnt->conf.noise_level;
2882 }
2883
2884 }
2885
mlp_frametiming(struct context * cnt)2886 static void mlp_frametiming(struct context *cnt){
2887
2888 int indx;
2889 struct timeval tv2;
2890 unsigned long int elapsedtime; //TODO: Need to evaluate logic for needing this.
2891 long int delay_time_nsec;
2892
2893 /***** MOTION LOOP - FRAMERATE TIMING AND SLEEPING SECTION *****/
2894 /*
2895 * Work out expected frame rate based on config setting which may
2896 * have changed from http-control
2897 */
2898 if (cnt->conf.framerate)
2899 cnt->required_frame_time = 1000000L / cnt->conf.framerate;
2900 else
2901 cnt->required_frame_time = 0;
2902
2903 /* Get latest time to calculate time taken to process video data */
2904 gettimeofday(&tv2, NULL);
2905 elapsedtime = (tv2.tv_usec + 1000000L * tv2.tv_sec) - cnt->timenow;
2906
2907 /*
2908 * Update history buffer but ignore first pass as timebefore
2909 * variable will be inaccurate
2910 */
2911 if (cnt->passflag)
2912 cnt->rolling_average_data[cnt->rolling_frame] = cnt->timenow - cnt->timebefore;
2913 else
2914 cnt->passflag = 1;
2915
2916 cnt->rolling_frame++;
2917 if (cnt->rolling_frame >= cnt->rolling_average_limit)
2918 cnt->rolling_frame = 0;
2919
2920 /* Calculate 10 second average and use deviation in delay calculation */
2921 cnt->rolling_average = 0L;
2922
2923 for (indx = 0; indx < cnt->rolling_average_limit; indx++)
2924 cnt->rolling_average += cnt->rolling_average_data[indx];
2925
2926 cnt->rolling_average /= cnt->rolling_average_limit;
2927 cnt->frame_delay = cnt->required_frame_time - elapsedtime - (cnt->rolling_average - cnt->required_frame_time);
2928
2929 if (cnt->frame_delay > 0) {
2930 /* Apply delay to meet frame time */
2931 if (cnt->frame_delay > cnt->required_frame_time)
2932 cnt->frame_delay = cnt->required_frame_time;
2933
2934 /* Delay time in nanoseconds for SLEEP */
2935 delay_time_nsec = cnt->frame_delay * 1000;
2936
2937 if (delay_time_nsec > 999999999)
2938 delay_time_nsec = 999999999;
2939
2940 /* SLEEP as defined in motion.h A safe sleep using nanosleep */
2941 SLEEP(0, delay_time_nsec);
2942 }
2943
2944 }
2945
2946 /**
2947 * motion_loop
2948 *
2949 * Thread function for the motion handling threads.
2950 *
2951 */
motion_loop(void * arg)2952 static void *motion_loop(void *arg)
2953 {
2954 struct context *cnt = arg;
2955
2956 if (motion_init(cnt) == 0){
2957 while (!cnt->finish || cnt->event_stop) {
2958 mlp_prepare(cnt);
2959 if (cnt->get_image) {
2960 mlp_resetimages(cnt);
2961 if (mlp_retry(cnt) == 1) break;
2962 if (mlp_capture(cnt) == 1) break;
2963 mlp_detection(cnt);
2964 mlp_tuning(cnt);
2965 mlp_overlay(cnt);
2966 mlp_actions(cnt);
2967 mlp_setupmode(cnt);
2968 }
2969 mlp_snapshot(cnt);
2970 mlp_timelapse(cnt);
2971 mlp_loopback(cnt);
2972 mlp_parmsupdate(cnt);
2973 mlp_frametiming(cnt);
2974 }
2975 }
2976
2977 cnt->lost_connection = 1;
2978 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Thread exiting"));
2979
2980 motion_cleanup(cnt);
2981
2982 pthread_mutex_lock(&global_lock);
2983 threads_running--;
2984 pthread_mutex_unlock(&global_lock);
2985
2986 cnt->running = 0;
2987 cnt->finish = 0;
2988
2989 pthread_exit(NULL);
2990 }
2991
2992 /**
2993 * become_daemon
2994 *
2995 * Turns Motion into a daemon through forking. The parent process (i.e. the
2996 * one initially calling this function) will exit inside this function, while
2997 * control will be returned to the child process. Standard input/output are
2998 * released properly, and the current directory is set to / in order to not
2999 * lock up any file system.
3000 *
3001 * Parameters:
3002 *
3003 * cnt - current thread's context struct
3004 *
3005 * Returns: nothing
3006 */
become_daemon(void)3007 static void become_daemon(void)
3008 {
3009 int i;
3010 FILE *pidf = NULL;
3011 struct sigaction sig_ign_action;
3012
3013 /* Setup sig_ign_action */
3014 #ifdef SA_RESTART
3015 sig_ign_action.sa_flags = SA_RESTART;
3016 #else
3017 sig_ign_action.sa_flags = 0;
3018 #endif
3019 sig_ign_action.sa_handler = SIG_IGN;
3020 sigemptyset(&sig_ign_action.sa_mask);
3021
3022 /* fork */
3023 if (fork()) {
3024 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Motion going to daemon mode"));
3025 exit(0);
3026 }
3027
3028 /*
3029 * Create the pid file if defined, if failed exit
3030 * If we fail we report it. If we succeed we postpone the log entry till
3031 * later when we have closed stdout. Otherwise Motion hangs in the terminal waiting
3032 * for an enter.
3033 */
3034 if (cnt_list[0]->conf.pid_file) {
3035 pidf = myfopen(cnt_list[0]->conf.pid_file, "w+");
3036
3037 if (pidf) {
3038 (void)fprintf(pidf, "%d\n", getpid());
3039 myfclose(pidf);
3040 } else {
3041 MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO
3042 ,_("Exit motion, cannot create process"
3043 " id file (pid file) %s"), cnt_list[0]->conf.pid_file);
3044 if (ptr_logfile)
3045 myfclose(ptr_logfile);
3046 exit(0);
3047 }
3048 }
3049
3050 /*
3051 * Changing dir to root enables people to unmount a disk
3052 * without having to stop Motion
3053 */
3054 if (chdir("/"))
3055 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, _("Could not change directory"));
3056
3057
3058 #if (defined(BSD) && !defined(__APPLE__))
3059 setpgrp(0, getpid());
3060 #else
3061 setpgrp();
3062 #endif
3063
3064
3065 if ((i = open("/dev/tty", O_RDWR)) >= 0) {
3066 ioctl(i, TIOCNOTTY, NULL);
3067 close(i);
3068 }
3069
3070 setsid();
3071 i = open("/dev/null", O_RDONLY);
3072
3073 if (i != -1) {
3074 dup2(i, STDIN_FILENO);
3075 close(i);
3076 }
3077
3078 i = open("/dev/null", O_WRONLY);
3079
3080 if (i != -1) {
3081 dup2(i, STDOUT_FILENO);
3082 dup2(i, STDERR_FILENO);
3083 close(i);
3084 }
3085
3086 /* Now it is safe to add the PID creation to the logs */
3087 if (pidf)
3088 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
3089 ,_("Created process id file %s. Process ID is %d")
3090 ,cnt_list[0]->conf.pid_file, getpid());
3091
3092 sigaction(SIGTTOU, &sig_ign_action, NULL);
3093 sigaction(SIGTTIN, &sig_ign_action, NULL);
3094 sigaction(SIGTSTP, &sig_ign_action, NULL);
3095 }
3096
cntlist_create(int argc,char * argv[])3097 static void cntlist_create(int argc, char *argv[]){
3098 /*
3099 * cnt_list is an array of pointers to the context structures cnt for each thread.
3100 * First we reserve room for a pointer to thread 0's context structure
3101 * and a NULL pointer which indicates that end of the array of pointers to
3102 * thread context structures.
3103 */
3104 cnt_list = mymalloc(sizeof(struct context *) * 2);
3105
3106 /* Now we reserve room for thread 0's context structure and let cnt_list[0] point to it */
3107 cnt_list[0] = mymalloc(sizeof(struct context));
3108
3109 /* Populate context structure with start/default values */
3110 context_init(cnt_list[0]);
3111
3112 /* Initialize some static and global string variables */
3113 gethostname (cnt_list[0]->hostname, PATH_MAX);
3114 cnt_list[0]->hostname[PATH_MAX-1] = '\0';
3115 /* end of variables */
3116
3117 /* cnt_list[1] pointing to zero indicates no more thread context structures - they get added later */
3118 cnt_list[1] = NULL;
3119
3120 /*
3121 * Command line arguments are being pointed to from cnt_list[0] and we call conf_load which loads
3122 * the config options from motion.conf, thread config files and the command line.
3123 */
3124 cnt_list[0]->conf.argv = argv;
3125 cnt_list[0]->conf.argc = argc;
3126 cnt_list = conf_load(cnt_list);
3127 }
3128
motion_shutdown(void)3129 static void motion_shutdown(void){
3130 int i = -1;
3131
3132 motion_remove_pid();
3133
3134 webu_stop(cnt_list);
3135
3136 while (cnt_list[++i])
3137 context_destroy(cnt_list[i]);
3138
3139 free(cnt_list);
3140 cnt_list = NULL;
3141
3142 vid_mutex_destroy();
3143 }
3144
motion_camera_ids(void)3145 static void motion_camera_ids(void){
3146 /* Set the camera id's on the context. They must be unique */
3147 int indx, indx2, invalid_ids;
3148
3149 /* Set defaults */
3150 indx = 0;
3151 while (cnt_list[indx] != NULL){
3152 if (cnt_list[indx]->conf.camera_id > 0){
3153 cnt_list[indx]->camera_id = cnt_list[indx]->conf.camera_id;
3154 } else {
3155 cnt_list[indx]->camera_id = indx;
3156 }
3157 indx++;
3158 }
3159
3160 invalid_ids = FALSE;
3161 indx = 0;
3162 while (cnt_list[indx] != NULL){
3163 if (cnt_list[indx]->camera_id > 32000) invalid_ids = TRUE;
3164 indx2 = indx + 1;
3165 while (cnt_list[indx2] != NULL){
3166 if (cnt_list[indx]->camera_id == cnt_list[indx2]->camera_id) invalid_ids = TRUE;
3167
3168 indx2++;
3169 }
3170 indx++;
3171 }
3172 if (invalid_ids){
3173 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
3174 ,_("Camara IDs are not unique or have values over 32,000. Falling back to thread numbers"));
3175 indx = 0;
3176 while (cnt_list[indx] != NULL){
3177 cnt_list[indx]->camera_id = indx;
3178 indx++;
3179 }
3180 }
3181 }
3182
motion_ntc(void)3183 static void motion_ntc(void){
3184
3185 #ifdef HAVE_V4L2
3186 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("v4l2 : available"));
3187 #else
3188 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("v4l2 : not available"));
3189 #endif
3190
3191 #ifdef HAVE_BKTR
3192 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("bktr : available"));
3193 #else
3194 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("bktr : not available"));
3195 #endif
3196
3197 #ifdef HAVE_WEBP
3198 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("webp : available"));
3199 #else
3200 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("webp : not available"));
3201 #endif
3202
3203 #ifdef HAVE_MMAL
3204 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("mmal : available"));
3205 #else
3206 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("mmal : not available"));
3207 #endif
3208
3209 #ifdef HAVE_FFMPEG
3210 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("ffmpeg : available"));
3211 #else
3212 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("ffmpeg : not available"));
3213 #endif
3214
3215 #ifdef HAVE_MYSQL
3216 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("mysql : available"));
3217 #else
3218 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("mysql : not available"));
3219 #endif
3220
3221 #ifdef HAVE_MARIADB
3222 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("MariaDB: available"));
3223 #else
3224 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("MariaDB: not available"));
3225 #endif
3226
3227 #ifdef HAVE_SQLITE3
3228 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("sqlite3: available"));
3229 #else
3230 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("sqlite3: not available"));
3231 #endif
3232
3233 #ifdef HAVE_PGSQL
3234 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("pgsql : available"));
3235 #else
3236 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("pgsql : not available"));
3237 #endif
3238
3239 #ifdef HAVE_GETTEXT
3240 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("nls : available"));
3241 #else
3242 MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("nls : not available"));
3243 #endif
3244
3245
3246 }
3247
3248
3249 /**
3250 * motion_startup
3251 *
3252 * Responsible for initializing stuff when Motion starts up or is restarted,
3253 * including daemon initialization and creating the context struct list.
3254 *
3255 * Parameters:
3256 *
3257 * daemonize - non-zero to do daemon init (if the config parameters says so),
3258 * or 0 to skip it
3259 * argc - size of argv
3260 * argv - command-line options, passed initially from 'main'
3261 *
3262 * Returns: nothing
3263 */
motion_startup(int daemonize,int argc,char * argv[])3264 static void motion_startup(int daemonize, int argc, char *argv[])
3265 {
3266 /* Initialize our global mutex */
3267 pthread_mutex_init(&global_lock, NULL);
3268
3269 /*
3270 * Create the list of context structures and load the
3271 * configuration.
3272 */
3273 cntlist_create(argc, argv);
3274
3275 if ((cnt_list[0]->conf.log_level > ALL) ||
3276 (cnt_list[0]->conf.log_level == 0)) {
3277 cnt_list[0]->conf.log_level = LEVEL_DEFAULT;
3278 cnt_list[0]->log_level = cnt_list[0]->conf.log_level;
3279 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
3280 ,_("Using default log level (%s) (%d)")
3281 ,get_log_level_str(cnt_list[0]->log_level)
3282 ,SHOW_LEVEL_VALUE(cnt_list[0]->log_level));
3283 } else {
3284 cnt_list[0]->log_level = cnt_list[0]->conf.log_level - 1; // Let's make syslog compatible
3285 }
3286
3287
3288 if ((cnt_list[0]->conf.log_file) && (strncmp(cnt_list[0]->conf.log_file, "syslog", 6))) {
3289 set_log_mode(LOGMODE_FILE);
3290 ptr_logfile = set_logfile(cnt_list[0]->conf.log_file);
3291
3292 if (ptr_logfile) {
3293 set_log_mode(LOGMODE_SYSLOG);
3294 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
3295 ,_("Logging to file (%s)"),cnt_list[0]->conf.log_file);
3296 set_log_mode(LOGMODE_FILE);
3297 } else {
3298 MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO
3299 ,_("Exit motion, cannot create log file %s")
3300 ,cnt_list[0]->conf.log_file);
3301 exit(0);
3302 }
3303 } else {
3304 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Logging to syslog"));
3305 }
3306
3307 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Motion %s Started"),VERSION);
3308
3309 if ((cnt_list[0]->conf.log_type == NULL) ||
3310 !(cnt_list[0]->log_type = get_log_type(cnt_list[0]->conf.log_type))) {
3311 cnt_list[0]->log_type = TYPE_DEFAULT;
3312 cnt_list[0]->conf.log_type = mystrcpy(cnt_list[0]->conf.log_type, "ALL");
3313 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO,_("Using default log type (%s)"),
3314 get_log_type_str(cnt_list[0]->log_type));
3315 }
3316
3317 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Using log type (%s) log level (%s)"),
3318 get_log_type_str(cnt_list[0]->log_type), get_log_level_str(cnt_list[0]->log_level));
3319
3320 set_log_level(cnt_list[0]->log_level);
3321 set_log_type(cnt_list[0]->log_type);
3322
3323
3324 if (daemonize) {
3325 /*
3326 * If daemon mode is requested, and we're not going into setup mode,
3327 * become daemon.
3328 */
3329 if (cnt_list[0]->daemon && cnt_list[0]->conf.setup_mode == 0) {
3330 become_daemon();
3331 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Motion running as daemon process"));
3332 }
3333 }
3334
3335 if (cnt_list[0]->conf.setup_mode)
3336 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO,_("Motion running in setup mode."));
3337
3338 conf_output_parms(cnt_list);
3339
3340 motion_ntc();
3341
3342 motion_camera_ids();
3343
3344 initialize_chars();
3345
3346 webu_start(cnt_list);
3347
3348 vid_mutex_init();
3349
3350 }
3351
3352 /**
3353 * motion_start_thread
3354 *
3355 * Called from main when start a motion thread
3356 *
3357 * Parameters:
3358 *
3359 * cnt - Thread context pointer
3360 * thread_attr - pointer to thread attributes
3361 *
3362 * Returns: nothing
3363 */
motion_start_thread(struct context * cnt)3364 static void motion_start_thread(struct context *cnt){
3365 int i;
3366 char service[6];
3367 pthread_attr_t thread_attr;
3368
3369 if (strcmp(cnt->conf_filename, "")){
3370 cnt->conf_filename[sizeof(cnt->conf_filename) - 1] = '\0';
3371 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Camera ID: %d is from %s")
3372 ,cnt->camera_id, cnt->conf_filename);
3373 }
3374
3375 if (cnt->conf.netcam_url){
3376 snprintf(service,6,"%s",cnt->conf.netcam_url);
3377 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO,_("Camera ID: %d Camera Name: %s Service: %s")
3378 ,cnt->camera_id, cnt->conf.camera_name,service);
3379 } else {
3380 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO,_("Camera ID: %d Camera Name: %s Device: %s")
3381 ,cnt->camera_id, cnt->conf.camera_name,cnt->conf.video_device);
3382 }
3383
3384 /*
3385 * Check the stream port number for conflicts.
3386 * First we check for conflict with the control port.
3387 * Second we check for that two threads does not use the same port number
3388 * for the stream. If a duplicate port is found the stream feature gets disabled (port = 0)
3389 * for this thread and a warning is written to console and syslog.
3390 */
3391
3392 if (cnt->conf.stream_port != 0) {
3393 /* Compare against the control port. */
3394 if (cnt_list[0]->conf.webcontrol_port == cnt->conf.stream_port) {
3395 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
3396 ,_("Stream port number %d for thread %d conflicts with the control port")
3397 ,cnt->conf.stream_port, cnt->threadnr);
3398 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
3399 ,_("Stream feature for thread %d is disabled.")
3400 ,cnt->threadnr);
3401 cnt->conf.stream_port = 0;
3402 }
3403 /* Compare against stream ports of other threads. */
3404 for (i = 1; cnt_list[i]; i++) {
3405 if (cnt_list[i] == cnt) continue;
3406
3407 if (cnt_list[i]->conf.stream_port == cnt->conf.stream_port) {
3408 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
3409 ,_("Stream port number %d for thread %d conflicts with thread %d")
3410 ,cnt->conf.stream_port, cnt->threadnr, cnt_list[i]->threadnr);
3411 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
3412 ,_("Stream feature for thread %d is disabled.")
3413 ,cnt->threadnr);
3414 cnt->conf.stream_port = 0;
3415 }
3416 }
3417 }
3418
3419 /*
3420 * Update how many threads we have running. This is done within a
3421 * mutex lock to prevent multiple simultaneous updates to
3422 * 'threads_running'.
3423 */
3424 pthread_mutex_lock(&global_lock);
3425 threads_running++;
3426 pthread_mutex_unlock(&global_lock);
3427
3428 /* Set a flag that we want this thread running */
3429 cnt->restart = 1;
3430
3431 /* Give the thread WATCHDOG_TMO to start */
3432 cnt->watchdog = WATCHDOG_TMO;
3433
3434 /* Flag it as running outside of the thread, otherwise if the main loop
3435 * checked if it is was running before the thread set it to 1, it would
3436 * start another thread for this device. */
3437 cnt->running = 1;
3438
3439 pthread_attr_init(&thread_attr);
3440 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
3441
3442 if (pthread_create(&cnt->thread_id, &thread_attr, &motion_loop, cnt)) {
3443 /* thread create failed, undo running state */
3444 cnt->running = 0;
3445 pthread_mutex_lock(&global_lock);
3446 threads_running--;
3447 pthread_mutex_unlock(&global_lock);
3448 }
3449 pthread_attr_destroy(&thread_attr);
3450
3451 }
3452
motion_restart(int argc,char ** argv)3453 static void motion_restart(int argc, char **argv){
3454 /*
3455 * Handle the restart situation. Currently the approach is to
3456 * cleanup everything, and then initialize everything again
3457 * (including re-reading the config file(s)).
3458 */
3459 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO,_("Restarting motion."));
3460 motion_shutdown();
3461
3462 SLEEP(2, 0);
3463
3464 motion_startup(0, argc, argv); /* 0 = skip daemon init */
3465 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO,_("Motion restarted"));
3466
3467 restart = 0;
3468 }
3469
motion_watchdog(int indx)3470 static void motion_watchdog(int indx){
3471
3472 /* Notes:
3473 * To test scenarios, just double lock a mutex in a spawned thread.
3474 * We use detached threads because pthread_join would lock the main thread
3475 * If we only call the first pthread_cancel when we reach the watchdog_kill
3476 * it does not break us out of the mutex lock.
3477 * We keep sending VTAlarms so the pthread_cancel queued can be caught.
3478 * The calls to pthread_kill 'may' not work or cause crashes
3479 * The cancel could finish and then the pthread_kill could be called
3480 * on the invalid thread_id which could cause undefined results
3481 * Even if the cancel finishes it is not clean since memory is not cleaned.
3482 * The other option instead of cancel would be to exit(1) and terminate everything
3483 * Best to just not get into a watchdog situation...
3484 */
3485
3486 if (!cnt_list[indx]->running) return;
3487
3488 cnt_list[indx]->watchdog--;
3489 if (cnt_list[indx]->watchdog == 0) {
3490 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
3491 ,_("Thread %d - Watchdog timeout. Trying to do a graceful restart")
3492 , cnt_list[indx]->threadnr);
3493 cnt_list[indx]->event_stop = TRUE; /* Trigger end of event */
3494 cnt_list[indx]->finish = 1;
3495 }
3496
3497 if (cnt_list[indx]->watchdog == WATCHDOG_KILL) {
3498 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
3499 ,_("Thread %d - Watchdog timeout did NOT restart, killing it!")
3500 , cnt_list[indx]->threadnr);
3501 if ((cnt_list[indx]->camera_type == CAMERA_TYPE_RTSP) &&
3502 (cnt_list[indx]->rtsp != NULL)){
3503 pthread_cancel(cnt_list[indx]->rtsp->thread_id);
3504 }
3505 if ((cnt_list[indx]->camera_type == CAMERA_TYPE_RTSP) &&
3506 (cnt_list[indx]->rtsp_high != NULL)){
3507 pthread_cancel(cnt_list[indx]->rtsp_high->thread_id);
3508 }
3509 if ((cnt_list[indx]->camera_type == CAMERA_TYPE_NETCAM) &&
3510 (cnt_list[indx]->netcam != NULL)){
3511 pthread_cancel(cnt_list[indx]->netcam->thread_id);
3512 }
3513 pthread_cancel(cnt_list[indx]->thread_id);
3514 }
3515
3516 if (cnt_list[indx]->watchdog < WATCHDOG_KILL) {
3517 if ((cnt_list[indx]->camera_type == CAMERA_TYPE_NETCAM) &&
3518 (cnt_list[indx]->rtsp != NULL)){
3519 if (!cnt_list[indx]->rtsp->handler_finished &&
3520 pthread_kill(cnt_list[indx]->rtsp->thread_id, 0) == ESRCH) {
3521 cnt_list[indx]->rtsp->handler_finished = TRUE;
3522 pthread_mutex_lock(&global_lock);
3523 threads_running--;
3524 pthread_mutex_unlock(&global_lock);
3525 netcam_rtsp_cleanup(cnt_list[indx],FALSE);
3526 } else {
3527 pthread_kill(cnt_list[indx]->rtsp->thread_id, SIGVTALRM);
3528 }
3529 }
3530 if ((cnt_list[indx]->camera_type == CAMERA_TYPE_NETCAM) &&
3531 (cnt_list[indx]->rtsp_high != NULL)){
3532 if (!cnt_list[indx]->rtsp_high->handler_finished &&
3533 pthread_kill(cnt_list[indx]->rtsp_high->thread_id, 0) == ESRCH) {
3534 cnt_list[indx]->rtsp_high->handler_finished = TRUE;
3535 pthread_mutex_lock(&global_lock);
3536 threads_running--;
3537 pthread_mutex_unlock(&global_lock);
3538 netcam_rtsp_cleanup(cnt_list[indx],FALSE);
3539 } else {
3540 pthread_kill(cnt_list[indx]->rtsp_high->thread_id, SIGVTALRM);
3541 }
3542 }
3543 if ((cnt_list[indx]->camera_type == CAMERA_TYPE_NETCAM) &&
3544 (cnt_list[indx]->netcam != NULL)){
3545 if (!cnt_list[indx]->netcam->handler_finished &&
3546 pthread_kill(cnt_list[indx]->netcam->thread_id, 0) == ESRCH) {
3547 pthread_mutex_lock(&global_lock);
3548 threads_running--;
3549 pthread_mutex_unlock(&global_lock);
3550 cnt_list[indx]->netcam->handler_finished = TRUE;
3551 cnt_list[indx]->netcam->finish = FALSE;
3552 } else {
3553 pthread_kill(cnt_list[indx]->netcam->thread_id, SIGVTALRM);
3554 }
3555 }
3556 if (cnt_list[indx]->running &&
3557 pthread_kill(cnt_list[indx]->thread_id, 0) == ESRCH){
3558 MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO
3559 ,_("Thread %d - Cleaning thread.")
3560 , cnt_list[indx]->threadnr);
3561 pthread_mutex_lock(&global_lock);
3562 threads_running--;
3563 pthread_mutex_unlock(&global_lock);
3564 motion_cleanup(cnt_list[indx]);
3565 cnt_list[indx]->running = 0;
3566 cnt_list[indx]->finish = 0;
3567 } else {
3568 pthread_kill(cnt_list[indx]->thread_id,SIGVTALRM);
3569 }
3570 }
3571 }
3572
motion_check_threadcount(void)3573 static int motion_check_threadcount(void){
3574 /* Return 1 if we should break out of loop */
3575
3576 /* It has been observed that this is not counting every
3577 * thread running. The netcams spawn handler threads which are not
3578 * counted here. This is only counting context threads and when they
3579 * all get to zero, then we are done.
3580 */
3581
3582 int motion_threads_running, indx;
3583
3584 motion_threads_running = 0;
3585
3586 for (indx = (cnt_list[1] != NULL ? 1 : 0); cnt_list[indx]; indx++) {
3587 if (cnt_list[indx]->running || cnt_list[indx]->restart)
3588 motion_threads_running++;
3589 }
3590
3591 /* If the web control/streams are in finish/shutdown, we
3592 * do not want to count them. They will be completely closed
3593 * by the process outside of loop that is checking the counts
3594 * of threads. If the webcontrol is not in a finish / shutdown
3595 * then we want to keep them in the tread count to allow user
3596 * to restart the cameras and keep Motion running.
3597 */
3598 indx = 0;
3599 while (cnt_list[indx] != NULL){
3600 if ((cnt_list[indx]->webcontrol_finish == FALSE) &&
3601 ((cnt_list[indx]->webcontrol_daemon != NULL) ||
3602 (cnt_list[indx]->webstream_daemon != NULL))) {
3603 motion_threads_running++;
3604 }
3605 indx++;
3606 }
3607
3608
3609 if (((motion_threads_running == 0) && finish) ||
3610 ((motion_threads_running == 0) && (threads_running == 0))) {
3611 MOTION_LOG(ALL, TYPE_ALL, NO_ERRNO
3612 ,_("DEBUG-1 threads_running %d motion_threads_running %d , finish %d")
3613 ,threads_running, motion_threads_running, finish);
3614 return 1;
3615 } else {
3616 return 0;
3617 }
3618 }
3619
3620 /**
3621 * main
3622 *
3623 * Main entry point of Motion. Launches all the motion threads and contains
3624 * the logic for starting up, restarting and cleaning up everything.
3625 *
3626 * Parameters:
3627 *
3628 * argc - size of argv
3629 * argv - command-line options
3630 *
3631 * Returns: Motion exit status = 0 always
3632 */
main(int argc,char ** argv)3633 int main (int argc, char **argv)
3634 {
3635 int i;
3636
3637 /* Create the TLS key for thread number. */
3638 pthread_key_create(&tls_key_threadnr, NULL);
3639 pthread_setspecific(tls_key_threadnr, (void *)(0));
3640
3641 setup_signals();
3642
3643 motion_startup(1, argc, argv);
3644
3645 ffmpeg_global_init();
3646
3647 dbse_global_init();
3648
3649 translate_init();
3650
3651 do {
3652 if (restart) motion_restart(argc, argv);
3653
3654 for (i = cnt_list[1] != NULL ? 1 : 0; cnt_list[i]; i++) {
3655 cnt_list[i]->threadnr = i ? i : 1;
3656 motion_start_thread(cnt_list[i]);
3657 }
3658
3659 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
3660 ,_("Waiting for threads to finish, pid: %d"), getpid());
3661
3662 while (1) {
3663 SLEEP(1, 0);
3664 if (motion_check_threadcount()) break;
3665
3666 for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) {
3667 /* Check if threads wants to be restarted */
3668 if ((!cnt_list[i]->running) && (cnt_list[i]->restart)) {
3669 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
3670 ,_("Motion thread %d restart"), cnt_list[i]->threadnr);
3671 motion_start_thread(cnt_list[i]);
3672 }
3673 motion_watchdog(i);
3674 }
3675 }
3676
3677 /* Reset end main loop flag */
3678 finish = 0;
3679
3680 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Threads finished"));
3681
3682 /* Rest for a while if we're supposed to restart. */
3683 if (restart) SLEEP(1, 0);
3684
3685 } while (restart); /* loop if we're supposed to restart */
3686
3687
3688 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Motion terminating"));
3689
3690 ffmpeg_global_deinit();
3691
3692 dbse_global_deinit();
3693
3694 motion_shutdown();
3695
3696 /* Perform final cleanup. */
3697 pthread_key_delete(tls_key_threadnr);
3698 pthread_mutex_destroy(&global_lock);
3699
3700 return 0;
3701 }
3702
3703 /**
3704 * mymalloc
3705 *
3706 * Allocates some memory and checks if that succeeded or not. If it failed,
3707 * do some errorlogging and bail out.
3708 *
3709 * NOTE: Kenneth Lavrsen changed printing of size_t types so instead of using
3710 * conversion specifier %zd I changed it to %llu and casted the size_t
3711 * variable to unsigned long long. The reason for this nonsense is that older
3712 * versions of gcc like 2.95 uses %Zd and does not understand %zd. So to avoid
3713 * this mess I used a more generic way. Long long should have enough bits for
3714 * 64-bit machines with large memory areas.
3715 *
3716 * Parameters:
3717 *
3718 * nbytes - no. of bytes to allocate
3719 *
3720 * Returns: a pointer to the allocated memory
3721 */
mymalloc(size_t nbytes)3722 void * mymalloc(size_t nbytes)
3723 {
3724 void *dummy = calloc(nbytes, 1);
3725
3726 if (!dummy) {
3727 MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO, _("Could not allocate %llu bytes of memory!")
3728 ,(unsigned long long)nbytes);
3729 motion_remove_pid();
3730 exit(1);
3731 }
3732
3733 return dummy;
3734 }
3735
3736 /**
3737 * myrealloc
3738 *
3739 * Re-allocate (i.e., resize) some memory and check if that succeeded or not.
3740 * If it failed, do some errorlogging and bail out. If the new memory size
3741 * is 0, the memory is freed.
3742 *
3743 * Parameters:
3744 *
3745 * ptr - pointer to the memory to resize/reallocate
3746 * size - new memory size
3747 * desc - name of the calling function
3748 *
3749 * Returns: a pointer to the reallocated memory, or NULL if the memory was
3750 * freed
3751 */
myrealloc(void * ptr,size_t size,const char * desc)3752 void *myrealloc(void *ptr, size_t size, const char *desc)
3753 {
3754 void *dummy = NULL;
3755
3756 if (size == 0) {
3757 free(ptr);
3758 MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
3759 ,_("Warning! Function %s tries to resize memoryblock at %p to 0 bytes!")
3760 ,desc, ptr);
3761 } else {
3762 dummy = realloc(ptr, size);
3763 if (!dummy) {
3764 MOTION_LOG(EMG, TYPE_ALL, NO_ERRNO
3765 ,_("Could not resize memory-block at offset %p to %llu bytes (function %s)!")
3766 ,ptr, (unsigned long long)size, desc);
3767 motion_remove_pid();
3768 exit(1);
3769 }
3770 }
3771
3772 return dummy;
3773 }
3774
3775
3776 /**
3777 * create_path
3778 *
3779 * This function creates a whole path, like mkdir -p. Example paths:
3780 * this/is/an/example/
3781 * /this/is/an/example/
3782 * Warning: a path *must* end with a slash!
3783 *
3784 * Parameters:
3785 *
3786 * cnt - current thread's context structure (for logging)
3787 * path - the path to create
3788 *
3789 * Returns: 0 on success, -1 on failure
3790 */
create_path(const char * path)3791 int create_path(const char *path)
3792 {
3793 char *start;
3794 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
3795
3796 if (path[0] == '/')
3797 start = strchr(path + 1, '/');
3798 else
3799 start = strchr(path, '/');
3800
3801 while (start) {
3802 char *buffer = mystrdup(path);
3803 buffer[start-path] = 0x00;
3804
3805 if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
3806 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
3807 ,_("Problem creating directory %s"), buffer);
3808 free(buffer);
3809 return -1;
3810 }
3811
3812 start = strchr(start + 1, '/');
3813
3814 if (!start)
3815 MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("creating directory %s"), buffer);
3816
3817 free(buffer);
3818 }
3819
3820 return 0;
3821 }
3822
3823 /**
3824 * myfopen
3825 *
3826 * This function opens a file, if that failed because of an ENOENT error
3827 * (which is: path does not exist), the path is created and then things are
3828 * tried again. This is faster then trying to create that path over and over
3829 * again. If someone removes the path after it was created, myfopen will
3830 * recreate the path automatically.
3831 *
3832 * Parameters:
3833 *
3834 * path - path to the file to open
3835 * mode - open mode
3836 *
3837 * Returns: the file stream object
3838 */
myfopen(const char * path,const char * mode)3839 FILE * myfopen(const char *path, const char *mode)
3840 {
3841 /* first, just try to open the file */
3842 FILE *dummy = fopen(path, mode);
3843 if (dummy) return dummy;
3844
3845 /* could not open file... */
3846 /* path did not exist? */
3847 if (errno == ENOENT) {
3848
3849 /* create path for file... */
3850 if (create_path(path) == -1)
3851 return NULL;
3852
3853 /* and retry opening the file */
3854 dummy = fopen(path, mode);
3855 }
3856 if (!dummy) {
3857 /*
3858 * Two possibilities
3859 * 1: there was an other error while trying to open the file for the
3860 * first time
3861 * 2: could still not open the file after the path was created
3862 */
3863 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
3864 ,_("Error opening file %s with mode %s"), path, mode);
3865 return NULL;
3866 }
3867
3868 return dummy;
3869 }
3870
3871 /**
3872 * myfclose
3873 *
3874 * Motion-specific variant of fclose()
3875 *
3876 * Returns: fclose() return value
3877 */
myfclose(FILE * fh)3878 int myfclose(FILE* fh)
3879 {
3880 int rval = fclose(fh);
3881
3882 if (rval != 0)
3883 MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, _("Error closing file"));
3884
3885 return rval;
3886 }
3887
3888 /**
3889 * mystrftime_long
3890 *
3891 * Motion-specific long form of format specifiers.
3892 *
3893 * Parameters:
3894 *
3895 * cnt - current thread's context structure.
3896 * width - width associated with the format specifier.
3897 * word - beginning of the format specifier's word.
3898 * l - length of the format specifier's word.
3899 * out - output buffer where to store the result. Size: PATH_MAX.
3900 *
3901 * This is called if a format specifier with the format below was found:
3902 *
3903 * % { word }
3904 *
3905 * As a special edge case, an incomplete format at the end of the string
3906 * is processed as well:
3907 *
3908 * % { word \0
3909 *
3910 * Any valid format specified width is supported, e.g. "%12{host}".
3911 *
3912 * The following specifier keywords are currently supported:
3913 *
3914 * host Replaced with the name of the local machine (see gethostname(2)).
3915 * fps Equivalent to %fps.
3916 */
mystrftime_long(const struct context * cnt,int width,const char * word,int l,char * out)3917 static void mystrftime_long (const struct context *cnt,
3918 int width, const char *word, int l, char *out)
3919 {
3920 #define SPECIFIERWORD(k) ((strlen(k)==l) && (!strncmp (k, word, l)))
3921
3922 if (SPECIFIERWORD("host")) {
3923 snprintf (out, PATH_MAX, "%*s", width, cnt->hostname);
3924 return;
3925 }
3926 if (SPECIFIERWORD("fps")) {
3927 sprintf(out, "%*d", width, cnt->movie_fps);
3928 return;
3929 }
3930 if (SPECIFIERWORD("dbeventid")) {
3931 sprintf(out, "%*llu", width, cnt->database_event_id);
3932 return;
3933 }
3934 if (SPECIFIERWORD("ver")) {
3935 sprintf(out, "%*s", width, VERSION);
3936 return;
3937 }
3938
3939 // Not a valid modifier keyword. Log the error and ignore.
3940 MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO,
3941 _("invalid format specifier keyword %*.*s"), l, l, word);
3942
3943 // Do not let the output buffer empty, or else where to restart the
3944 // interpretation of the user string will become dependent to far too
3945 // many conditions. Maybe change loop to "if (*pos_userformat == '%') {
3946 // ...} __else__ ..."?
3947 out[0] = '~'; out[1] = 0;
3948 }
3949
3950 /**
3951 * mystrftime
3952 *
3953 * Motion-specific variant of strftime(3) that supports additional format
3954 * specifiers in the format string.
3955 *
3956 * Parameters:
3957 *
3958 * cnt - current thread's context structure
3959 * s - destination string
3960 * max - max number of bytes to write
3961 * userformat - format string
3962 * tm - time information
3963 * filename - string containing full path of filename
3964 * set this to NULL if not relevant
3965 * sqltype - Filetype as used in SQL feature, set to 0 if not relevant
3966 *
3967 * Returns: number of bytes written to the string s
3968 */
mystrftime(const struct context * cnt,char * s,size_t max,const char * userformat,const struct timeval * tv1,const char * filename,int sqltype)3969 size_t mystrftime(const struct context *cnt, char *s, size_t max, const char *userformat,
3970 const struct timeval *tv1, const char *filename, int sqltype)
3971 {
3972 char formatstring[PATH_MAX] = "";
3973 char tempstring[PATH_MAX] = "";
3974 char *format, *tempstr;
3975 const char *pos_userformat;
3976 int width;
3977 struct tm timestamp_tm;
3978
3979 localtime_r(&tv1->tv_sec, ×tamp_tm);
3980
3981 format = formatstring;
3982
3983 /* if mystrftime is called with userformat = NULL we return a zero length string */
3984 if (userformat == NULL) {
3985 *s = '\0';
3986 return 0;
3987 }
3988
3989 for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
3990
3991 if (*pos_userformat == '%') {
3992 /*
3993 * Reset 'tempstr' to point to the beginning of 'tempstring',
3994 * otherwise we will eat up tempstring if there are many
3995 * format specifiers.
3996 */
3997 tempstr = tempstring;
3998 tempstr[0] = '\0';
3999 width = 0;
4000 while ('0' <= pos_userformat[1] && pos_userformat[1] <= '9') {
4001 width *= 10;
4002 width += pos_userformat[1] - '0';
4003 ++pos_userformat;
4004 }
4005
4006 switch (*++pos_userformat) {
4007 case '\0': // end of string
4008 --pos_userformat;
4009 break;
4010
4011 case 'v': // event
4012 sprintf(tempstr, "%0*d", width ? width : 2, cnt->event_nr);
4013 break;
4014
4015 case 'q': // shots
4016 sprintf(tempstr, "%0*d", width ? width : 2,
4017 cnt->current_image->shot);
4018 break;
4019
4020 case 'D': // diffs
4021 sprintf(tempstr, "%*d", width, cnt->current_image->diffs);
4022 break;
4023
4024 case 'N': // noise
4025 sprintf(tempstr, "%*d", width, cnt->noise);
4026 break;
4027
4028 case 'i': // motion width
4029 sprintf(tempstr, "%*d", width,
4030 cnt->current_image->location.width);
4031 break;
4032
4033 case 'J': // motion height
4034 sprintf(tempstr, "%*d", width,
4035 cnt->current_image->location.height);
4036 break;
4037
4038 case 'K': // motion center x
4039 sprintf(tempstr, "%*d", width, cnt->current_image->location.x);
4040 break;
4041
4042 case 'L': // motion center y
4043 sprintf(tempstr, "%*d", width, cnt->current_image->location.y);
4044 break;
4045
4046 case 'o': // threshold
4047 sprintf(tempstr, "%*d", width, cnt->threshold);
4048 break;
4049
4050 case 'Q': // number of labels
4051 sprintf(tempstr, "%*d", width,
4052 cnt->current_image->total_labels);
4053 break;
4054
4055 case 't': // camera id
4056 sprintf(tempstr, "%*d", width, cnt->camera_id);
4057 break;
4058
4059 case 'C': // text_event
4060 if (cnt->text_event_string[0])
4061 snprintf(tempstr, PATH_MAX, "%*s", width,
4062 cnt->text_event_string);
4063 else
4064 ++pos_userformat;
4065 break;
4066
4067 case 'w': // picture width
4068 sprintf(tempstr, "%*d", width, cnt->imgs.width);
4069 break;
4070
4071 case 'h': // picture height
4072 sprintf(tempstr, "%*d", width, cnt->imgs.height);
4073 break;
4074
4075 case 'f': // filename -- or %fps
4076 if ((*(pos_userformat+1) == 'p') && (*(pos_userformat+2) == 's')) {
4077 sprintf(tempstr, "%*d", width, cnt->movie_fps);
4078 pos_userformat += 2;
4079 break;
4080 }
4081
4082 if (filename)
4083 snprintf(tempstr, PATH_MAX, "%*s", width, filename);
4084 else
4085 ++pos_userformat;
4086 break;
4087
4088 case 'n': // sqltype
4089 if (sqltype)
4090 sprintf(tempstr, "%*d", width, sqltype);
4091 else
4092 ++pos_userformat;
4093 break;
4094
4095 case '{': // long format specifier word.
4096 {
4097 const char *word = ++pos_userformat;
4098 while ((*pos_userformat != '}') && (*pos_userformat != 0))
4099 ++pos_userformat;
4100 mystrftime_long (cnt, width, word, (int)(pos_userformat-word), tempstr);
4101 if (*pos_userformat == '\0') --pos_userformat;
4102 }
4103 break;
4104
4105 case '$': // thread name
4106 if (cnt->conf.camera_name && cnt->conf.camera_name[0])
4107 snprintf(tempstr, PATH_MAX, "%s", cnt->conf.camera_name);
4108 else
4109 ++pos_userformat;
4110 break;
4111
4112 default: // Any other code is copied with the %-sign
4113 *format++ = '%';
4114 *format++ = *pos_userformat;
4115 continue;
4116 }
4117
4118 /*
4119 * If a format specifier was found and used, copy the result from
4120 * 'tempstr' to 'format'.
4121 */
4122 if (tempstr[0]) {
4123 while ((*format = *tempstr++) != '\0')
4124 ++format;
4125 continue;
4126 }
4127 }
4128
4129 /* For any other character than % we just simply copy the character */
4130 *format++ = *pos_userformat;
4131 }
4132
4133 *format = '\0';
4134 format = formatstring;
4135
4136 return strftime(s, max, format, ×tamp_tm);
4137 }
4138 /* This is a temporary location for these util functions. All the generic utility
4139 * functions will be collected here and ultimately moved into a new common "util" module
4140 */
util_threadname_set(const char * abbr,int threadnbr,const char * threadname)4141 void util_threadname_set(const char *abbr, int threadnbr, const char *threadname){
4142 /* When the abbreviation is sent in as null, that means we are being
4143 * provided a fully filled out thread name (usually obtained from a
4144 * previously called get_threadname so we set it without additional
4145 * formatting.
4146 */
4147
4148 char tname[16];
4149 if (abbr != NULL){
4150 snprintf(tname, sizeof(tname), "%s%d%s%s",abbr,threadnbr,
4151 threadname ? ":" : "",
4152 threadname ? threadname : "");
4153 } else {
4154 snprintf(tname, sizeof(tname), "%s",threadname);
4155 }
4156
4157 #ifdef __APPLE__
4158 pthread_setname_np(tname);
4159 #elif defined(BSD)
4160 pthread_set_name_np(pthread_self(), tname);
4161 #elif HAVE_PTHREAD_SETNAME_NP
4162 pthread_setname_np(pthread_self(), tname);
4163 #else
4164 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, _("Unable to set thread name %s"), tname);
4165 #endif
4166
4167 }
4168
util_threadname_get(char * threadname)4169 void util_threadname_get(char *threadname){
4170
4171 #if ((!defined(BSD) && HAVE_PTHREAD_GETNAME_NP) || defined(__APPLE__))
4172 char currname[16];
4173 pthread_getname_np(pthread_self(), currname, sizeof(currname));
4174 snprintf(threadname, sizeof(currname), "%s",currname);
4175 #else
4176 snprintf(threadname, 8, "%s","Unknown");
4177 #endif
4178
4179 }
util_check_passthrough(struct context * cnt)4180 int util_check_passthrough(struct context *cnt){
4181 #if (HAVE_FFMPEG && LIBAVFORMAT_VERSION_MAJOR < 55)
4182 if (cnt->movie_passthrough)
4183 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
4184 ,_("FFMPEG version too old. Disabling pass-through processing."));
4185 return 0;
4186 #else
4187 if (cnt->movie_passthrough){
4188 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
4189 ,_("pass-through is enabled but is still experimental."));
4190 return 1;
4191 } else {
4192 return 0;
4193 }
4194 #endif
4195
4196 }
4197