📄 motion.c
字号:
/* Draw location */ if (cnt->locate == LOCATE_ON) alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_BOTH); /* Calculate how centric motion is if configured preview center*/ if (cnt->new_img & NEWIMG_CENTER) { unsigned int distX = abs((imgs->width/2) - location->x); unsigned int distY = abs((imgs->height/2) - location->y); img->cent_dist = distX*distX + distY*distY; } /* Do things only if we have got minimum_motion_frames */ if (img->flags & IMAGE_TRIGGER) { /* Take action if this is a new event and we have a trigger image */ if (cnt->event_nr != cnt->prev_event) { /* 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 = img->timestamp; 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, img->image, NULL, NULL, &img->timestamp_tm); if (cnt->conf.setup_mode) motion_log(-1, 0, "Motion detected - starting event %d", cnt->event_nr); /* always save first motion frame as preview-shot, may be changed to an other one later */ if (cnt->new_img & (NEWIMG_FIRST | NEWIMG_BEST | NEWIMG_CENTER)) { image_save_as_preview(cnt, img); } } /* EVENT_MOTION triggers event_beep and on_motion_detected_command */ event(cnt, EVENT_MOTION, NULL, NULL, NULL, &img->timestamp_tm); } /* Limit framerate */ if (img->shot < conf->frame_limit) { /* 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 && img->shot != 1) { event(cnt, EVENT_WEBCAM, img->image, NULL, NULL, &img->timestamp_tm); } /* Save motion jpeg, if configured */ /* Output the image_out (motion) picture. */ if (conf->motion_img) { event(cnt, EVENT_IMAGEM_DETECTED, NULL, NULL, NULL, &img->timestamp_tm); } } if (cnt->track.type) { cnt->moved = track_move(cnt, dev, location, imgs, 0); }}/** * process_image_ring * * Called from 'motion_loop' to save images / send images to movie * * Parameters: * * cnt - current thread's context struct * max_images - Max number of images to process * Set to IMAGE_BUFFER_FLUSH to send/save all images in buffer */#define IMAGE_BUFFER_FLUSH ((unsigned int)-1)static void process_image_ring(struct context *cnt, unsigned int max_images){ /* we are going to send an event, in the events there is still * some code that use cnt->current_image * so set it temporary to our image */ struct image_data *saved_current_image = cnt->current_image; /* If image is flaged to be saved and not saved yet, process it */ do { /* Check if we should save/send this image, breakout if not */ if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & (IMAGE_SAVE | IMAGE_SAVED)) != IMAGE_SAVE) break; /* Set inte global cotext that we are working with this image */ cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_out]; if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot < cnt->conf.frame_limit) { /* Output the picture to jpegs and ffmpeg */ event(cnt, EVENT_IMAGE_DETECTED, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); } /* Mark the image as saved */ cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags |= IMAGE_SAVED; /* Store it as a preview image, only if it have motion */ if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) { /* Check for most significant preview-shot when output_normal=best */ if (cnt->new_img & NEWIMG_BEST) { if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].diffs > cnt->imgs.preview_image.diffs) { image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); } } /* Check for most significant preview-shot when output_normal=center */ if (cnt->new_img & NEWIMG_CENTER) { if(cnt->imgs.image_ring[cnt->imgs.image_ring_out].cent_dist < cnt->imgs.preview_image.cent_dist) { image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); } } } /* Increment to image after last sended */ if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) cnt->imgs.image_ring_out = 0; if (max_images != IMAGE_BUFFER_FLUSH) { max_images--; /* breakout if we have done max_images */ if (max_images == 0) break; } /* loop until out and in is same e.g. buffer empty */ } while (cnt->imgs.image_ring_out != cnt->imgs.image_ring_in); /* restore global context values */ cnt->current_image = saved_current_image;}/** * 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: 0 OK * -1 Fatal error, open loopback error * -2 Fatal error, open SQL database error */static int 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->currenttime_tm = mymalloc(sizeof(struct tm)); cnt->eventtime_tm = mymalloc(sizeof(struct tm)); /* Init frame time */ cnt->currenttime = time(NULL); localtime_r(&cnt->currenttime, cnt->currenttime_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; cnt->lightswitch_framecounter = 0; cnt->detecting_motion = 0; cnt->makemovie = 0; motion_log(LOG_DEBUG, 0, "Thread %d started", (unsigned long)pthread_getspecific(tls_key_threadnr)); if (!cnt->conf.filepath) cnt->conf.filepath = strdup("."); /* set the device settings */ cnt->video_dev = vid_start(cnt); /* We failed to get an initial image from a camera * So we need to guess height and width based on the config * file options. */ if (cnt->video_dev < 0) { motion_log(LOG_ERR, 0, "Could not fetch initial image from 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; } image_ring_resize(cnt, 1); /* Create a initial precapture ring buffer with 1 frame */ cnt->imgs.ref = mymalloc(cnt->imgs.size); cnt->imgs.out = mymalloc(cnt->imgs.size); memset(cnt->imgs.out, 0, cnt->imgs.size); cnt->imgs.ref_dyn = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.ref_dyn)); /* contains the moving objects of ref. frame */ cnt->imgs.image_virgin = mymalloc(cnt->imgs.size); cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize); cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize); cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.smartmask_buffer)); cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.labels)); cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(cnt->imgs.labelsize)); /* allocate buffer here for preview buffer */ cnt->imgs.preview_image.image = mymalloc(cnt->imgs.size); /* Allocate a buffer for temp. usage in some places */ /* Only despeckle & bayer2rgb24() for now for now... */ cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width * cnt->imgs.height); /* 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 */ /* Capture first image, or we will get an alarm on start */ if (cnt->video_dev > 0) { for (i = 0; i < 5; i++) { if (vid_next(cnt, cnt->imgs.image_virgin) == 0) break; SLEEP(2,0); } if (i >= 5) { memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */ draw_text(cnt->imgs.image_virgin, 10, 20, cnt->imgs.width, "Error capturing first image", cnt->conf.text_double); motion_log(LOG_ERR, 0, "Error capturing first image"); } } /* create a reference frame */ alg_update_reference_frame(cnt, RESET_REF_FRAME);#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"); return -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"); return -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)); return -2; } #if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID > 50012) my_bool my_true = TRUE; mysql_options(cnt->database,MYSQL_OPT_RECONNECT,&my_true); #endif }#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)); return -2; } }#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(cnt->imgs.smartmask_buffer)); /* 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 in port %d", cnt->conf.webcam_port); cnt->finish = 1; }else 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; /* 2 sec startup delay so FPS is calculated correct */ cnt->startup_frames = cnt->conf.frame_limit * 2; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -