⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 motion.c

📁 video motion detection of linux base
💻 C
📖 第 1 页 / 共 5 页
字号:
/** * 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 + -