📄 motion.c
字号:
cnt->makemovie = 1; /* Now test for quiet longer than 'gap' OR make movie as decided in * previous statement. */ if (((cnt->currenttime - cnt->lasttime >= cnt->conf.gap) && cnt->conf.gap > 0) || cnt->makemovie) { if (cnt->event_nr == cnt->prev_event || cnt->makemovie) { /* Flush image buffer */ process_image_ring(cnt, IMAGE_BUFFER_FLUSH); /* Save preview_shot here at the end of event */ if (cnt->imgs.preview_image.diffs) { preview_save(cnt); cnt->imgs.preview_image.diffs = 0; } event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, cnt->currenttime_tm); /* if tracking is enabled we center our camera so it does not * point to a place where it will miss the next action */ if (cnt->track.type) cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); if (cnt->conf.setup_mode) motion_log(-1, 0, "End of event %d", cnt->event_nr); cnt->makemovie = 0; /* Reset post capture */ cnt->postcap = 0; /* Finally we increase the event number */ cnt->event_nr++; cnt->lightswitch_framecounter = 0; /* And we unset the text_event_string to avoid that buffered * images get a timestamp from previous event. */ cnt->text_event_string[0] = '\0'; } } /* Save/send to movie some images */ process_image_ring(cnt, 2); /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/ /* If setup_mode enabled output some numbers to console */ if (cnt->conf.setup_mode){ char msg[1024] = "\0"; char part[100]; if (cnt->conf.despeckle) { snprintf(part, 99, "Raw changes: %5d - changes after '%s': %5d", olddiffs, cnt->conf.despeckle, cnt->current_image->diffs); strcat(msg, part); if (strchr(cnt->conf.despeckle, 'l')){ sprintf(part, " - labels: %3d", cnt->current_image->total_labels); strcat(msg, part); } } else{ sprintf(part, "Changes: %5d", cnt->current_image->diffs); strcat(msg, part); } if (cnt->conf.noise_tune){ sprintf(part, " - noise level: %2d", cnt->noise); strcat(msg, part); } if (cnt->conf.threshold_tune){ sprintf(part, " - threshold: %d", cnt->threshold); strcat(msg, part); } motion_log(-1, 0, "%s", msg); } } /* get_image end */ /***** MOTION LOOP - SNAPSHOT FEATURE SECTION *****/ /* Did we get triggered to make a snapshot from control http? Then shoot a snap * If snapshot_interval is not zero and time since epoch MOD snapshot_interval = 0 then snap * We actually allow the time to run over the interval in case we have a delay * from slow camera. * Note: Negative value means SIGALRM snaps are enabled * httpd-control snaps are always enabled. */ /* time_current_frame is used both for snapshot and timelapse features */ time_current_frame = cnt->currenttime; if ( (cnt->conf.snapshot_interval > 0 && cnt->shots == 0 && time_current_frame % cnt->conf.snapshot_interval <= time_last_frame % cnt->conf.snapshot_interval) || cnt->snapshot) { event(cnt, EVENT_IMAGE_SNAPSHOT, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); cnt->snapshot = 0; } /***** MOTION LOOP - TIMELAPSE FEATURE SECTION *****/#ifdef HAVE_FFMPEG if (cnt->conf.timelapse) { /* Check to see if we should start a new timelapse file. We start one when * we are on the first shot, and and the seconds are zero. We must use the seconds * to prevent the timelapse file from getting reset multiple times during the minute. */ if (cnt->current_image->timestamp_tm.tm_min == 0 && (time_current_frame % 60 < time_last_frame % 60) && cnt->shots == 0) { if (strcasecmp(cnt->conf.timelapse_mode,"manual") == 0) ;/* No action */ /* If we are daily, raise timelapseend event at midnight */ else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) { if (cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); } /* handle the hourly case */ else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) { event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); } /* If we are weekly-sunday, raise timelapseend event at midnight on sunday */ else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-sunday") == 0) { if (cnt->current_image->timestamp_tm.tm_wday == 0 && cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); } /* If we are weekly-monday, raise timelapseend event at midnight on monday */ else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-monday") == 0) { if (cnt->current_image->timestamp_tm.tm_wday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); } /* If we are monthly, raise timelapseend event at midnight on first day of month */ else if (strcasecmp(cnt->conf.timelapse_mode, "monthly") == 0) { if (cnt->current_image->timestamp_tm.tm_mday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); } /* If invalid we report in syslog once and continue in manual mode */ else { motion_log(LOG_ERR, 0, "Invalid timelapse_mode argument '%s'", cnt->conf.timelapse_mode); motion_log(LOG_ERR, 0, "Defaulting to manual timelapse mode"); conf_cmdparse(&cnt, (char *)"ffmpeg_timelapse_mode",(char *)"manual"); } } /* If ffmpeg timelapse is enabled and time since epoch MOD ffmpeg_timelaps = 0 * add a timelapse frame to the timelapse mpeg. */ if (cnt->shots == 0 && time_current_frame % cnt->conf.timelapse <= time_last_frame % cnt->conf.timelapse) event(cnt, EVENT_TIMELAPSE, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); } /* if timelapse mpeg is in progress but conf.timelapse is zero then close timelapse file * This is an important feature that allows manual roll-over of timelapse file using the http * remote control via a cron job. */ else if (cnt->ffmpeg_timelapse) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);#endif /* HAVE_FFMPEG */ time_last_frame = time_current_frame; /***** MOTION LOOP - VIDEO LOOPBACK SECTION *****/ /* feed last image and motion image to video device pipes and the webcam clients * In setup mode we send the special setup mode image to both webcam and vloopback pipe * In normal mode we feed the latest image to vloopback device and we send * the image to the webcam. We always send the first image in a second to the webcam. * Other image are sent only when the config option webcam_motion is off * The result is that with webcam_motion on the webcam stream is normally at the minimal * 1 frame per second but the minute motion is detected the motion_detected() function * sends all detected pictures to the webcam except the 1st per second which is already sent. */ if (cnt->conf.setup_mode) { event(cnt, EVENT_IMAGE, cnt->imgs.out, NULL, &cnt->pipe, cnt->currenttime_tm); event(cnt, EVENT_WEBCAM, cnt->imgs.out, NULL, NULL, cnt->currenttime_tm); } else { event(cnt, EVENT_IMAGE, cnt->current_image->image, NULL, &cnt->pipe, &cnt->current_image->timestamp_tm); if (!cnt->conf.webcam_motion || cnt->shots == 1) event(cnt, EVENT_WEBCAM, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); } event(cnt, EVENT_IMAGEM, cnt->imgs.out, NULL, &cnt->mpipe, cnt->currenttime_tm); /***** MOTION LOOP - ONCE PER SECOND PARAMETER UPDATE SECTION *****/ /* Check for some config parameter changes but only every second */ if (cnt->shots == 0){ if (strcasecmp(cnt->conf.output_normal, "on") == 0) cnt->new_img = NEWIMG_ON; else if (strcasecmp(cnt->conf.output_normal, "first") == 0) cnt->new_img = NEWIMG_FIRST; else if (strcasecmp(cnt->conf.output_normal, "best") == 0) cnt->new_img = NEWIMG_BEST; else if (strcasecmp(cnt->conf.output_normal, "center") == 0) cnt->new_img = NEWIMG_CENTER; else cnt->new_img = NEWIMG_OFF; if (strcasecmp(cnt->conf.locate, "on") == 0) cnt->locate = LOCATE_ON; else if (strcasecmp(cnt->conf.locate, "preview") == 0) cnt->locate = LOCATE_PREVIEW; else cnt->locate = LOCATE_OFF; /* Sanity check for smart_mask_speed, silly value disables smart mask */ if (cnt->conf.smart_mask_speed < 0 || cnt->conf.smart_mask_speed > 10) cnt->conf.smart_mask_speed = 0; /* Has someone changed smart_mask_speed or framerate? */ if (cnt->conf.smart_mask_speed != cnt->smartmask_speed || smartmask_lastrate != cnt->lastrate){ if (cnt->conf.smart_mask_speed == 0){ memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); } smartmask_lastrate = cnt->lastrate; cnt->smartmask_speed = cnt->conf.smart_mask_speed; /* Decay delay - based on smart_mask_speed (framerate independent) This is always 5*smartmask_speed seconds */ smartmask_ratio = 5 * cnt->lastrate * (11 - cnt->smartmask_speed); }#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) /* Set the sql mask file according to the SQL config options * We update it for every frame in case the config was updated * via remote control. */ cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + cnt->conf.sql_log_mpeg * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE;#endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) */ } /***** MOTION LOOP - FRAMERATE TIMING AND SLEEPING SECTION *****/ /* Work out expected frame rate based on config setting which may have changed from http-control */ if (cnt->conf.frame_limit) required_frame_time = 1000000L / cnt->conf.frame_limit; else required_frame_time = 0; /* Get latest time to calculate time taken to process video data */ gettimeofday(&tv2, NULL); elapsedtime = (tv2.tv_usec + 1000000L * tv2.tv_sec) - timenow; /* Update history buffer but ignore first pass as timebefore variable will be inaccurate */ if (passflag) rolling_average_data[rolling_frame] = timenow-timebefore; else passflag = 1; rolling_frame++; if (rolling_frame >= rolling_average_limit) rolling_frame = 0; /* Calculate 10 second average and use deviation in delay calculation */ rolling_average = 0L; for (j=0; j < rolling_average_limit; j++) rolling_average += rolling_average_data[j]; rolling_average /= rolling_average_limit; frame_delay = required_frame_time-elapsedtime - (rolling_average - required_frame_time); if (frame_delay > 0) { /* Apply delay to meet frame time */ if (frame_delay > required_frame_time) frame_delay = required_frame_time; /* Delay time in nanoseconds for SLEEP */ delay_time_nsec = frame_delay * 1000; if (delay_time_nsec > 999999999) delay_time_nsec = 999999999; /* SLEEP as defined in motion.h A safe sleep using nanosleep */ SLEEP(0, delay_time_nsec); } } /* END OF MOTION MAIN LOOP * If code continues here it is because the thread is exiting or restarting */err: if (rolling_average_data) free(rolling_average_data); cnt->lost_connection = 1; motion_log(-1, 0, "Thread exiting"); motion_cleanup(cnt); pthread_mutex_lock(&global_lock); threads_running--; pthread_mutex_unlock(&global_lock); if (!cnt->restart) cnt->watchdog=WATCHDOG_OFF; cnt->running = 0; cnt->finish = 0; pthread_exit(NULL);}/** * become_daemon * * Turns Motion into a daemon through forking. The parent process (i.e. the * one initially calling this function) will exit inside this function, while * control will be returned to the child process. Standard input/output are * released properly, and the current directory is set to / in order to not * lock up any file system. * * Parameters: * * cnt - current thread's context struct * * Returns: nothing */static void become_daemon(void){ int i; FILE *pidf = NULL; struct sigaction sig_ign_action; /* Setup sig_ign_action */#ifdef SA_RESTART sig_ign_action.sa_flags = SA_RESTART;#else sig_ign_action.sa_flags = 0;#endif sig_ign_action.sa_handler = SIG_IGN; sigemptyset(&sig_ign_action.sa_mask); /* fork */ if (fork()) { motion_log(-1, 0, "Motion going to daemon mode"); exit(0); } /* Create the pid file if defined, if failed exit * If we fail we report it. If we succeed we postpone the log entry till * later when we have closed stdout. Otherwise Motion hangs in the terminal waiting * for an enter. */ if (cnt_list[0]->conf.pid_file) { pidf = fopen(cnt_list[0]->conf.pid_file, "w+"); if ( pidf ) { (void)fprintf(pidf, "%d\n", getpid()); fclose(pidf); } else { motion_log(LOG_ERR, 1, "Exit motion,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -