📄 motion.c
字号:
/* motion.c * * Detect changes in a video stream. * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */#include "ffmpeg.h"#include "motion.h"#if (defined(BSD)) #include "video_freebsd.h"#else#include "video.h"#endif /* BSD */#include "conf.h"#include "alg.h"#include "track.h"#include "event.h"#include "picture.h"#include "rotate.h"/** * tls_key_threadnr * * TLS key for storing thread number in thread-local storage. */pthread_key_t tls_key_threadnr;/** * global_lock * * Protects any global variables (like 'threads_running') during updates, * to prevent problems with multiple threads updating at the same time. *///pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t global_lock;/** * cnt_list * * List of context structures, one for each main Motion thread. */struct context **cnt_list=NULL;/** * threads_running * * Keeps track of number of Motion threads currently running. Also used * by 'main' to know when all threads have exited. */volatile int threads_running=0;/* * debug_level is for developers, normally used to control which * types of messages get output. */int debug_level;/** * restart * * Differentiates between a quit and a restart. When all threads have * finished running, 'main' checks if 'restart' is true and if so starts * up again (instead of just quitting). */int restart=0;/** * context_init * * Initializes a context struct with the default values for all the * variables. * * Parameters: * * cnt - the context struct to destroy * * Returns: nothing */static void context_init (struct context *cnt){ /* * We first clear the entire structure to zero, then fill in any * values which have non-zero default values. Note that this * assumes that a NULL address pointer has a value of binary 0 * (this is also assumed at other places within the code, i.e. * there are instances of "if (ptr)"). Just for possible future * changes to this assumption, any pointers which are intended * to be initialised to NULL are listed within a comment. */ memset(cnt, 0, sizeof(struct context)); cnt->noise = 255; cnt->lastrate = 25; memcpy(&cnt->track, &track_template, sizeof(struct trackoptions)); cnt->pipe = -1; cnt->mpipe = -1;}/** * context_destroy * * Destroys a context struct by freeing allocated memory, calling the * appropriate cleanup functions and finally freeing the struct itself. * * Parameters: * * cnt - the context struct to destroy * * Returns: nothing */static void context_destroy(struct context *cnt){ int j; if (cnt->imgs.out) free(cnt->imgs.out); if (cnt->imgs.ref) free(cnt->imgs.ref); if (cnt->imgs.image_virgin) free(cnt->imgs.image_virgin); if (cnt->imgs.image_ring_buffer) free(cnt->imgs.image_ring_buffer); if (cnt->imgs.labels) free(cnt->imgs.labels); if (cnt->imgs.labelsize) free(cnt->imgs.labelsize); if (cnt->imgs.smartmask) free(cnt->imgs.smartmask); if (cnt->imgs.smartmask_final) free(cnt->imgs.smartmask_final); if (cnt->imgs.smartmask_buffer) free(cnt->imgs.smartmask_buffer); if (cnt->imgs.common_buffer) free(cnt->imgs.common_buffer); if (cnt->imgs.timestamp) free(cnt->imgs.timestamp); if (cnt->imgs.shotstamp) free(cnt->imgs.shotstamp); if (cnt->imgs.preview_buffer) free(cnt->imgs.preview_buffer); rotate_deinit(cnt); /* cleanup image rotation data */ if(cnt->pipe != -1) close(cnt->pipe); if(cnt->mpipe != -1) close(cnt->mpipe); /* Cleanup the netcam part */ if(cnt->netcam) netcam_cleanup(cnt->netcam, 0); /* Cleanup the current time structure */ if (cnt->currenttime_tm) free(cnt->currenttime_tm); /* Cleanup the event time structure */ if (cnt->eventtime_tm) free(cnt->eventtime_tm); /* Free memory allocated for config parameters */ for (j = 0; config_params[j].param_name != NULL; j++) { if (config_params[j].copy == copy_string) { void **val; val = (void *)((char *)cnt+(int)config_params[j].conf_value); if (*val) { free(*val); *val = NULL; } } } free(cnt);}/** * sig_handler * * Our SIGNAL-Handler. We need this to handle alarms and external signals. */static void sig_handler(int signo){ int i; switch(signo) { case SIGALRM: /* Somebody (maybe we ourself) wants us to make a snapshot * This feature triggers snapshots on ALL threads that have * snapshot_interval different from 0. */ if (cnt_list) { i = -1; while (cnt_list[++i]) { if (cnt_list[i]->conf.snapshot_interval) { cnt_list[i]->snapshot=1; } } } break; case SIGUSR1: /* Ouch! We have been hit from the outside! Someone wants us to make a movie! */ if (cnt_list) { i = -1; while (cnt_list[++i]) cnt_list[i]->makemovie=1; } break; case SIGHUP: restart = 1; /* Fall through, as the value of 'restart' is the only difference * between SIGHUP and the ones below. */ case SIGINT: case SIGQUIT: case SIGTERM: /* Somebody wants us to quit! We should better finish the actual movie and end up! */ if (cnt_list) { i = -1; while (cnt_list[++i]) { cnt_list[i]->makemovie=1; cnt_list[i]->finish=1; } } break; case SIGSEGV: exit(0); }}/** * sigchild_handler * * This function is a POSIX compliant replacement of the commonly used * signal(SIGCHLD, SIG_IGN). */static void sigchild_handler(int signo ATTRIBUTE_UNUSED){#ifdef WNOHANG while (waitpid(-1, NULL, WNOHANG) > 0) {};#endif /* WNOHANG */ return;}/** * motion_detected * * Called from 'motion_loop' when motion is detected, or when to act as if * motion was detected (e.g. in post capture). * * Parameters: * * cnt - current thread's context struct * diffs - number of different pixels between the reference image and the * new image (may be zero) * dev - video device file descriptor * devpipe - file descriptor of still image pipe * devmpipe - file descriptor of motion pipe * newimg - pointer to the newly captured image */static void motion_detected(struct context *cnt, int diffs, int dev, unsigned char *newimg){ struct config *conf = &cnt->conf; struct images *imgs = &cnt->imgs; struct coord *location = &cnt->location; cnt->lasttime = cnt->currenttime; /* Take action if this is a new event */ if (cnt->event_nr != cnt->prev_event) { int i, tmpshots; struct tm tmptime; cnt->preview_max = 0; /* Reset prev_event number to current event and save event time * in both time_t and struct tm format. */ cnt->prev_event = cnt->event_nr; cnt->eventtime = cnt->currenttime; localtime_r(&cnt->eventtime, cnt->eventtime_tm); /* Since this is a new event we create the event_text_string used for * the %C conversion specifier. We may already need it for * on_motion_detected_commend so it must be done now. */ mystrftime(cnt, cnt->text_event_string, sizeof(cnt->text_event_string), cnt->conf.text_event, cnt->eventtime_tm, NULL, 0); /* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */ event(cnt, EVENT_FIRSTMOTION, newimg, NULL, NULL, cnt->currenttime_tm); if (cnt->conf.setup_mode) motion_log(-1, 0, "Motion detected - starting event %d", cnt->event_nr); /* pre_capture frames are written as jpegs and to the ffmpeg film * We store the current cnt->shots temporarily until we are done with * the pre_capture stuff */ tmpshots = cnt->shots; for (i=cnt->precap_cur; i < cnt->precap_nr; i++) { localtime_r(cnt->imgs.timestamp + i, &tmptime); cnt->shots = *(cnt->imgs.shotstamp + i); event(cnt, EVENT_IMAGE_DETECTED, cnt->imgs.image_ring_buffer + (cnt->imgs.size * i), NULL, NULL, &tmptime); } if (cnt->precap_cur) { localtime_r(cnt->imgs.timestamp+cnt->precap_nr, &tmptime); cnt->shots = *(cnt->imgs.shotstamp + cnt->precap_nr); event(cnt, EVENT_IMAGE_DETECTED, cnt->imgs.image_ring_buffer + (cnt->imgs.size * cnt->precap_nr), NULL, NULL, &tmptime); } for (i=0; i < cnt->precap_cur-1; i++) { localtime_r(cnt->imgs.timestamp + i, &tmptime); cnt->shots = *(cnt->imgs.shotstamp + i); event(cnt, EVENT_IMAGE_DETECTED, cnt->imgs.image_ring_buffer + (cnt->imgs.size * i), NULL, NULL, &tmptime); } /* If output_normal=first always capture first motion frame as preview-shot */ if (cnt->new_img == NEWIMG_FIRST){ cnt->preview_shot = 1; if (cnt->locate == LOCATE_PREVIEW){ alg_draw_location(location, imgs, imgs->width, newimg, LOCATE_NORMAL); } } cnt->shots = tmpshots; } /* motion_detected is called with diffs = 0 during post_capture * and if cnt->conf.output_all is enabled. We only want to draw location * and call EVENT_MOTION when it is a picture frame with actual motion detected. */ if (diffs) { if (cnt->locate == LOCATE_ON) alg_draw_location(location, imgs, imgs->width, newimg, LOCATE_BOTH); /* EVENT_MOTION triggers event_beep and on_motion_detected_command */ event(cnt, EVENT_MOTION, NULL, NULL, NULL, cnt->currenttime_tm); } /* Check for most significant preview-shot when output_normal=best */ if (cnt->new_img == NEWIMG_BEST && diffs > cnt->preview_max) { memcpy(cnt->imgs.preview_buffer, newimg, cnt->imgs.size); cnt->preview_max = diffs; if (cnt->locate == LOCATE_PREVIEW){ alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_buffer, LOCATE_NORMAL); } } if (cnt->shots < conf->frame_limit) { cnt->lastshottime = cnt->currenttime; /* Output the latest picture 'image_new' or image_out for motion picture. */ event(cnt, EVENT_IMAGE_DETECTED, newimg, NULL, NULL, cnt->currenttime_tm); /* If config option webcam_motion is enabled, send the latest motion detected image * to the webcam but only if it is not the first shot within a second. This is to * avoid double frames since we already have sent a frame to the webcam. * We also disable this in setup_mode. */ if (conf->webcam_motion && !conf->setup_mode && cnt->shots != 1) event(cnt, EVENT_WEBCAM, newimg, NULL, NULL, cnt->currenttime_tm); cnt->preview_shot = 0; } if (cnt->track.type != 0 && diffs != 0) { cnt->moved = track_move(cnt, dev, &cnt->location, imgs, 0); }}/** * motion_remove_pid * * This function remove the process id file ( pid file ) before motion exit. * */static void motion_remove_pid(void){ if ((cnt_list[0]->daemon) && (cnt_list[0]->conf.pid_file)){ if (!unlink(cnt_list[0]->conf.pid_file)) motion_log(LOG_INFO, 0, "Removed process id file (pid file)."); else motion_log(LOG_INFO, 1, "Error removing pid file"); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -