📄 motion.c
字号:
/** * motion_init * * This routine is called from motion_loop (the main thread of the program) to do * all of the initialisation required before starting the actual run. * * Parameters: * * cnt Pointer to the motion context structure * * Returns: nothing */static void motion_init(struct context *cnt){ int i; FILE *picture; /* Store thread number in TLS. */ pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); cnt->diffs = 0; cnt->currenttime_tm = mymalloc(sizeof(struct tm)); cnt->eventtime_tm = mymalloc(sizeof(struct tm)); cnt->smartmask_speed = 0; /* We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so * that certain code below does not run until motion has been detected the first time */ cnt->event_nr = 1; cnt->prev_event = 0; motion_log(LOG_DEBUG, 0, "Thread started"); if (!cnt->conf.filepath) cnt->conf.filepath = strdup("."); /* set the device settings */ cnt->video_dev = vid_start(cnt); /* We still cannot handle a V4L type camera not being available * during startup. We have no other option than to die */ if (cnt->video_dev == -1 && !cnt->conf.netcam_url) { motion_log(LOG_ERR, 0, "Capture error calling vid_start"); motion_log(-1 , 0, "Thread finishing..."); motion_remove_pid(); exit(1); } /* We failed to get an initial image from a network camera * So we need to guess height and width based on the config * file options. */ if (cnt->video_dev == -1) { motion_log(LOG_ERR, 0, "Could not fetch initial image from network camera"); motion_log(LOG_ERR, 0, "Motion continues using width and height from config file(s)"); cnt->imgs.width = cnt->conf.width; cnt->imgs.height = cnt->conf.height; cnt->imgs.size = cnt->conf.width * cnt->conf.height * 3 / 2; cnt->imgs.motionsize = cnt->conf.width * cnt->conf.height; cnt->imgs.type = VIDEO_PALETTE_YUV420P; } cnt->imgs.image_ring_buffer = mymalloc(cnt->imgs.size); memset(cnt->imgs.image_ring_buffer, 0x80, cnt->imgs.size); /* initialize to grey */ cnt->imgs.ref = mymalloc(cnt->imgs.size); cnt->imgs.out = mymalloc(cnt->imgs.size); cnt->imgs.image_virgin = mymalloc(cnt->imgs.size); memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */ cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize); cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize); cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(int)); cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.labels)); cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(cnt->imgs.labelsize)); cnt->imgs.timestamp = mymalloc(sizeof(time_t)); cnt->imgs.shotstamp = mymalloc(sizeof(int)); /* Allocate a buffer for temp. usage in some places */ /* Only despeckle for now... */ cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width); /* Now is a good time to init rotation data. Since vid_start has been * called, we know that we have imgs.width and imgs.height. When capturing * from a V4L device, these are copied from the corresponding conf values * in vid_start. When capturing from a netcam, they get set in netcam_start, * which is called from vid_start. * * rotate_init will set cap_width and cap_height in cnt->rotate_data. */ rotate_init(cnt); /* rotate_deinit is called in main */ /* Allow videodevice to settle in */ /* Capture first image, or we will get an alarm on start */ if (cnt->video_dev > 0) { for (i = 0; i < 10; i++) { if (vid_next(cnt, cnt->imgs.image_virgin) == 0) break; SLEEP(2,0); } /* We still cannot handle a V4L type camera not being available * during startup. We have no other option than to die */ if (i >= 10) { motion_log(LOG_ERR, 0, "Error capturing first image"); motion_log(-1, 0, "Thread finishing..."); motion_remove_pid(); exit(1); } } /* create a reference frame */ memcpy(cnt->imgs.ref, cnt->imgs.image_virgin, cnt->imgs.size);#ifndef WITHOUT_V4L#if (!defined(BSD)) /* open video loopback devices if enabled */ if (cnt->conf.vidpipe) { if (cnt->conf.setup_mode) motion_log(-1, 0, "Opening video loopback device for normal pictures"); /* vid_startpipe should get the output dimensions */ cnt->pipe = vid_startpipe(cnt->conf.vidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type); if (cnt->pipe < 0) { motion_log(LOG_ERR, 0, "Failed to open video loopback"); motion_log(-1, 0, "Thread finishing..."); motion_remove_pid(); exit(1); } } if (cnt->conf.motionvidpipe) { if (cnt->conf.setup_mode) motion_log(-1, 0, "Opening video loopback device for motion pictures"); /* vid_startpipe should get the output dimensions */ cnt->mpipe = vid_startpipe(cnt->conf.motionvidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type); if (cnt->mpipe < 0) { motion_log(LOG_ERR, 0, "Failed to open video loopback"); motion_log(-1, 0, "Thread finishing..."); motion_remove_pid(); exit(1); } }#endif /* BSD */#endif /*WITHOUT_V4L*/#ifdef HAVE_MYSQL if(cnt->conf.mysql_db) { cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL)); mysql_init(cnt->database); if (!mysql_real_connect(cnt->database, cnt->conf.mysql_host, cnt->conf.mysql_user, cnt->conf.mysql_password, cnt->conf.mysql_db, 0, NULL, 0)) { motion_log(LOG_ERR, 0, "Cannot connect to MySQL database %s on host %s with user %s", cnt->conf.mysql_db, cnt->conf.mysql_host, cnt->conf.mysql_user); motion_log(LOG_ERR, 0, "MySQL error was %s", mysql_error(cnt->database)); motion_log(-1, 0, "Thread finishing..."); motion_remove_pid(); exit(1); } }#endif /* HAVE_MYSQL */#ifdef HAVE_PGSQL if (cnt->conf.pgsql_db) { char connstring[255]; /* create the connection string. Quote the values so we can have null values (blank)*/ snprintf(connstring, 255, "dbname='%s' host='%s' user='%s' password='%s' port='%d'", cnt->conf.pgsql_db, /* dbname */ (cnt->conf.pgsql_host ? cnt->conf.pgsql_host : ""), /* host (may be blank) */ (cnt->conf.pgsql_user ? cnt->conf.pgsql_user : ""), /* user (may be blank) */ (cnt->conf.pgsql_password ? cnt->conf.pgsql_password : ""), /* password (may be blank) */ cnt->conf.pgsql_port ); cnt->database_pg = PQconnectdb(connstring); if (PQstatus(cnt->database_pg) == CONNECTION_BAD) { motion_log(LOG_ERR, 0, "Connection to PostgreSQL database '%s' failed: %s", cnt->conf.pgsql_db, PQerrorMessage(cnt->database_pg)); motion_log(-1, 0, "Thread finishing..."); motion_remove_pid(); exit(1); } }#endif /* HAVE_PGSQL */#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) /* Set the sql mask file according to the SQL config options*/ 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) */ /* Load the mask file if any */ if (cnt->conf.mask_file) { if ((picture = fopen(cnt->conf.mask_file, "r"))) { /* NOTE: The mask is expected to have the output dimensions. I.e., the mask * applies to the already rotated image, not the capture image. Thus, use * width and height from imgs. */ cnt->imgs.mask = get_pgm(picture, cnt->imgs.width, cnt->imgs.height); fclose(picture); } else { motion_log(LOG_ERR, 1, "Error opening mask file %s", cnt->conf.mask_file); /* Try to write an empty mask file to make it easier for the user to edit it */ put_fixed_mask(cnt, cnt->conf.mask_file); } if (!cnt->imgs.mask) { motion_log(LOG_ERR, 0, "Failed to read mask image. Mask feature disabled."); } else { if (cnt->conf.setup_mode) motion_log(-1, 0, "Maskfile \"%s\" loaded.",cnt->conf.mask_file); } } else cnt->imgs.mask=NULL; /* Always initialize smart_mask - someone could turn it on later... */ memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); memset(cnt->imgs.smartmask_buffer, 0, cnt->imgs.motionsize*sizeof(int)); /* Set noise level */ cnt->noise = cnt->conf.noise; /* Set threshold value */ cnt->threshold = cnt->conf.max_changes; /* Initialize webcam server if webcam port is specified to not 0 */ if (cnt->conf.webcam_port) { if ( webcam_init(cnt) == -1 ) { motion_log(LOG_ERR, 1, "Problem enabling stream server"); cnt->finish = 1; cnt->makemovie = 0; } motion_log(LOG_DEBUG, 0, "Started stream webcam server in port %d", cnt->conf.webcam_port); } /* Prevent first few frames from triggering motion... */ cnt->moved = 8;}/** * motion_loop * * Thread function for the motion handling threads. * */static void *motion_loop(void *arg){ struct context *cnt = arg; int i, j, detecting_motion = 0; time_t lastframetime = 0; int postcap = 0; int frame_buffer_size; int smartmask_ratio = 0; int smartmask_count = 20; int smartmask_lastrate = 0; int olddiffs = 0; int text_size_factor; int passflag = 0; long int *rolling_average_data; 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; unsigned char *newimg = NULL; /* Pointer to where new image is stored */ 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 */ 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; motion_init(cnt); /* Initialize the double sized characters if needed. */ if(cnt->conf.text_double) text_size_factor = 2; else text_size_factor = 1; /* 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(long int) * 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 *****/ /* 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 - 1; if (cnt->precap_nr != frame_buffer_size) { /* Only decrease if at last position in new buffer */ if (frame_buffer_size > cnt->precap_nr || frame_buffer_size == cnt->precap_cur) { unsigned char *tmp; time_t *tmp2; int *tmp3; int smallest; smallest = (cnt->precap_nr < frame_buffer_size) ? cnt->precap_nr : frame_buffer_size; tmp=mymalloc(cnt->imgs.size*(1+frame_buffer_size)); tmp2=mymalloc(sizeof(time_t)*(1+frame_buffer_size)); tmp3=mymalloc(sizeof(int)*(1+frame_buffer_size)); memcpy(tmp, cnt->imgs.image_ring_buffer, cnt->imgs.size * (1+smallest)); memcpy(tmp2, cnt->imgs.timestamp, sizeof(time_t) * (1+smallest)); memcpy(tmp3, cnt->imgs.shotstamp, sizeof(int) * (1+smallest)); free(cnt->imgs.image_ring_buffer); free(cnt->imgs.timestamp); free(cnt->imgs.shotstamp); cnt->imgs.image_ring_buffer = tmp; cnt->imgs.timestamp = tmp2; cnt->imgs.shotstamp = tmp3; cnt->precap_nr = 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 (get_image){ if (cnt->conf.minimum_frame_time) { minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; get_image = 0; } /* Store time with pre_captured image*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -