📄 motion.c
字号:
/** * motion_cleanup * * This routine is called from motion_loop when thread ends to * cleanup all memory etc. that motion_init did. * * Parameters: * * cnt Pointer to the motion context structure * * Returns: nothing */static void motion_cleanup(struct context *cnt){ /* Stop webcam */ event(cnt, EVENT_STOP, NULL, NULL, NULL, NULL); if (cnt->video_dev >= 0){ motion_log(LOG_DEBUG, 0, "Calling vid_close() from motion_cleanup"); vid_close(cnt); } if (cnt->imgs.out) { free(cnt->imgs.out); cnt->imgs.out = NULL; } if (cnt->imgs.ref) { free(cnt->imgs.ref); cnt->imgs.ref = NULL; } if (cnt->imgs.ref_dyn) { free(cnt->imgs.ref_dyn); cnt->imgs.ref_dyn = NULL; } if (cnt->imgs.image_virgin) { free(cnt->imgs.image_virgin); cnt->imgs.image_virgin = NULL; } if (cnt->imgs.labels) { free(cnt->imgs.labels); cnt->imgs.labels = NULL; } if (cnt->imgs.labelsize) { free(cnt->imgs.labelsize); cnt->imgs.labelsize = NULL; } if (cnt->imgs.smartmask) { free(cnt->imgs.smartmask); cnt->imgs.smartmask = NULL; } if (cnt->imgs.smartmask_final) { free(cnt->imgs.smartmask_final); cnt->imgs.smartmask_final = NULL; } if (cnt->imgs.smartmask_buffer) { free(cnt->imgs.smartmask_buffer); cnt->imgs.smartmask_buffer = NULL; } if (cnt->imgs.common_buffer) { free(cnt->imgs.common_buffer); cnt->imgs.common_buffer = NULL; } if (cnt->imgs.preview_image.image) { free(cnt->imgs.preview_image.image); cnt->imgs.preview_image.image = NULL; } image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */ rotate_deinit(cnt); /* cleanup image rotation data */ if(cnt->pipe != -1) { close(cnt->pipe); cnt->pipe = -1; } if(cnt->mpipe != -1) { close(cnt->mpipe); cnt->mpipe = -1; } /* Cleanup the current time structure */ if (cnt->currenttime_tm) { free(cnt->currenttime_tm); cnt->currenttime_tm = NULL; } /* Cleanup the event time structure */ if (cnt->eventtime_tm) { free(cnt->eventtime_tm); cnt->eventtime_tm = NULL; }}/** * motion_loop * * Thread function for the motion handling threads. * */static void *motion_loop(void *arg){ struct context *cnt = arg; int i, j, z = 0; time_t lastframetime = 0; int frame_buffer_size; unsigned short int ref_frame_limit = 0; int area_once = 0; int area_minx[9], area_miny[9], area_maxx[9], area_maxy[9]; int smartmask_ratio = 0; int smartmask_count = 20; int smartmask_lastrate = 0; int olddiffs = 0; int previous_diffs = 0, previous_location_x = 0, previous_location_y = 0; unsigned short int text_size_factor; unsigned short int passflag = 0; long int *rolling_average_data = NULL; long int rolling_average_limit, required_frame_time, frame_delay, delay_time_nsec; int rolling_frame = 0; struct timeval tv1, tv2; unsigned long int rolling_average, elapsedtime; unsigned long long int timenow = 0, timebefore = 0; int vid_return_code = 0; /* Return code used when calling vid_next */ int minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; /* time in seconds to skip between capturing images */ unsigned short int get_image = 1; /* Flag used to signal that we capture new image when we run the loop */ /* Next two variables are used for snapshot and timelapse feature * time_last_frame is set to 1 so that first coming timelapse or second=0 * is acted upon. */ unsigned long int time_last_frame=1, time_current_frame; cnt->running = 1; if (motion_init(cnt) < 0) { goto err; } /* Initialize the double sized characters if needed. */ if(cnt->conf.text_double) text_size_factor = 2; else text_size_factor = 1; /* Initialize area detection */ area_minx[0] = area_minx[3] = area_minx[6] = area_miny[0] = area_miny[1] = area_miny[2] = 0; area_minx[1] = area_minx[4] = area_minx[7] = area_maxx[0] = area_maxx[3] = area_maxx[6] = cnt->imgs.width / 3; area_minx[2] = area_minx[5] = area_minx[8] = area_maxx[1] = area_maxx[4] = area_maxx[7] = cnt->imgs.width / 3 * 2; area_miny[3] = area_miny[4] = area_miny[5] = area_maxy[0] = area_maxy[1] = area_maxy[2] = cnt->imgs.height / 3; area_miny[6] = area_miny[7] = area_miny[8] = area_maxy[3] = area_maxy[4] = area_maxy[5] = cnt->imgs.height / 3 * 2; area_maxx[2] = area_maxx[5] = area_maxx[8] = cnt->imgs.width; area_maxy[6] = area_maxy[7] = area_maxy[8] = cnt->imgs.height; /* Work out expected frame rate based on config setting */ if (cnt->conf.frame_limit < 2) cnt->conf.frame_limit = 2; required_frame_time = 1000000L / cnt->conf.frame_limit; frame_delay = required_frame_time; /* * Reserve enough space for a 10 second timing history buffer. Note that, * if there is any problem on the allocation, mymalloc does not return. */ rolling_average_limit = 10 * cnt->conf.frame_limit; rolling_average_data = mymalloc(sizeof(rolling_average_data) * rolling_average_limit); /* Preset history buffer with expected frame rate */ for (j=0; j< rolling_average_limit; j++) rolling_average_data[j]=required_frame_time; /* MAIN MOTION LOOP BEGINS HERE */ /* Should go on forever... unless you bought vaporware :) */ while (!cnt->finish || cnt->makemovie) { /***** MOTION LOOP - PREPARE FOR NEW FRAME SECTION *****/ cnt->watchdog = WATCHDOG_TMO; /* Get current time and preserver last time for frame interval calc. */ timebefore = timenow; gettimeofday(&tv1, NULL); timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; /* since we don't have sanity checks done when options are set, * this sanity check must go in the main loop :(, before pre_captures * are attempted. */ if (cnt->conf.minimum_motion_frames < 1) cnt->conf.minimum_motion_frames = 1; if (cnt->conf.pre_capture < 0) cnt->conf.pre_capture = 0; /* Check if our buffer is still the right size * If pre_capture or minimum_motion_frames has been changed * via the http remote control we need to re-size the ring buffer */ frame_buffer_size = cnt->conf.pre_capture + cnt->conf.minimum_motion_frames; if (cnt->imgs.image_ring_size != frame_buffer_size) { image_ring_resize(cnt, frame_buffer_size); } /* Get time for current frame */ cnt->currenttime = time(NULL); /* localtime returns static data and is not threadsafe * so we use localtime_r which is reentrant and threadsafe */ localtime_r(&cnt->currenttime, cnt->currenttime_tm); /* If we have started on a new second we reset the shots variable * lastrate is updated to be the number of the last frame. last rate * is used as the ffmpeg framerate when motion is detected. */ if (lastframetime != cnt->currenttime) { cnt->lastrate = cnt->shots + 1; cnt->shots = -1; lastframetime = cnt->currenttime; if (cnt->conf.minimum_frame_time) { minimum_frame_time_downcounter--; if (minimum_frame_time_downcounter == 0) get_image = 1; } else get_image = 1; } /* Increase the shots variable for each frame captured within this second */ cnt->shots++; if (cnt->startup_frames > 0) cnt->startup_frames--; if (get_image){ if (cnt->conf.minimum_frame_time) { minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; get_image = 0; } /* ring_buffer_in is pointing to current pos, update before put in a new image */ if (++cnt->imgs.image_ring_in >= cnt->imgs.image_ring_size) cnt->imgs.image_ring_in = 0; /* Check if we have filled the ring buffer, throw away last image */ if (cnt->imgs.image_ring_in == cnt->imgs.image_ring_out) { if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) cnt->imgs.image_ring_out = 0; } /* cnt->current_image points to position in ring where to store image, diffs etc. */ cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_in]; /* Init/clear current_image */ { /* Store time with pre_captured image */ cnt->current_image->timestamp = cnt->currenttime; localtime_r(&cnt->current_image->timestamp, &cnt->current_image->timestamp_tm); /* Store shot number with pre_captured image */ cnt->current_image->shot = cnt->shots; /* set diffs to 0 now, will be written after we calculated diffs in new image */ cnt->current_image->diffs = 0; /* Set flags to 0 */ cnt->current_image->flags = 0; cnt->current_image->cent_dist = 0; /* Clear location data */ memset(&cnt->current_image->location, 0, sizeof(cnt->current_image->location)); cnt->current_image->total_labels = 0; } /***** MOTION LOOP - RETRY INITIALIZING SECTION *****/ /* If a camera is not available we keep on retrying every 10 seconds * until it shows up. */ if (cnt->video_dev < 0 && cnt->currenttime % 10 == 0 && cnt->shots == 0) { motion_log(LOG_ERR, 0, "Retrying until successful connection with camera"); cnt->video_dev = vid_start(cnt); /* if the netcam has different dimensions than in the config file * we need to restart Motion to re-allocate all the buffers */ if (cnt->imgs.width != cnt->conf.width || cnt->imgs.height != cnt->conf.height) { motion_log(LOG_ERR, 0, "Camera has finally become available"); motion_log(LOG_ERR, 0, "Camera image has different width and height " "from what is in the config file. You should fix that"); motion_log(LOG_ERR, 0, "Restarting Motion thread to reinitialize all " "image buffers to new picture dimensions"); cnt->conf.width = cnt->imgs.width; cnt->conf.height = cnt->imgs.height; /* Break out of main loop terminating thread * watchdog will start us again */ break; } } /***** MOTION LOOP - IMAGE CAPTURE SECTION *****/ /* Fetch next frame from camera * If vid_next returns 0 all is well and we got a new picture * Any non zero value is an error. * 0 = OK, valid picture * <0 = fatal error - leave the thread by breaking out of the main loop * >0 = non fatal error - copy last image or show grey image with message */ if (cnt->video_dev >= 0) vid_return_code = vid_next(cnt, cnt->current_image->image); else vid_return_code = 1; /* Non fatal error */ // VALID PICTURE if (vid_return_code == 0) { cnt->lost_connection = 0; cnt->connectionlosttime = 0; /* If all is well reset missing_frame_counter */ if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { /* If we previously logged starting a grey image, now log video re-start */ motion_log(LOG_ERR, 0, "Video signal re-acquired"); // event for re-acquired video signal can be called here } cnt->missing_frame_counter = 0;#ifdef HAVE_FFMPEG /* Deinterlace the image with ffmpeg, before the image is modified. */ if(cnt->conf.ffmpeg_deinterlace) { ffmpeg_deinterlace(cnt->current_image->image, cnt->imgs.width, cnt->imgs.height); }#endif /* save the newly captured still virgin image to a buffer * which we will not alter with text and location graphics */ memcpy(cnt->imgs.image_virgin, cnt->current_image->image, cnt->imgs.size); /* If the camera is a netcam we let the camera decide the pace. * Otherwise we will keep on adding duplicate frames. * By resetting the timer the framerate becomes maximum the rate * of the Netcam. */ if (cnt->conf.netcam_url) { gettimeofday(&tv1, NULL); timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; } // FATAL ERROR - leave the thread by breaking out of the main loop } else if (vid_return_code < 0) { /* Fatal error - Close video device */ motion_log(LOG_ERR, 0, "Video device fatal error - Closing video device"); vid_close(cnt); /* Use virgin image, if we are not able to open it again next loop * a gray image with message is applied * flag lost_connection */ memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); cnt->lost_connection = 1; /* NO FATAL ERROR - * copy last image or show grey image with message * flag on lost_connection if : * vid_return_code == NETCAM_RESTART_ERROR * cnt->video_dev < 0 * cnt->missing_frame_counter > (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) */ } else { if (debug_level >= CAMERA_VERBOSE) motion_log(-1, 0, "vid_return_code %d", vid_return_code); /* Netcams that change dimensions while Motion is running will * require that Motion restarts to reinitialize all the many * buffers inside Motion. It will be a mess to try and recover any * other way */ if (vid_return_code == NETCAM_RESTART_ERROR) { motion_log(LOG_ERR, 0, "Restarting Motion thread to reinitialize all " "image buffers"); /* Break out of main loop terminating thread
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -