Index: motion.c
===================================================================
--- motion.c	(revision 213)
+++ motion.c	(working copy)
@@ -69,6 +69,123 @@
 int restart=0;
 
 /**
+ * image_ring_resize
+ *
+ * This routine is called from motion_loop to resize the image precapture ringbuffer
+ * NOTE: This function clears all images in the old ring buffer
+ 
+ * Parameters:
+ *
+ *      cnt      Pointer to the motion context structure
+ *      new_size The new size of the ring buffer
+ *
+ * Returns:     nothing
+ */
+static void image_ring_resize(struct context *cnt, int new_size)
+{
+	/* Only resize if :
+	 * Not in an event and
+	 * decreasing at last position in new buffer
+	 * increasing at last position in old buffer 
+	 * e.g. at end of smallest buffer */
+	if (cnt->event_nr != cnt->prev_event) {
+		int smallest;
+		if (new_size < cnt->imgs.image_ring_size) { /* Decreasing */
+			smallest = new_size;
+		} else { /* Increasing */
+			smallest = cnt->imgs.image_ring_size;
+		}
+		if (cnt->imgs.image_ring_in == smallest - 1 || smallest == 0) {
+			motion_log(LOG_INFO, 0, "Resizing pre_capture buffer to %d items", new_size);
+
+			/* Create memory for new ring buffer */
+			struct image_data *tmp;
+			tmp = mymalloc(new_size * sizeof(struct image_data));
+
+			/* Copy all information from old to new 
+			 * Smallest is 0 at initial init */
+			if (smallest > 0) {
+				memcpy(tmp, cnt->imgs.image_ring, sizeof(struct image_data) * smallest);
+			}
+
+			/* In the new buffers, allocate image memory */
+			{
+				int i;
+				for(i = smallest; i < new_size; i++) {
+					tmp[i].image = mymalloc(cnt->imgs.size);
+					memset(tmp[i].image, 0x80, cnt->imgs.size);  /* initialize to grey */
+				}
+			}
+			
+			/* Free the old ring */
+			free(cnt->imgs.image_ring);
+
+			/* Point to the new ring */
+			cnt->imgs.image_ring = tmp;
+
+			cnt->imgs.image_ring_size = new_size;
+		}
+	}
+}
+
+/**
+ * image_ring_destroy
+ *
+ * This routine is called when we want to free the ring
+ *
+ * Parameters:
+ *
+ *      cnt      Pointer to the motion context structure
+ *
+ * Returns:     nothing
+ */
+static void image_ring_destroy(struct context *cnt)
+{
+	int i;
+	
+	/* Exit if don't have any ring */
+	if (cnt->imgs.image_ring == NULL)
+		return;
+
+	/* Free all image buffers */
+	for (i = 0; i <= cnt->imgs.image_ring_size; i++) {
+		free(cnt->imgs.image_ring[i].image);
+	}
+	
+	/* Free the ring */
+	free(cnt->imgs.image_ring);
+
+	cnt->imgs.image_ring = NULL;
+	cnt->imgs.image_ring_size = 0;
+}
+
+/**
+ * image_save_as_preview
+ *
+ * This routine is called when we detect motion and want to save a image in the preview buffer
+ *
+ * Parameters:
+ *
+ *      cnt      Pointer to the motion context structure
+ *      img      Pointer to the image_data structure we want to set as preview image
+ *
+ * Returns:     nothing
+ */
+static void image_save_as_preview(struct context *cnt, struct image_data *img)
+{
+	void * image;
+	/* Save preview image pointer */
+	image = cnt->imgs.preview_image.image;
+	/* Copy all info */
+	memcpy(&cnt->imgs.preview_image.image, img, sizeof(struct image_data));
+	/* restore image pointer */
+	cnt->imgs.preview_image.image = image;
+
+	/* Copy image */
+	memcpy(cnt->imgs.preview_image.image, img->image, cnt->imgs.size);
+}
+
+/**
  * context_init
  *
  *   Initializes a context struct with the default values for all the
@@ -124,8 +241,6 @@
 		free(cnt->imgs.ref);
 	if (cnt->imgs.image_virgin)
 		free(cnt->imgs.image_virgin);
-	if (cnt->imgs.image_ring_buffer)
-		free(cnt->imgs.image_ring_buffer);
 	if (cnt->imgs.labels)
 		free(cnt->imgs.labels);
 	if (cnt->imgs.labelsize)
@@ -138,14 +253,11 @@
 		free(cnt->imgs.smartmask_buffer);
 	if (cnt->imgs.common_buffer)
 		free(cnt->imgs.common_buffer);
-	if (cnt->imgs.timestamp_ring_buffer)
-		free(cnt->imgs.timestamp_ring_buffer);
-	if (cnt->imgs.shotstamp_ring_buffer)
-		free(cnt->imgs.shotstamp_ring_buffer);
-	if (cnt->imgs.diffs_ring_buffer)
-		free(cnt->imgs.diffs_ring_buffer);
-	if (cnt->imgs.preview_buffer)
-		free(cnt->imgs.preview_buffer);
+	if (cnt->imgs.preview_image.image)
+		free(cnt->imgs.preview_image.image);
+
+	image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */
+
 	rotate_deinit(cnt); /* cleanup image rotation data */
 
 	if(cnt->pipe != -1)
@@ -269,8 +381,7 @@
 /**
  * motion_detected
  *
- *   Called from 'motion_loop' when motion is detected, or when to act as if
- *   motion was detected (e.g. in post capture).
+ *   Called from 'motion_loop' when motion is detected
  *
  * Parameters:
  *
@@ -280,33 +391,31 @@
  *   dev      - video device file descriptor
  *   devpipe  - file descriptor of still image pipe
  *   devmpipe - file descriptor of motion pipe
- *   newimg   - pointer to the newly captured image
+ *   img      - pointer to the captured image with detected motion
  */
-static void motion_detected(struct context *cnt, int diffs, int dev, unsigned char *newimg)
+static void motion_detected(struct context *cnt, int dev, struct image_data *img)
 {
 	struct config *conf = &cnt->conf;
 	struct images *imgs = &cnt->imgs;
 	struct coord *location = &cnt->location;
 
-	/* sanity check */
-	if (newimg != cnt->imgs.image_ring_buffer + (cnt->imgs.size * cnt->imgs.ring_buffer_last_in)) {
-		motion_log(LOG_ERR, 0, "Error in %s: newimg is not at last_in pos in ringbuffer", __FUNCTION__);
-		motion_log(-1, 0, "Thread finishing...");
-		motion_remove_pid();
-		exit(1);
+	cnt->lasttime = img->timestamp;
+
+	/* Calculate how centric motion is if configured preview center*/
+	if (cnt->new_img & NEWIMG_CENTER) {
+		unsigned int distX = abs((cnt->imgs.width/2) - location->x);
+		unsigned int distY = abs((cnt->imgs.height/2) - location->y);
+
+		img->cent_dist = distX*distX + distY*distY;
 	}
 
-	cnt->lasttime = cnt->currenttime;
-
 	/* Take action if this is a new event */
 	if (cnt->event_nr != cnt->prev_event) {
-		cnt->preview_max = 0;
-
 		/* 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 = cnt->currenttime;
+		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
@@ -317,79 +426,54 @@
 		           cnt->conf.text_event, cnt->eventtime_tm, NULL, 0);
 
 		/* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */
-		event(cnt, EVENT_FIRSTMOTION, newimg, NULL, NULL, cnt->currenttime_tm);
+		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 */
+		/* 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)) {
-			unsigned int distX = abs((cnt->imgs.width/2) - location->x);
-			unsigned int distY = abs((cnt->imgs.height/2) - location->y);
-
-			memcpy(cnt->imgs.preview_buffer, newimg, cnt->imgs.size);
-			cnt->preview_max = diffs;
-			cnt->preview_cent_dist = distX*distX + distY*distY;
+			image_save_as_preview(cnt, img);
 			cnt->preview_time = cnt->currenttime;
 			cnt->preview_shots = cnt->shots;
 			
 			/* We haven't yet draw the location on the image, do it if configured */
 			if (cnt->locate == LOCATE_PREVIEW || cnt->locate == LOCATE_ON) {
-				alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_buffer, LOCATE_NORMAL);
+				alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_image.image, LOCATE_NORMAL);
 			}
 		}
 	}
 
-	/* motion_detected is called with diffs = 0 during post_capture
-	 * and if cnt->conf.output_all is enabled. We only want to draw location
-	 * and call EVENT_MOTION when it is a picture frame with actual motion detected.
-	 */
-	if (diffs) {
-		if (cnt->locate == LOCATE_ON)
-			alg_draw_location(location, imgs, imgs->width, newimg, LOCATE_BOTH);
+	/* Draw location and call EVENT_MOTION */
+	if (cnt->locate == LOCATE_ON)
+		alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_BOTH);
 
-		/* EVENT_MOTION triggers event_beep and on_motion_detected_command */
-		event(cnt, EVENT_MOTION, NULL, NULL, NULL, cnt->currenttime_tm);
-		cnt->postcap = cnt->conf.post_capture;
-	}
-	else {
-		if (cnt->postcap > 0)
-			cnt->postcap--;
-	}
+	/* EVENT_MOTION triggers event_beep and on_motion_detected_command */
+		event(cnt, EVENT_MOTION, NULL, NULL, NULL, &img->timestamp_tm);
 
+	/* Setup the postcap counter */
+	cnt->postcap = cnt->conf.post_capture;
+
 	/* Check for most significant preview-shot when output_normal=best */
-	if ((cnt->new_img & NEWIMG_BEST) && (diffs > cnt->preview_max)) {
-		unsigned int distX = abs((cnt->imgs.width/2) - location->x);
-		unsigned int distY = abs((cnt->imgs.height/2) - location->y);
-
-		memcpy(cnt->imgs.preview_buffer, newimg, cnt->imgs.size);
-		cnt->preview_max = diffs;
-		cnt->preview_cent_dist = distX*distX + distY*distY;
+	if ((cnt->new_img & NEWIMG_BEST) && (img->diffs > cnt->imgs.preview_image.diffs)) {
+		image_save_as_preview(cnt, img);
 		cnt->preview_time = cnt->currenttime;
 		cnt->preview_shots = cnt->shots;
 
 		if (cnt->locate == LOCATE_PREVIEW) {
-			alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_buffer, LOCATE_NORMAL);
+			alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_image.image, LOCATE_NORMAL);
 		}
 	}
 	/* Check for most significant preview-shot when output_normal=center */
-	if ((cnt->new_img & NEWIMG_CENTER) && (diffs > 0)) {
-		unsigned long distance;
-		unsigned int distX = abs((cnt->imgs.width/2) - location->x);
-		unsigned int distY = abs((cnt->imgs.height/2) - location->y);
-
-		distance = distX*distX + distY*distY;
-		if(distance < cnt->preview_cent_dist) {
-			memcpy(cnt->imgs.preview_buffer, newimg, cnt->imgs.size);
-			cnt->preview_max = diffs;
-			cnt->preview_cent_dist = distance;
+	if (cnt->new_img & NEWIMG_CENTER) {
+		if(img->cent_dist < cnt->imgs.preview_image.cent_dist) {
+			image_save_as_preview(cnt, img);
 			cnt->preview_time = cnt->currenttime;
 			cnt->preview_shots = cnt->shots;
 
 			if (cnt->locate == LOCATE_PREVIEW) {
-				alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_buffer, LOCATE_NORMAL);
-			}
-		
+				alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_image.image, LOCATE_NORMAL);
+			}		
 		}
 	}
 
@@ -401,60 +485,76 @@
 		 * We also disable this in setup_mode.
 		 */
 		if (conf->webcam_motion && !conf->setup_mode && cnt->shots != 1) {
-			event(cnt, EVENT_WEBCAM, newimg, NULL, NULL, cnt->currenttime_tm);
+			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, cnt->currenttime_tm);
+			event(cnt, EVENT_IMAGEM_DETECTED, NULL, NULL, NULL, &img->timestamp_tm);
 		}
 	}
 
-	/* Send some images from ring buffer to jpegs and ffmpeg */
+	if (cnt->track.type != 0)	{
+		cnt->moved = track_move(cnt, dev, &cnt->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)
+void process_image_ring(struct context *cnt, 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 preview image */
+	struct image_data *saved_current_image = cnt->current_image;
+
+	/* If image is flaged to be saved and not saved yet, process it */
+	while((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & (IMAGE_SAVE | IMAGE_SAVED)) == IMAGE_SAVE)
 	{
-		int tmpshots;
-		struct tm tmptime;
-		int frames_handled = 0;
+		/* Set inte global cotext that we are working with this image */
+		cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_out];
 
-		/* pre_capture frames are written as jpegs and to the ffmpeg film
-		 * We store the current cnt->shots temporarily until we are done with
-		 * the ring_buffer stuff
+		/* Mark the image as saved */
+		cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags |= IMAGE_SAVED;
+
+		cnt->shots = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot;
+		if (cnt->shots < 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);
+		}
+
+		/* Increment out pointer */
+		/* Does we point to same as in? e.g. buffer empty */
+		if (cnt->imgs.image_ring_out != 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;
+		}
+
+		if (max_images != IMAGE_BUFFER_FLUSH) {
+			max_images--;
+		}
+		/* breakout if we have done max_images images,
 		 */
-		tmpshots = cnt->shots;
-		/* We should always take at least one. 
-		 * If we have minimum_motion_frames==1 and pre_capture==0, we have a ring buffer of 1 element
-		 * and then last_out always equals last_in.
-		 */
-		do {
-			/* If we are at end in ring buffer don't increment out */
-			if (cnt->imgs.ring_buffer_last_out != cnt->imgs.ring_buffer_last_in) {
-				cnt->imgs.ring_buffer_last_out++;
-				if (cnt->imgs.ring_buffer_last_out >= cnt->imgs.ring_buffer_size)
-					cnt->imgs.ring_buffer_last_out = 0;
-			}
-			localtime_r(&cnt->imgs.timestamp_ring_buffer[cnt->imgs.ring_buffer_last_out], &tmptime);
-			cnt->shots = cnt->imgs.shotstamp_ring_buffer[cnt->imgs.ring_buffer_last_out] & 0x0fff;
-			if (cnt->shots < conf->frame_limit) {
-				/* Output the picture to jpegs and ffmpeg */
-				event(cnt, EVENT_IMAGE_DETECTED,
-				      cnt->imgs.image_ring_buffer + (cnt->imgs.size * cnt->imgs.ring_buffer_last_out), NULL, NULL, &tmptime);
-			}
-			frames_handled++;
-			/* breakout if we have done some images (>=2), 
-			 * but if we are at end of postcap send 
-			 * everything left in ring_buffer 
-			 */
-			if (frames_handled >= 2 && !(cnt->postcap == 0))
-				break;
-		} while (cnt->imgs.ring_buffer_last_out != cnt->imgs.ring_buffer_last_in);
-		cnt->shots = tmpshots;
+		if (max_images == 0)
+			break;
 	}
 
-	if (cnt->track.type != 0 && diffs != 0)	{
-		cnt->moved = track_move(cnt, dev, &cnt->location, imgs, 0);
-	}
+	/* restore global context values */
+	cnt->current_image = saved_current_image;
 }
 
 /**
@@ -477,7 +577,6 @@
 	/* 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;
@@ -519,8 +618,8 @@
 		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 */
+	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);
 	cnt->imgs.image_virgin = mymalloc(cnt->imgs.size);
@@ -530,13 +629,9 @@
 	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_ring_buffer = mymalloc(sizeof(cnt->imgs.timestamp_ring_buffer[0]));
-	cnt->imgs.shotstamp_ring_buffer = mymalloc(sizeof(cnt->imgs.shotstamp_ring_buffer[0]));
-	cnt->imgs.diffs_ring_buffer = mymalloc(sizeof(cnt->imgs.diffs_ring_buffer[0]));
-	cnt->imgs.ring_buffer_size = 1;
 
 	/* allocate buffer here for preview buffer */
-	cnt->imgs.preview_buffer = mymalloc(cnt->imgs.size);
+	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... */
@@ -733,7 +828,6 @@
 	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 */
@@ -796,41 +890,8 @@
 		 * 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.ring_buffer_size != frame_buffer_size) {
-			/* Only resize if :
-			 * decreasing at last position in new buffer
-			 * increasing at last position in old buffer 
-			 * e.g. at end of smallest buffer */
-			int smallest;
-			if (frame_buffer_size < cnt->imgs.ring_buffer_size) { /* Decreasing */
-				smallest = frame_buffer_size;
-			}
-			else { /* Increasing */
-				smallest = cnt->imgs.ring_buffer_size;
-			}
-			if (cnt->imgs.ring_buffer_last_in == smallest - 1) {
-				void *tmp;
-				void *tmp2;
-				void *tmp3;
-				void *tmp4;
-				tmp = mymalloc(cnt->imgs.size * frame_buffer_size);
-				tmp2 = mymalloc(sizeof(cnt->imgs.timestamp_ring_buffer[0]) * frame_buffer_size);
-				tmp3 = mymalloc(sizeof(cnt->imgs.shotstamp_ring_buffer[0]) * frame_buffer_size);
-				tmp4 = mymalloc(sizeof(cnt->imgs.diffs_ring_buffer[0]) * frame_buffer_size);
-				memcpy(tmp, cnt->imgs.image_ring_buffer, cnt->imgs.size * smallest);
-				memcpy(tmp2, cnt->imgs.timestamp_ring_buffer, sizeof(cnt->imgs.timestamp_ring_buffer[0]) * smallest);
-				memcpy(tmp3, cnt->imgs.shotstamp_ring_buffer, sizeof(cnt->imgs.shotstamp_ring_buffer[0]) * smallest);
-				memcpy(tmp4, cnt->imgs.diffs_ring_buffer, sizeof(cnt->imgs.diffs_ring_buffer[0]) * smallest);
-				free(cnt->imgs.image_ring_buffer);
-				free(cnt->imgs.timestamp_ring_buffer);
-				free(cnt->imgs.shotstamp_ring_buffer);
-				free(cnt->imgs.diffs_ring_buffer);
-				cnt->imgs.image_ring_buffer = tmp;
-				cnt->imgs.timestamp_ring_buffer = tmp2;
-				cnt->imgs.shotstamp_ring_buffer = tmp3;
-				cnt->imgs.diffs_ring_buffer = tmp4;
-				cnt->imgs.ring_buffer_size = frame_buffer_size;
-			}
+		if (cnt->imgs.image_ring_size != frame_buffer_size) {
+			image_ring_resize(cnt, frame_buffer_size);
 		}
 
 		/* Get time for current frame */
@@ -869,28 +930,36 @@
 				get_image = 0;
 			}
 
-			/* ring_buffer_last_in is pointing to current pos, update before put in a new image */
-			if (++cnt->imgs.ring_buffer_last_in >= cnt->imgs.ring_buffer_size)
-				cnt->imgs.ring_buffer_last_in = 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.ring_buffer_last_in == cnt->imgs.ring_buffer_last_out) {
-				if (++cnt->imgs.ring_buffer_last_out >= cnt->imgs.ring_buffer_size)
-					cnt->imgs.ring_buffer_last_out = 0;
+			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;
 			}
 
-			/* Store time with pre_captured image */
-			cnt->imgs.timestamp_ring_buffer[cnt->imgs.ring_buffer_last_in] = cnt->currenttime;
+			/* 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];
 
-			/* Store shot number with pre_captured image */
-			cnt->imgs.shotstamp_ring_buffer[cnt->imgs.ring_buffer_last_in] = cnt->shots;
+			/* 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);
 
-			/* set diffs to 0 now, will be written after we calculated diffs in new image */
-			cnt->imgs.diffs_ring_buffer[cnt->imgs.ring_buffer_last_in] = 0;
+				/* Store shot number with pre_captured image */
+				cnt->current_image->shot = cnt->shots;
 
-			/* newimg points to position in ring where to store image */
-			newimg = cnt->imgs.image_ring_buffer + (cnt->imgs.size * cnt->imgs.ring_buffer_last_in);
+				/* 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;
+			}
+
 		/***** MOTION LOOP - RETRY INITIALIZING NETCAM SECTION *****/
 			/* If a network camera is not available we keep on retrying every 10 seconds
 			 * until it shows up.
@@ -927,7 +996,7 @@
 			 * <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
 			 */
-			vid_return_code = vid_next(cnt, newimg);
+			vid_return_code = vid_next(cnt, cnt->current_image->image);
 
 			// VALID PICTURE
 			if (vid_return_code == 0) {
@@ -944,14 +1013,14 @@
 #ifdef HAVE_FFMPEG
 				/* Deinterlace the image with ffmpeg, before the image is modified. */
 				if(cnt->conf.ffmpeg_deinterlace) {
-					ffmpeg_deinterlace(newimg, cnt->imgs.width, cnt->imgs.height);
+					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, newimg, cnt->imgs.size);
+				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.
@@ -1004,16 +1073,16 @@
 				 */
 				if (cnt->video_dev != -1 &&
 				    ++cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit)) {
-					memcpy(newimg, cnt->imgs.image_virgin, cnt->imgs.size);
+					memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size);
 
 				} else {
 					char tmpout[80];
 					char tmpin[] = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T";
 					struct tm tmptime;
 					localtime_r(&cnt->connectionlosttime, &tmptime);
-					memset(newimg, 0x80, cnt->imgs.size);
+					memset(cnt->current_image->image, 0x80, cnt->imgs.size);
 					mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tmptime, NULL, 0);
-					draw_text(newimg, 10, 20 * text_size_factor, cnt->imgs.width,
+					draw_text(cnt->current_image->image, 10, 20 * text_size_factor, cnt->imgs.width,
 					          tmpout, cnt->conf.text_double);
 
 					/* Write error message only once */
@@ -1042,9 +1111,9 @@
 				 * anyway
 				 */
 				if (detecting_motion || cnt->conf.setup_mode)
-					cnt->diffs = alg_diff_standard(cnt, cnt->imgs.image_virgin);
+					cnt->current_image->diffs = alg_diff_standard(cnt, cnt->imgs.image_virgin);
 				else
-					cnt->diffs = alg_diff(cnt, cnt->imgs.image_virgin);
+					cnt->current_image->diffs = alg_diff(cnt, cnt->imgs.image_virgin);
 
 				/* Lightswitch feature - has light intensity changed?
 				 * This can happen due to change of light conditions or due to a sudden change of the camera
@@ -1052,12 +1121,12 @@
 				 * 5 frames to allow the camera to settle.
 				 */
 				if (cnt->conf.lightswitch) {
-					if (alg_lightswitch(cnt, cnt->diffs)) {
+					if (alg_lightswitch(cnt, cnt->current_image->diffs)) {
 						if (cnt->conf.setup_mode)
 							motion_log(-1, 0, "Lightswitch detected");
 						if (cnt->moved < 5)
 							cnt->moved = 5;
-						cnt->diffs = 0;
+						cnt->current_image->diffs = 0;
 					}
 				}
 
@@ -1069,10 +1138,10 @@
 				 * We do not suspend motion detection like we did for lightswitch
 				 * because with Round Robin this is controlled by roundrobin_skip.
 				 */
-				if (cnt->conf.switchfilter && cnt->diffs > cnt->threshold) {
-					cnt->diffs = alg_switchfilter(cnt, cnt->diffs, newimg);
-					if (cnt->diffs <= cnt->threshold) {
-						cnt->diffs = 0;
+				if (cnt->conf.switchfilter && cnt->current_image->diffs > cnt->threshold) {
+					cnt->current_image->diffs = alg_switchfilter(cnt, cnt->current_image->diffs, cnt->current_image->image);
+					if (cnt->current_image->diffs <= cnt->threshold) {
+						cnt->current_image->diffs = 0;
 						if (cnt->conf.setup_mode)
 							motion_log(-1, 0, "Switchfilter detected");
 					}
@@ -1087,14 +1156,14 @@
 				cnt->imgs.total_labels = 0;
 				cnt->imgs.largest_label = 0;
 				olddiffs = 0;
-				if (cnt->conf.despeckle && cnt->diffs > 0) {
-					olddiffs = cnt->diffs;
-					cnt->diffs = alg_despeckle(cnt, olddiffs);
+				if (cnt->conf.despeckle && cnt->current_image->diffs > 0) {
+					olddiffs = cnt->current_image->diffs;
+					cnt->current_image->diffs = alg_despeckle(cnt, olddiffs);
 				}else if (cnt->imgs.labelsize_max)
 					cnt->imgs.labelsize_max = 0; /* Disable labeling if enabled */
 
 			} else if (!cnt->conf.setup_mode)
-				cnt->diffs = 0;
+				cnt->current_image->diffs = 0;
 
 			/* Manipulate smart_mask sensitivity (only every smartmask_ratio seconds) */
 			if (cnt->smartmask_speed) {
@@ -1114,19 +1183,16 @@
 			 */
 			if (cnt->moved) {
 				cnt->moved--;
-				cnt->diffs = 0;
+				cnt->current_image->diffs = 0;
 			}
 
-			/* save diffs in ring buffer */
-			cnt->imgs.diffs_ring_buffer[cnt->imgs.ring_buffer_last_in] = cnt->diffs;
-
 		/***** MOTION LOOP - TUNING SECTION *****/
 
 			/* if noise tuning was selected, do it now. but only when
 			 * no frames have been recorded and only once per second
 			 */
 			if (cnt->conf.noise_tune && cnt->shots == 0) {
-				if (!detecting_motion && (cnt->diffs <= cnt->threshold))
+				if (!detecting_motion && (cnt->current_image->diffs <= cnt->threshold))
 					alg_noise_tune(cnt, cnt->imgs.image_virgin);
 			}
 
@@ -1141,7 +1207,7 @@
 			 * changes of threshold are used.
 			 */
 			if (cnt->conf.threshold_tune)
-				alg_threshold_tune(cnt, cnt->diffs, detecting_motion);
+				alg_threshold_tune(cnt, cnt->current_image->diffs, detecting_motion);
 			else
 				cnt->threshold = cnt->conf.max_changes;
 
@@ -1162,10 +1228,10 @@
 			if (cnt->imgs.largest_label && (cnt->conf.motion_img || cnt->conf.ffmpeg_cap_motion || cnt->conf.setup_mode) )
 				overlay_largest_label(cnt, cnt->imgs.out);
 
-			/* If motion is detected (cnt->diffs > cnt->threshold) and before we add text to the pictures
+			/* If motion is detected (cnt->current_image->diffs > cnt->threshold) and before we add text to the pictures
 			   we find the center and size coordinates of the motion to be used for text overlays and later
 			   for adding the locate rectangle */
-			if (cnt->diffs > cnt->threshold)
+			if (cnt->current_image->diffs > cnt->threshold)
 				 alg_locate_center_size(&cnt->imgs, cnt->imgs.width, cnt->imgs.height, &cnt->location);
 
 			/* Fixed mask overlay */
@@ -1186,18 +1252,18 @@
 				char tmp[15];
 
 				if (!cnt->pause)
-					sprintf(tmp, "%d", cnt->diffs);
+					sprintf(tmp, "%d", cnt->current_image->diffs);
 				else
 					sprintf(tmp, "-");
 
-				draw_text(newimg, cnt->imgs.width - 10, 10, cnt->imgs.width, tmp, cnt->conf.text_double);
+				draw_text(cnt->current_image->image, cnt->imgs.width - 10, 10, cnt->imgs.width, tmp, cnt->conf.text_double);
 			}
 
 			/* Add changed pixels to motion-images (for webcam) in setup_mode
 			   and always overlay smartmask (not only when motion is detected) */
 			if (cnt->conf.setup_mode) {
 				char tmp[PATH_MAX];
-				sprintf(tmp, "D:%5d L:%3d N:%3d", cnt->diffs, cnt->imgs.total_labels, cnt->noise);
+				sprintf(tmp, "D:%5d L:%3d N:%3d", cnt->current_image->diffs, cnt->imgs.total_labels, cnt->noise);
 				draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 30 * text_size_factor,
 				          cnt->imgs.width, tmp, cnt->conf.text_double);
 				sprintf(tmp, "THREAD %d SETUP", cnt->threadnr);
@@ -1208,16 +1274,16 @@
 			/* Add text in lower left corner of the pictures */
 			if (cnt->conf.text_left) {
 				char tmp[PATH_MAX];
-				mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_left, cnt->currenttime_tm, NULL, 0);
-				draw_text(newimg, 10, cnt->imgs.height - 10 * text_size_factor, cnt->imgs.width,
+				mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_left, &cnt->current_image->timestamp_tm, NULL, 0);
+				draw_text(cnt->current_image->image, 10, cnt->imgs.height - 10 * text_size_factor, cnt->imgs.width,
 				          tmp, cnt->conf.text_double);
 			}
 
 			/* Add text in lower right corner of the pictures */
 			if (cnt->conf.text_right) {
 				char tmp[PATH_MAX];
-				mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_right, cnt->currenttime_tm, NULL, 0);
-				draw_text(newimg, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor,
+				mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_right, &cnt->current_image->timestamp_tm, NULL, 0);
+				draw_text(cnt->current_image->image, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor,
 				          cnt->imgs.width, tmp, cnt->conf.text_double);
 			}
 
@@ -1232,24 +1298,51 @@
 			 */
 			if (cnt->conf.output_all) {
 				detecting_motion = 1;
-				motion_detected(cnt, 0, cnt->video_dev, newimg);
-			} else if (cnt->diffs > cnt->threshold) {
+				cnt->current_image->flags |= IMAGE_SAVE;
+			} else if (cnt->current_image->diffs > cnt->threshold) {
+				/* flag this image, it have motion */
+				cnt->current_image->flags |= IMAGE_MOTION;
 				/* Did we detect motion (like the cat just walked in :) )?
 				 * If so, ensure the motion is sustained if minimum_motion_frames
-				 * is set, and take action by calling motion_detected().
-				 * pre_capture is handled by motion_detected(), and we handle
-				 * post_capture here. */
-				if (!detecting_motion)
-					detecting_motion = 1;
+				 * is set, and take action by calling motion_detected(). */
 
-				detecting_motion++;
+				/* Count how many frames with motion there is in the last minimum_motion_frames in precap buffer */
+				int frame_count = 0;
+				int i;
+				int pos = cnt->imgs.image_ring_in;
+				for(i = 0; i < cnt->conf.minimum_motion_frames; i++)
+				{
+					if(cnt->imgs.image_ring[pos].flags & IMAGE_MOTION)
+						frame_count++;
+					if (pos == 0) {
+						pos = cnt->imgs.image_ring_size-1;
+					} else {
+						pos--;
+					}
+				}
 
-				if (detecting_motion > cnt->conf.minimum_motion_frames) {
-					motion_detected(cnt, cnt->diffs, cnt->video_dev, newimg);
+				if (frame_count >= cnt->conf.minimum_motion_frames) {
+					int i;
+					cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE);
+					detecting_motion = 1;
+					/* Mark all images in image_ring to be saved */
+					for(i = 0; i < cnt->imgs.image_ring_size; i++) {
+						cnt->imgs.image_ring[i].flags |= IMAGE_SAVE;
+					}
+					motion_detected(cnt, cnt->video_dev, cnt->current_image);
 				}
+				else if (cnt->postcap) { /* we have motion in this frame, but not enought frames for trigger. Check postcap */
+					cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE);
+					cnt->postcap--;
+				} else {
+					cnt->current_image->flags |= IMAGE_PRECAP;
+				}
+
 			} else if (cnt->postcap) {
-				motion_detected(cnt, 0, cnt->video_dev, newimg);
+				cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE);
+				cnt->postcap--;
 			} else {
+				cnt->current_image->flags |= IMAGE_PRECAP;
 				detecting_motion = 0;
 			}
 
@@ -1266,10 +1359,13 @@
 			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->preview_max) {
+					if (cnt->imgs.preview_image.diffs) {
 						preview_save(cnt);
-						cnt->preview_max = 0;
+						cnt->imgs.preview_image.diffs = 0;
 					}
 
 					event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, cnt->currenttime_tm);
@@ -1295,11 +1391,13 @@
 				}
 			}
 
+			/* Save/send to movie some images */
+			process_image_ring(cnt, 2);
 
 		/***** MOTION LOOP - REFERENCE FRAME SECTION *****/
 
 			/* Update reference frame */
-			if ((cnt->diffs > cnt->threshold * 2) ||
+			if ((cnt->current_image->diffs > cnt->threshold * 2) ||
 			    (cnt->moved && (cnt->track.type || cnt->conf.lightswitch))) {
 				/* Prevent the motion created by moving camera or sudden light intensity
 				 * being detected by creating a fresh reference frame. Decaying is also
@@ -1330,7 +1428,7 @@
 
 				if (cnt->conf.despeckle) {
 					snprintf(part, 99, "Raw changes: %5d - changes after '%s': %5d",
-					         olddiffs, cnt->conf.despeckle, cnt->diffs);
+					         olddiffs, cnt->conf.despeckle, cnt->current_image->diffs);
 					strcat(msg, part);
 					if (strchr(cnt->conf.despeckle, 'l')){
 						sprintf(part, " - labels: %3d", cnt->imgs.total_labels);
@@ -1338,7 +1436,7 @@
 					}
 				}
 				else{
-					sprintf(part, "Changes: %5d", cnt->diffs);
+					sprintf(part, "Changes: %5d", cnt->current_image->diffs);
 					strcat(msg, part);
 				}
 				if (cnt->conf.noise_tune){
@@ -1370,7 +1468,7 @@
 		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, newimg, NULL, NULL, cnt->currenttime_tm);
+			event(cnt, EVENT_IMAGE_SNAPSHOT, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm);
 			cnt->snapshot = 0;
 		}
 
@@ -1387,7 +1485,7 @@
 			 * 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->currenttime_tm->tm_min == 0 &&
+			if (cnt->current_image->timestamp_tm.tm_min == 0 &&
 			    (time_current_frame % 60 < time_last_frame % 60) &&
 			    cnt->shots == 0) {
 
@@ -1396,31 +1494,31 @@
 
 				/* If we are daily, raise timelapseend event at midnight */
 				else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) {
-					if (cnt->currenttime_tm->tm_hour == 0)
-						event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);
+					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->currenttime_tm);
+					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->currenttime_tm->tm_wday == 0 && cnt->currenttime_tm->tm_hour == 0)
-						event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);
+					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->currenttime_tm->tm_wday == 1 && cnt->currenttime_tm->tm_hour == 0)
-						event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);
+					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->currenttime_tm->tm_mday == 1 && cnt->currenttime_tm->tm_hour == 0)
-						event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);
+					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 */
@@ -1437,7 +1535,7 @@
 			 */
 			if (cnt->shots == 0 &&
 				time_current_frame % cnt->conf.timelapse <= time_last_frame % cnt->conf.timelapse)
-				event(cnt, EVENT_TIMELAPSE, newimg, NULL, NULL, cnt->currenttime_tm);
+				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
@@ -1468,9 +1566,9 @@
 			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, newimg, NULL, &cnt->pipe, cnt->currenttime_tm);
+			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, newimg, NULL, NULL, cnt->currenttime_tm);
+				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);
@@ -1488,6 +1586,12 @@
 				cnt->new_img = NEWIMG_BEST;
 			else if (strcasecmp(cnt->conf.output_normal, "center") == 0)
 				cnt->new_img = NEWIMG_CENTER;
+			else if (strcasecmp(cnt->conf.output_normal, "on_and_first") == 0)
+				cnt->new_img = NEWIMG_FIRST | NEWIMG_ON;
+			else if (strcasecmp(cnt->conf.output_normal, "on_and_best") == 0)
+				cnt->new_img = NEWIMG_BEST | NEWIMG_ON;
+			else if (strcasecmp(cnt->conf.output_normal, "on_and_center") == 0)
+				cnt->new_img = NEWIMG_CENTER | NEWIMG_ON;
 			else
 				cnt->new_img = NEWIMG_OFF;
 
@@ -2271,11 +2375,11 @@
 					break;
 
 				case 'q': // shots
-					sprintf(tempstr, "%02d", cnt->shots);
+					sprintf(tempstr, "%02d", cnt->current_image->shot);
 					break;
 
 				case 'D': // diffs
-					sprintf(tempstr, "%d", cnt->diffs);
+					sprintf(tempstr, "%d", cnt->current_image->diffs);
 					break;
 
 				case 'N': // noise
Index: motion.h
===================================================================
--- motion.h	(revision 213)
+++ motion.h	(working copy)
@@ -1,361 +1,383 @@
-/*	motion.h
- *
- *	Include file for motion.c
- *      Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
- *      This software is distributed under the GNU public license version 2
- *      See also the file 'COPYING'.
- *
- */
-
-#ifndef _INCLUDE_MOTION_H
-#define _INCLUDE_MOTION_H
-
-/* Includes */
-#ifdef HAVE_MYSQL
-#include <mysql.h>
-#endif
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#define __USE_GNU
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <signal.h>
-#include <syslog.h>
-#include <limits.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-
-#define _LINUX_TIME_H 1
-#if (!defined(WITHOUT_V4L)) && (!defined(BSD))
-#include <linux/videodev.h>
-#endif
-
-#include <pthread.h>
-
-#ifdef HAVE_PGSQL
-#include <libpq-fe.h>
-#endif
-
-#include "conf.h"
-#include "webcam.h"
-#include "webhttpd.h"
-
-/**
- * ATTRIBUTE_UNUSED:
- *
- * Macro used to signal to GCC unused function parameters
- */
-#ifdef __GNUC__
-#ifdef HAVE_ANSIDECL_H
-#include <ansidecl.h>
-#endif
-#ifndef ATTRIBUTE_UNUSED
-#define ATTRIBUTE_UNUSED __attribute__((unused))
-#endif
-#else
-#define ATTRIBUTE_UNUSED
-#endif
-
-/* The macro below defines a version of sleep using nanosleep
- * If a signal such as SIG_CHLD interrupts the sleep we just continue sleeping
- */
-#define SLEEP(seconds, nanoseconds) {              \
-                struct timespec tv;                \
-                tv.tv_sec = (seconds);             \
-                tv.tv_nsec = (nanoseconds);        \
-                while (nanosleep(&tv, &tv) == -1); \
-        } 
-
-
-#if defined(WITHOUT_V4L) || defined(BSD)
- 
-#define VIDEO_PALETTE_GREY      1       /* Linear greyscale */
-#define VIDEO_PALETTE_HI240     2       /* High 240 cube (BT848) */
-#define VIDEO_PALETTE_RGB565    3       /* 565 16 bit RGB */
-#define VIDEO_PALETTE_RGB24     4       /* 24bit RGB */
-#define VIDEO_PALETTE_RGB32     5       /* 32bit RGB */
-#define VIDEO_PALETTE_RGB555    6       /* 555 15bit RGB */
-#define VIDEO_PALETTE_YUV422    7       /* YUV422 capture */
-#define VIDEO_PALETTE_YUYV      8
-#define VIDEO_PALETTE_UYVY      9       /* The great thing about standards is ... */
-#define VIDEO_PALETTE_YUV420    10
-#define VIDEO_PALETTE_YUV411    11      /* YUV411 capture */
-#define VIDEO_PALETTE_RAW       12      /* RAW capture (BT848) */
-#define VIDEO_PALETTE_YUV422P   13      /* YUV 4:2:2 Planar */
-#define VIDEO_PALETTE_YUV411P   14      /* YUV 4:1:1 Planar */
-#define VIDEO_PALETTE_YUV420P   15      /* YUV 4:2:0 Planar */
-#define VIDEO_PALETTE_YUV410P   16      /* YUV 4:1:0 Planar */
-#define VIDEO_PALETTE_PLANAR    13      /* start of planar entries */
-#define VIDEO_PALETTE_COMPONENT 7       /* start of component entries */
-#endif
-
-
-/* Debug levels */
-#define CAMERA_WARNINGS         3   /* warnings only */
-#define CAMERA_INFO             5   /* debug level to activate everything */
-
-/* Default picture settings */
-#define DEF_WIDTH              352
-#define DEF_HEIGHT             288
-#define DEF_QUALITY             75
-#define DEF_CHANGES           1500
-
-#define DEF_MAXFRAMERATE       100
-#define DEF_NOISELEVEL          32
-
-/* Minimum time between two 'actions' (email, sms, external) */
-#define DEF_GAP                 60  /* 1 minutes */
-#define DEF_MAXMPEGTIME       3600  /* 60 minutes */
-
-#define DEF_FFMPEG_BPS      400000
-#define DEF_FFMPEG_VBR           0
-#define DEF_FFMPEG_CODEC   "mpeg4"
-
-#define THRESHOLD_TUNE_LENGTH  256
-
-#define MISSING_FRAMES_TIMEOUT  30  /* When failing to get picture frame from camera
-                                     * we reuse the previous frame until
-                                     * MISSING_FRAMES_TIMEOUT seconds has passed
-                                     * and then we show a grey image instead
-                                     */
-
-#define CONNECTION_KO "Lost connection"
-#define CONNECTION_OK "Connection OK"
-
-#define DEF_MAXSTREAMS          10  /* Maximum number of webcam clients per camera */
-#define DEF_MAXWEBQUEUE         10  /* Maximum number of webcam client in queue */
-
-#define DEF_TIMESTAMP      "%Y-%m-%d\\n%T"
-#define DEF_EVENTSTAMP     "%Y%m%d%H%M%S"
-
-#define DEF_SNAPPATH       "%v-%Y%m%d%H%M%S-snapshot"
-#define DEF_JPEGPATH       "%v-%Y%m%d%H%M%S-%q"
-#define DEF_MPEGPATH       "%v-%Y%m%d%H%M%S"
-#define DEF_TIMEPATH       "%Y%m%d-timelapse"
-
-#define DEF_TIMELAPSE_MODE "daily"
-
-/* Do not break this line into two or more. Must be ONE line */
-#define DEF_SQL_QUERY "sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')"
-
-/* Filetype defines */
-#define FTYPE_IMAGE            1
-#define FTYPE_IMAGE_SNAPSHOT   2
-#define FTYPE_IMAGE_MOTION     4
-#define FTYPE_MPEG             8
-#define FTYPE_MPEG_MOTION     16
-#define FTYPE_MPEG_TIMELAPSE  32
-
-#define FTYPE_MPEG_ANY    (FTYPE_MPEG | FTYPE_MPEG_MOTION | FTYPE_MPEG_TIMELAPSE)
-#define FTYPE_IMAGE_ANY   (FTYPE_IMAGE | FTYPE_IMAGE_SNAPSHOT | FTYPE_IMAGE_MOTION)
-
-/* What types of jpeg files do we want to have */
-#define NEWIMG_OFF       0
-#define NEWIMG_ON        1
-#define NEWIMG_FIRST     2
-#define NEWIMG_BEST      4
-#define NEWIMG_CENTER    8
-
-#define LOCATE_OFF       0
-#define LOCATE_ON        1
-#define LOCATE_PREVIEW   2
-
-#define LOCATE_NORMAL    0
-#define LOCATE_BOTH      1
-
-/* DIFFERENCES BETWEEN imgs.width, conf.width AND rotate_data.cap_width
- * (and the corresponding height values, of course)
- * ===========================================================================
- * Location      Purpose
- * 
- * conf          The values in conf reflect width and height set in the
- *               configuration file. These can be set via http remote control, 
- *               but they are not used internally by Motion, so it won't break
- *               anything. These values are transferred to imgs in vid_start.
- *
- * imgs          The values in imgs are the actual output dimensions. Normally
- *               the output dimensions are the same as the capture dimensions,
- *               but for 90 or 270 degrees rotation, they are not. E.g., if 
- *               you capture at 320x240, and rotate 90 degrees, the output
- *               dimensions are 240x320.
- *               These values are set from the conf values in vid_start, or 
- *               from the first JPEG image in netcam_start. For 90 or 270 
- *               degrees rotation, they are swapped in rotate_init.
- *
- * rotate_data   The values in rotate_data are named cap_width and cap_height,
- *               and contain the capture dimensions. The difference between
- *               capture and output dimensions is explained above.
- *               These values are set in rotate_init.
- */
-
-/* date/time drawing, draw.c */
-int draw_text (unsigned char *image, int startx, int starty, int width, char *text, int factor);
-int initialize_chars(void);
-
-struct images {
-	unsigned char *image_ring_buffer; /* The base address of the image ring buffer */
-	time_t *timestamp_ring_buffer;
-	int *shotstamp_ring_buffer;
-	int *diffs_ring_buffer;
-	int ring_buffer_size;
-	int ring_buffer_last_in;
-	int ring_buffer_last_out;
-	unsigned char *ref;               /* The reference frame */
-	unsigned char *out;               /* Picture buffer for motion images */
-	unsigned char *image_virgin;      /* Last picture frame with no text or locate overlay */
-	unsigned char *preview_buffer;    /* Picture buffer for best image when enables */
-	unsigned char *mask;              /* Buffer for the mask file */
-	unsigned char *smartmask;
-	unsigned char *smartmask_final;
-	unsigned char *common_buffer;
-	int *smartmask_buffer;
-	int *labels;
-	int *labelsize;
-	int width;
-	int height;
-	int type;
-	int size;
-	int motionsize;
-	int total_labels;
-	int labelsize_max;
-	int labelgroup_max;
-	int labels_above;
-	int largest_label;
-};
-
-/* Contains data for image rotation, see rotate.c. */
-struct rotdata {
-	/* Temporary buffer for 90 and 270 degrees rotation. */
-	unsigned char *temp_buf;
-	/* Degrees to rotate; copied from conf.rotate_deg. This is the value
-	 * that is actually used. The value of conf.rotate_deg cannot be used
-	 * because it can be changed by motion-control, and changing rotation
-	 * while Motion is running just causes problems.
-	 */
-	int degrees;
-	/* Capture width and height - different from output width and height if 
-	 * rotating 90 or 270 degrees. */
-	int cap_width;
-	int cap_height;
-};
-
-#include "track.h"
-
-#include "netcam.h"
-
-
-
-/*
-	these used to be global variables but now each thread will have its
-	own context
- */
-struct context {
-	char conf_filename[PATH_MAX];
-	int threadnr;
-	int daemon;
-
-	struct config conf;
-	struct images imgs;
-	struct trackoptions track;
-	struct netcam_context *netcam;
-	int new_img;
-	int preview_max;            /* Stores max diff number seen in an event of output_normal==best */
-	/* Current preview frame, movement to img center distance 
-	 * Note Dist is calculated distX*distX + distY*distY */
-	unsigned long preview_cent_dist;
-	time_t preview_time;              /* Timestamp of preview image */
-	int preview_shots;                 /* Shot of preview buffer image */
-
-	int locate;
-	struct coord location;      /* coordinates for center and size of last motion detection*/
-	struct rotdata rotate_data; /* rotation data is thread-specific */
-
-	int diffs;
-	int noise;
-	int threshold;
-	int diffs_last[THRESHOLD_TUNE_LENGTH];
-	int smartmask_speed;
-
-	int snapshot;
-	int makemovie;
-	int finish;
-
-	int event_nr;
-	int prev_event;
-	char text_event_string[PATH_MAX]; /* The text for conv. spec. %C -
-	                                     we assume PATH_MAX normally 4096 characters is fine */
-	int postcap;		/* downcounter, frames left to to send post event */
-
-	int shots;
-	struct tm *currenttime_tm;
-	struct tm *eventtime_tm;
-
-	time_t currenttime;
-	time_t lasttime;
-	time_t eventtime;
-	time_t connectionlosttime;   /* timestamp from connection lost */
-
-	int lastrate;
-	int moved;
-	int switched;
-	int pause;
-	int missing_frame_counter;   /* counts failed attempts to fetch picture frame from camera */
-	int lost_connection;	
-
-#if (defined(BSD))
-	int tuner_dev;
-#endif
-	int video_dev;
-	int pipe;
-	int mpipe;
-
-	struct webcam webcam;
-	int stream_count;
-	
-#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL)
-	int sql_mask;
-#endif
-
-#ifdef HAVE_MYSQL
-	MYSQL *database;
-#endif
-
-#ifdef HAVE_PGSQL
-	PGconn *database_pg;
-#endif
-
-#ifdef HAVE_FFMPEG
-	struct ffmpeg *ffmpeg_new;
-	struct ffmpeg *ffmpeg_motion;
-	struct ffmpeg *ffmpeg_timelapse;
-	struct ffmpeg *ffmpeg_smartmask;
-	char newfilename[PATH_MAX];
-	char motionfilename[PATH_MAX];
-	char timelapsefilename[PATH_MAX];
-#endif
-};
-
-extern pthread_mutex_t global_lock;
-extern volatile int threads_running;
-extern int debug_level;
-
-/* TLS keys below */
-extern pthread_key_t tls_key_threadnr; /* key for thread number */
-
-int http_bindsock(int, int);
-void * mymalloc(size_t);
-void * myrealloc(void *, size_t, const char *);
-FILE * myfopen(const char *, const char *);
-size_t mystrftime(struct context *, char *, size_t, const char *, const struct tm *, const char *, int);
-int create_path(const char *);
-void motion_log(int, int, const char *, ...);
-#endif /* _INCLUDE_MOTION_H */
+/*	motion.h
+ *
+ *	Include file for motion.c
+ *      Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
+ *      This software is distributed under the GNU public license version 2
+ *      See also the file 'COPYING'.
+ *
+ */
+
+#ifndef _INCLUDE_MOTION_H
+#define _INCLUDE_MOTION_H
+
+/* Includes */
+#ifdef HAVE_MYSQL
+#include <mysql.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#define __USE_GNU
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <signal.h>
+#include <syslog.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#define _LINUX_TIME_H 1
+#if (!defined(WITHOUT_V4L)) && (!defined(BSD))
+#include <linux/videodev.h>
+#endif
+
+#include <pthread.h>
+
+#ifdef HAVE_PGSQL
+#include <libpq-fe.h>
+#endif
+
+#include "conf.h"
+#include "webcam.h"
+#include "webhttpd.h"
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * Macro used to signal to GCC unused function parameters
+ */
+#ifdef __GNUC__
+#ifdef HAVE_ANSIDECL_H
+#include <ansidecl.h>
+#endif
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#endif
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
+/* The macro below defines a version of sleep using nanosleep
+ * If a signal such as SIG_CHLD interrupts the sleep we just continue sleeping
+ */
+#define SLEEP(seconds, nanoseconds) {              \
+                struct timespec tv;                \
+                tv.tv_sec = (seconds);             \
+                tv.tv_nsec = (nanoseconds);        \
+                while (nanosleep(&tv, &tv) == -1); \
+        } 
+
+
+#if defined(WITHOUT_V4L) || defined(BSD)
+ 
+#define VIDEO_PALETTE_GREY      1       /* Linear greyscale */
+#define VIDEO_PALETTE_HI240     2       /* High 240 cube (BT848) */
+#define VIDEO_PALETTE_RGB565    3       /* 565 16 bit RGB */
+#define VIDEO_PALETTE_RGB24     4       /* 24bit RGB */
+#define VIDEO_PALETTE_RGB32     5       /* 32bit RGB */
+#define VIDEO_PALETTE_RGB555    6       /* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422    7       /* YUV422 capture */
+#define VIDEO_PALETTE_YUYV      8
+#define VIDEO_PALETTE_UYVY      9       /* The great thing about standards is ... */
+#define VIDEO_PALETTE_YUV420    10
+#define VIDEO_PALETTE_YUV411    11      /* YUV411 capture */
+#define VIDEO_PALETTE_RAW       12      /* RAW capture (BT848) */
+#define VIDEO_PALETTE_YUV422P   13      /* YUV 4:2:2 Planar */
+#define VIDEO_PALETTE_YUV411P   14      /* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P   15      /* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P   16      /* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR    13      /* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7       /* start of component entries */
+#endif
+
+
+/* Debug levels */
+#define CAMERA_WARNINGS         3   /* warnings only */
+#define CAMERA_INFO             5   /* debug level to activate everything */
+
+/* Default picture settings */
+#define DEF_WIDTH              352
+#define DEF_HEIGHT             288
+#define DEF_QUALITY             75
+#define DEF_CHANGES           1500
+
+#define DEF_MAXFRAMERATE       100
+#define DEF_NOISELEVEL          32
+
+/* Minimum time between two 'actions' (email, sms, external) */
+#define DEF_GAP                 60  /* 1 minutes */
+#define DEF_MAXMPEGTIME       3600  /* 60 minutes */
+
+#define DEF_FFMPEG_BPS      400000
+#define DEF_FFMPEG_VBR           0
+#define DEF_FFMPEG_CODEC   "mpeg4"
+
+#define THRESHOLD_TUNE_LENGTH  256
+
+#define MISSING_FRAMES_TIMEOUT  30  /* When failing to get picture frame from camera
+                                     * we reuse the previous frame until
+                                     * MISSING_FRAMES_TIMEOUT seconds has passed
+                                     * and then we show a grey image instead
+                                     */
+
+#define CONNECTION_KO "Lost connection"
+#define CONNECTION_OK "Connection OK"
+
+#define DEF_MAXSTREAMS          10  /* Maximum number of webcam clients per camera */
+#define DEF_MAXWEBQUEUE         10  /* Maximum number of webcam client in queue */
+
+#define DEF_TIMESTAMP      "%Y-%m-%d\\n%T"
+#define DEF_EVENTSTAMP     "%Y%m%d%H%M%S"
+
+#define DEF_SNAPPATH       "%v-%Y%m%d%H%M%S-snapshot"
+#define DEF_JPEGPATH       "%v-%Y%m%d%H%M%S-%q"
+#define DEF_MPEGPATH       "%v-%Y%m%d%H%M%S"
+#define DEF_TIMEPATH       "%Y%m%d-timelapse"
+
+#define DEF_TIMELAPSE_MODE "daily"
+
+/* Do not break this line into two or more. Must be ONE line */
+#define DEF_SQL_QUERY "sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')"
+
+/* Filetype defines */
+#define FTYPE_IMAGE            1
+#define FTYPE_IMAGE_SNAPSHOT   2
+#define FTYPE_IMAGE_MOTION     4
+#define FTYPE_MPEG             8
+#define FTYPE_MPEG_MOTION     16
+#define FTYPE_MPEG_TIMELAPSE  32
+
+#define FTYPE_MPEG_ANY    (FTYPE_MPEG | FTYPE_MPEG_MOTION | FTYPE_MPEG_TIMELAPSE)
+#define FTYPE_IMAGE_ANY   (FTYPE_IMAGE | FTYPE_IMAGE_SNAPSHOT | FTYPE_IMAGE_MOTION)
+
+/* What types of jpeg files do we want to have */
+#define NEWIMG_OFF       0
+#define NEWIMG_ON        1
+#define NEWIMG_FIRST     2
+#define NEWIMG_BEST      4
+#define NEWIMG_CENTER    8
+
+#define LOCATE_OFF       0
+#define LOCATE_ON        1
+#define LOCATE_PREVIEW   2
+
+#define LOCATE_NORMAL    0
+#define LOCATE_BOTH      1
+
+/* Structure to hold images information
+ * The idea is that this should have all information about a picture e.g. diffs, timestamp etc.
+ * The exception is the label information, it uses a lot of memory
+ * When the image is stored all texts motion marks etc. is written to the image
+ * so we only have to send it out when/if we want.
+ */
+/* A image can have detected motion in it, but dosn't trigger an event, if we use minimum_motion_frames */
+#define IMAGE_MOTION     1
+#define IMAGE_TRIGGER    2
+#define IMAGE_SAVE       4
+#define IMAGE_SAVED      8
+#define IMAGE_PRECAP    16
+#define IMAGE_POSTCAP   32
+
+struct image_data {
+	unsigned char *image;
+	int diffs;
+	time_t timestamp;         /* Timestamp when image was captured */
+	struct tm timestamp_tm;
+	int shot;                 /* Sub second timestamp count */
+
+	/* movement center to img center distance 
+	 * Note Dist is calculated distX*distX + distY*distY */
+	unsigned long cent_dist;
+
+	unsigned int flags;       /* Se IMAGE_* defines */
+};
+
+/* DIFFERENCES BETWEEN imgs.width, conf.width AND rotate_data.cap_width
+ * (and the corresponding height values, of course)
+ * ===========================================================================
+ * Location      Purpose
+ * 
+ * conf          The values in conf reflect width and height set in the
+ *               configuration file. These can be set via http remote control, 
+ *               but they are not used internally by Motion, so it won't break
+ *               anything. These values are transferred to imgs in vid_start.
+ *
+ * imgs          The values in imgs are the actual output dimensions. Normally
+ *               the output dimensions are the same as the capture dimensions,
+ *               but for 90 or 270 degrees rotation, they are not. E.g., if 
+ *               you capture at 320x240, and rotate 90 degrees, the output
+ *               dimensions are 240x320.
+ *               These values are set from the conf values in vid_start, or 
+ *               from the first JPEG image in netcam_start. For 90 or 270 
+ *               degrees rotation, they are swapped in rotate_init.
+ *
+ * rotate_data   The values in rotate_data are named cap_width and cap_height,
+ *               and contain the capture dimensions. The difference between
+ *               capture and output dimensions is explained above.
+ *               These values are set in rotate_init.
+ */
+
+/* date/time drawing, draw.c */
+int draw_text (unsigned char *image, int startx, int starty, int width, char *text, int factor);
+int initialize_chars(void);
+
+struct images {
+	struct image_data *image_ring;    /* The base address of the image ring buffer */
+	int image_ring_size;
+	int image_ring_in;                /* Index in image ring buffer we last added a image into */
+	int image_ring_out;               /* Index in image ring buffer we want to process next time */
+
+	unsigned char *ref;               /* The reference frame */
+	unsigned char *out;               /* Picture buffer for motion images */
+	unsigned char *image_virgin;      /* Last picture frame with no text or locate overlay */
+	struct image_data preview_image;  /* Picture buffer for best image when enables */
+	unsigned char *mask;              /* Buffer for the mask file */
+	unsigned char *smartmask;
+	unsigned char *smartmask_final;
+	unsigned char *common_buffer;
+	int *smartmask_buffer;
+	int *labels;
+	int *labelsize;
+	int width;
+	int height;
+	int type;
+	int size;
+	int motionsize;
+	int total_labels;
+	int labelsize_max;
+	int labelgroup_max;
+	int labels_above;
+	int largest_label;
+};
+
+/* Contains data for image rotation, see rotate.c. */
+struct rotdata {
+	/* Temporary buffer for 90 and 270 degrees rotation. */
+	unsigned char *temp_buf;
+	/* Degrees to rotate; copied from conf.rotate_deg. This is the value
+	 * that is actually used. The value of conf.rotate_deg cannot be used
+	 * because it can be changed by motion-control, and changing rotation
+	 * while Motion is running just causes problems.
+	 */
+	int degrees;
+	/* Capture width and height - different from output width and height if 
+	 * rotating 90 or 270 degrees. */
+	int cap_width;
+	int cap_height;
+};
+
+#include "track.h"
+
+#include "netcam.h"
+
+
+
+/*
+	these used to be global variables but now each thread will have its
+	own context
+ */
+struct context {
+	char conf_filename[PATH_MAX];
+	int threadnr;
+	int daemon;
+
+	struct config conf;
+	struct images imgs;
+	struct trackoptions track;
+	struct netcam_context *netcam;
+	struct image_data *current_image; /* Pointer to a structure where the image, diffs etc is stored */
+	int new_img;
+	time_t preview_time;              /* Timestamp of preview image */
+	int preview_shots;                 /* Shot of preview buffer image */
+
+	int locate;
+	struct coord location;      /* coordinates for center and size of last motion detection*/
+	struct rotdata rotate_data; /* rotation data is thread-specific */
+
+	int noise;
+	int threshold;
+	int diffs_last[THRESHOLD_TUNE_LENGTH];
+	int smartmask_speed;
+
+	int snapshot;
+	int makemovie;
+	int finish;
+
+	int event_nr;
+	int prev_event;
+	char text_event_string[PATH_MAX]; /* The text for conv. spec. %C -
+	                                     we assume PATH_MAX normally 4096 characters is fine */
+	int postcap;		/* downcounter, frames left to to send post event */
+
+	int shots;
+	struct tm *currenttime_tm;
+	struct tm *eventtime_tm;
+
+	time_t currenttime;
+	time_t lasttime;
+	time_t eventtime;
+	time_t connectionlosttime;   /* timestamp from connection lost */
+
+	int lastrate;
+	int moved;
+	int switched;
+	int pause;
+	int missing_frame_counter;   /* counts failed attempts to fetch picture frame from camera */
+	int lost_connection;	
+
+#if (defined(BSD))
+	int tuner_dev;
+#endif
+	int video_dev;
+	int pipe;
+	int mpipe;
+
+	struct webcam webcam;
+	int stream_count;
+	
+#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL)
+	int sql_mask;
+#endif
+
+#ifdef HAVE_MYSQL
+	MYSQL *database;
+#endif
+
+#ifdef HAVE_PGSQL
+	PGconn *database_pg;
+#endif
+
+#ifdef HAVE_FFMPEG
+	struct ffmpeg *ffmpeg_new;
+	struct ffmpeg *ffmpeg_motion;
+	struct ffmpeg *ffmpeg_timelapse;
+	struct ffmpeg *ffmpeg_smartmask;
+	char newfilename[PATH_MAX];
+	char motionfilename[PATH_MAX];
+	char timelapsefilename[PATH_MAX];
+#endif
+};
+
+extern pthread_mutex_t global_lock;
+extern volatile int threads_running;
+extern int debug_level;
+
+/* TLS keys below */
+extern pthread_key_t tls_key_threadnr; /* key for thread number */
+
+int http_bindsock(int, int);
+void * mymalloc(size_t);
+void * myrealloc(void *, size_t, const char *);
+FILE * myfopen(const char *, const char *);
+size_t mystrftime(struct context *, char *, size_t, const char *, const struct tm *, const char *, int);
+int create_path(const char *);
+void motion_log(int, int, const char *, ...);
+#endif /* _INCLUDE_MOTION_H */
Index: event.c
===================================================================
--- event.c	(revision 213)
+++ event.c	(working copy)
@@ -30,7 +30,7 @@
 static void exec_command(struct context *cnt, char *command, char *filename, int filetype)
 {
 	char stamp[PATH_MAX];
-	mystrftime(cnt, stamp, sizeof(stamp), command, cnt->currenttime_tm, filename, filetype);
+	mystrftime(cnt, stamp, sizeof(stamp), command, &cnt->current_image->timestamp_tm, filename, filetype);
 	
 	if (!fork()) {
 		int i;
@@ -123,13 +123,9 @@
 	 */
 	{
 		char sqlquery[PATH_MAX];
-		char timestr[20];
-		char eventtimestr[20];
 		int ret;
 	
-		strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", cnt->currenttime_tm);
-		strftime(eventtimestr, sizeof(eventtimestr), "%Y-%m-%d %T", cnt->eventtime_tm);
-		mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query, cnt->currenttime_tm, filename, sqltype);
+		mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query, &cnt->current_image->timestamp_tm, filename, sqltype);
 		
 
 #ifdef HAVE_MYSQL
Index: video_common.c
===================================================================
--- video_common.c	(revision 213)
+++ video_common.c	(working copy)
@@ -468,7 +468,7 @@
 	int brightness_window_low;
 	int brightness_target;
 	int i, j = 0, avg = 0, step = 0;
-	unsigned char *image = cnt->imgs.image_ring_buffer;
+	unsigned char *image = cnt->imgs.image_ring[0].image;
 
 	int make_change = 0;
 
Index: picture.c
===================================================================
--- picture.c	(revision 213)
+++ picture.c	(working copy)
@@ -1,690 +1,689 @@
-/*	picture.c
- *
- *	Various funtions for saving/loading pictures.
- *	Copyright 2002 by Jeroen Vreeken (pe1rxq@amsat.org)
- *	Portions of this file are Copyright by Lionnel Maugis
- *	This software is distributed under the GNU public license version 2
- *	See also the file 'COPYING'.
- *
- */
-
-#include "picture.h"
-#include "event.h"
-
-#include <jpeglib.h>
-#include <jerror.h>
-
-/* The following declarations and 5 functions are jpeg related 
- * functions used by put_jpeg_grey_memory and put_jpeg_yuv420p_memory
- */
-typedef struct {
-        struct jpeg_destination_mgr pub;
-        JOCTET *buf;
-        size_t bufsize;
-        size_t jpegsize;
-} mem_destination_mgr;
-
-typedef mem_destination_mgr *mem_dest_ptr;
-
-
-METHODDEF(void) init_destination(j_compress_ptr cinfo)
-{
-        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
-
-        dest->pub.next_output_byte = dest->buf;
-        dest->pub.free_in_buffer = dest->bufsize;
-        dest->jpegsize = 0;
-}
-
-METHODDEF(boolean) empty_output_buffer(j_compress_ptr cinfo)
-{
-        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
-
-        dest->pub.next_output_byte = dest->buf;
-        dest->pub.free_in_buffer = dest->bufsize;
-
-        return FALSE;
-        ERREXIT(cinfo, JERR_BUFFER_SIZE);
-}
-
-METHODDEF(void) term_destination(j_compress_ptr cinfo)
-{
-        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
-        dest->jpegsize = dest->bufsize - dest->pub.free_in_buffer;
-}
-
-static GLOBAL(void) jpeg_mem_dest(j_compress_ptr cinfo, JOCTET* buf, size_t bufsize)
-{
-        mem_dest_ptr dest;
-
-        if (cinfo->dest == NULL) {
-                cinfo->dest = (struct jpeg_destination_mgr *)
-                    (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
-                    sizeof(mem_destination_mgr));
-        }
-
-        dest = (mem_dest_ptr) cinfo->dest;
-
-        dest->pub.init_destination    = init_destination;
-        dest->pub.empty_output_buffer = empty_output_buffer;
-        dest->pub.term_destination    = term_destination;
-
-        dest->buf      = buf;
-        dest->bufsize  = bufsize;
-        dest->jpegsize = 0;
-}
-
-static GLOBAL(int) jpeg_mem_size(j_compress_ptr cinfo)
-{
-        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
-        return dest->jpegsize;
-}
-
-
-/* put_jpeg_yuv420p_memory converts an input image in the YUV420P format into a jpeg image and puts
- * it in a memory buffer.
- * Inputs:
- * - image_size is the size of the input image buffer.
- * - input_image is the image in YUV420P format.
- * - width and height are the dimensions of the image
- * - quality is the jpeg encoding quality 0-100%
- * Output:
- * - dest_image is a pointer to the jpeg image buffer
- * Returns buffer size of jpeg image     
- */
-static int put_jpeg_yuv420p_memory(unsigned char *dest_image, int image_size,
-                            unsigned char *input_image, int width, int height, int quality)
-{
-	int i, j, jpeg_image_size;
-
-	JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane)
-	JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5
-
-	struct jpeg_compress_struct cinfo;
-	struct jpeg_error_mgr jerr;
-
-	data[0] = y;
-	data[1] = cb;
-	data[2] = cr;
-
-	cinfo.err = jpeg_std_error(&jerr);  // errors get written to stderr 
-	
-	jpeg_create_compress(&cinfo);
-	cinfo.image_width = width;
-	cinfo.image_height = height;
-	cinfo.input_components = 3;
-	jpeg_set_defaults (&cinfo);
-
-	jpeg_set_colorspace(&cinfo, JCS_YCbCr);
-
-	cinfo.raw_data_in = TRUE; // supply downsampled data
-	cinfo.comp_info[0].h_samp_factor = 2;
-	cinfo.comp_info[0].v_samp_factor = 2;
-	cinfo.comp_info[1].h_samp_factor = 1;
-	cinfo.comp_info[1].v_samp_factor = 1;
-	cinfo.comp_info[2].h_samp_factor = 1;
-	cinfo.comp_info[2].v_samp_factor = 1;
-
-	jpeg_set_quality(&cinfo, quality, TRUE);
-	cinfo.dct_method = JDCT_FASTEST;
-
-	jpeg_mem_dest(&cinfo, dest_image, image_size);	// data written to mem
-	
-	jpeg_start_compress (&cinfo, TRUE);
-
-	for (j=0; j<height; j+=16) {
-		for (i=0; i<16; i++) {
-			y[i] = input_image + width*(i+j);
-			if (i%2 == 0) {
-				cb[i/2] = input_image + width*height + width/2*((i+j)/2);
-				cr[i/2] = input_image + width*height + width*height/4 + width/2*((i+j)/2);
-			}
-		}
-		jpeg_write_raw_data(&cinfo, data, 16);
-	}
-
-	jpeg_finish_compress(&cinfo);
-	jpeg_image_size = jpeg_mem_size(&cinfo);
-	jpeg_destroy_compress(&cinfo);
-	
-	return jpeg_image_size;
-}
-
-/* put_jpeg_grey_memory converts an input image in the grayscale format into a jpeg image
- * Inputs:
- * - image_size is the size of the input image buffer.
- * - input_image is the image in grayscale format.
- * - width and height are the dimensions of the image
- * - quality is the jpeg encoding quality 0-100%
- * Output:
- * - dest_image is a pointer to the jpeg image buffer
- * Returns buffer size of jpeg image     
- */
-static int put_jpeg_grey_memory(unsigned char *dest_image, int image_size, unsigned char *input_image, int width, int height, int quality)
-{
-	int y, dest_image_size;
-	JSAMPROW row_ptr[1];
-	struct jpeg_compress_struct cjpeg;
-	struct jpeg_error_mgr jerr;
-
-	cjpeg.err = jpeg_std_error(&jerr);
-	jpeg_create_compress(&cjpeg);
-	cjpeg.image_width = width;
-	cjpeg.image_height = height;
-	cjpeg.input_components = 1; /* one colour component */
-	cjpeg.in_color_space = JCS_GRAYSCALE;
-
-	jpeg_set_defaults(&cjpeg);
-
-	jpeg_set_quality(&cjpeg, quality, TRUE);
-	cjpeg.dct_method = JDCT_FASTEST;
-	jpeg_mem_dest(&cjpeg, dest_image, image_size);  // data written to mem
-
-	jpeg_start_compress (&cjpeg, TRUE);
-
-	row_ptr[0] = input_image;
-	
-	for (y=0; y<height; y++) {
-		jpeg_write_scanlines(&cjpeg, row_ptr, 1);
-		row_ptr[0] += width;
-	}
-	
-	jpeg_finish_compress(&cjpeg);
-	dest_image_size = jpeg_mem_size(&cjpeg);
-	jpeg_destroy_compress(&cjpeg);
-
-	return dest_image_size;
-}
-
-/* put_jpeg_yuv420p_file converts an YUV420P coded image to a jpeg image and writes
- * it to an already open file.
- * Inputs:
- * - image is the image in YUV420P format.
- * - width and height are the dimensions of the image
- * - quality is the jpeg encoding quality 0-100%
- * Output:
- * - The jpeg is written directly to the file given by the file pointer fp
- * Returns nothing
- */
-static void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image, int width, int height, int quality)
-{
-	int i,j;
-
-	JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane)
-	JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5
-
-	struct jpeg_compress_struct cinfo;
-	struct jpeg_error_mgr jerr;
-
-	data[0] = y;
-	data[1] = cb;
-	data[2] = cr;
-
-	cinfo.err = jpeg_std_error(&jerr);  // errors get written to stderr 
-	
-	jpeg_create_compress(&cinfo);
-	cinfo.image_width = width;
-	cinfo.image_height = height;
-	cinfo.input_components = 3;
-	jpeg_set_defaults(&cinfo);
-
-	jpeg_set_colorspace(&cinfo, JCS_YCbCr);
-
-	cinfo.raw_data_in = TRUE; // supply downsampled data
-	cinfo.comp_info[0].h_samp_factor = 2;
-	cinfo.comp_info[0].v_samp_factor = 2;
-	cinfo.comp_info[1].h_samp_factor = 1;
-	cinfo.comp_info[1].v_samp_factor = 1;
-	cinfo.comp_info[2].h_samp_factor = 1;
-	cinfo.comp_info[2].v_samp_factor = 1;
-
-	jpeg_set_quality(&cinfo, quality, TRUE);
-	cinfo.dct_method = JDCT_FASTEST;
-
-	jpeg_stdio_dest(&cinfo, fp);  	  // data written to file
-	jpeg_start_compress(&cinfo, TRUE);
-
-	for (j=0;j<height;j+=16) {
-		for (i=0;i<16;i++) {
-			y[i] = image + width*(i+j);
-			if (i%2 == 0) {
-				cb[i/2] = image + width*height + width/2*((i+j)/2);
-				cr[i/2] = image + width*height + width*height/4 + width/2*((i+j)/2);
-			}
-		}
-		jpeg_write_raw_data(&cinfo, data, 16);
-	}
-
-	jpeg_finish_compress(&cinfo);
-	jpeg_destroy_compress(&cinfo);
-}
-
-
-/* put_jpeg_grey_file converts an greyscale image to a jpeg image and writes
- * it to an already open file.
- * Inputs:
- * - image is the image in greyscale format.
- * - width and height are the dimensions of the image
- * - quality is the jpeg encoding quality 0-100%
- * Output:
- * - The jpeg is written directly to the file given by the file pointer fp
- * Returns nothing
- */
-static void put_jpeg_grey_file(FILE *picture, unsigned char *image, int width, int height, int quality)
-{
-	int y;
-	JSAMPROW row_ptr[1];
-	struct jpeg_compress_struct cjpeg;
-	struct jpeg_error_mgr jerr;
-
-	cjpeg.err = jpeg_std_error(&jerr);
-	jpeg_create_compress(&cjpeg);
-	cjpeg.image_width = width;
-	cjpeg.image_height = height;
-	cjpeg.input_components = 1; /* one colour component */
-	cjpeg.in_color_space = JCS_GRAYSCALE;
-
-	jpeg_set_defaults(&cjpeg);
-
-	jpeg_set_quality(&cjpeg, quality, TRUE);
-	cjpeg.dct_method = JDCT_FASTEST;
-	jpeg_stdio_dest(&cjpeg, picture);
-
-	jpeg_start_compress(&cjpeg, TRUE);
-
-	row_ptr[0]=image;
-	for (y=0; y<height; y++) {
-		jpeg_write_scanlines(&cjpeg, row_ptr, 1);
-		row_ptr[0]+=width;
-	}
-	jpeg_finish_compress(&cjpeg);
-	jpeg_destroy_compress(&cjpeg);
-}
-
-
-/* put_ppm_bgr24_file converts an greyscale image to a PPM image and writes
- * it to an already open file.
- * Inputs:
- * - image is the image in YUV420P format.
- * - width and height are the dimensions of the image
- * Output:
- * - The PPM is written directly to the file given by the file pointer fp
- * Returns nothing
- */
-static void put_ppm_bgr24_file(FILE *picture, unsigned char *image, int width, int height)
-{
-	int x, y;
-	unsigned char *l=image;
-	unsigned char *u=image+width*height;
-	unsigned char *v=u+(width*height)/4;
-	int r, g, b;
-	int warningkiller;
-	unsigned char rgb[3];
-	
-	/*	ppm header
-	 *	width height
-	 *	maxval
-	 */
-	fprintf(picture, "P6\n");
-	fprintf(picture, "%d %d\n", width, height);
-	fprintf(picture, "%d\n", 255);
-	for (y=0; y<height; y++) {
-		
-		for (x=0; x<width; x++) {
-			r = 76283*(((int)*l)-16)+104595*(((int)*u)-128);
-			g = 76283*(((int)*l)-16)- 53281*(((int)*u)-128)-25625*(((int)*v)-128);
-			b = 76283*(((int)*l)-16)+132252*(((int)*v)-128);
-			r = r>>16;
-			g = g>>16;
-			b = b>>16;
-			if (r<0)
-				r=0;
-			else if (r>255)
-				r=255;
-			if (g<0)
-				g=0;
-			else if (g>255)
-				g=255;
-			if (b<0)
-				b=0;
-			else if (b>255)
-				b=255;
-
-			rgb[0]=b;
-			rgb[1]=g;
-			rgb[2]=r;
-
-			l++;
-			if (x&1) {
-				u++;
-				v++;
-			}
-			/* ppm is rgb not bgr */
-			warningkiller=fwrite(rgb, 1, 3, picture);
-		}
-		if (y&1) {
-			u-=width/2;
-			v-=width/2;
-		}
-	}
-}
-
-/* copy smartmask as an overlay into motion images and movies */
-void overlay_smartmask(struct context *cnt, unsigned char *out)
-{
-	int i, x, v, width, height, line;
-	struct images *imgs = &cnt->imgs;
-	unsigned char *smartmask = imgs->smartmask_final;
-	unsigned char *out_y, *out_u, *out_v;
-	
-	i = imgs->motionsize;
-	v = i + ((imgs->motionsize) / 4);
-	width = imgs->width;
-	height = imgs->height;
-
-	/* set V to 255 to make smartmask appear red */
-	out_v = out + v;
-	out_u = out + i;
-	for ( i = 0; i < height; i += 2){
-		line = i * width;
-		for (x = 0; x < width; x += 2){
-			if (smartmask[line + x] == 0 ||
-				smartmask[line + x + 1] == 0 ||
-				smartmask[line + width + x] == 0 ||
-				smartmask[line + width + x + 1] == 0){
-					*out_v = 255;
-					*out_u = 128;
-			}
-			out_v++;
-			out_u++;
-		}
-	}
-	out_y = out;
-	/* set colour intensity for smartmask */
-	for (i = 0; i < imgs->motionsize; i++){
-		if (smartmask[i] == 0)
-			*out_y = 0;
-		out_y++;
-	}
-}
-
-/* copy fixed mask as an overlay into motion images and movies */
-void overlay_fixed_mask(struct context *cnt, unsigned char *out)
-{
-	int i;
-	struct images *imgs=&cnt->imgs;
-	unsigned char *motion_img=imgs->out;
-	unsigned char *mask=imgs->mask;
-	int pixel;
-	
-	/* set y to mask + motion-pixel to keep motion pixels visible on grey background*/
-	for (i=0; i<imgs->motionsize; i++){
-		pixel=255-mask[i]+motion_img[i];
-		if (pixel>255)
-			*out=255;
-		else
-			*out=pixel;
-		out++;
-	}
-}
-
-/* copy largest label as an overlay into motion images and movies */
-void overlay_largest_label(struct context *cnt, unsigned char *out)
-{
-	int i, x, v, width, height, line;
-	struct images *imgs=&cnt->imgs;
-	int *labels=imgs->labels;
-	unsigned char *out_y, *out_u, *out_v;
-	
-	i=imgs->motionsize;
-	v=i+((imgs->motionsize)/4);
-	width=imgs->width;
-	height=imgs->height;
-
-	/* set U to 255 to make label appear blue */
-	out_u=out+i;
-	out_v=out+v;
-	for ( i=0; i<height; i+=2){
-		line=i*width;
-		for (x=0; x<width; x+=2){
-			if (labels[line+x] & 32768 ||
-				labels[line+x+1] & 32768 ||
-				labels[line+width+x] & 32768 ||
-				labels[line+width+x+1] & 32768) {
-					*out_u=255;
-					*out_v=128;
-			}
-			out_u++;
-			out_v++;
-		}
-	}
-	out_y=out;
-	/* set intensity for coloured label to have better visibility */
-	for (i=0; i<imgs->motionsize; i++) {
-		if (*labels++ & 32768)
-			*out_y=0;
-		out_y++;
-	}
-}
-
-/* put_picture_mem is used for the webcam feature. Depending on the image type
- * (colour YUV420P or greyscale) the corresponding put_jpeg_X_memory function is called.
- * Inputs:
- * - cnt is the global context struct and only cnt->imgs.type is used.
- * - image_size is the size of the input image buffer
- * - *image points to the image buffer that contains the YUV420P or Grayscale image about to be put
- * - quality is the jpeg quality setting from the config file.
- * Output:
- * - **dest_image is a pointer to a pointer that points to the destination buffer in which the
- *   converted image it put
- * Function returns the dest_image_size if successful. Otherwise 0.
- */ 
-int put_picture_memory(struct context *cnt, unsigned char* dest_image, int image_size,
-                       unsigned char *image, int quality)
-{
-	switch (cnt->imgs.type) {
-		case VIDEO_PALETTE_YUV420P:
-			return put_jpeg_yuv420p_memory(dest_image, image_size, image,
-			                               cnt->imgs.width, cnt->imgs.height, quality);
-		case VIDEO_PALETTE_GREY:
-			return put_jpeg_grey_memory(dest_image, image_size, image,
-			                            cnt->imgs.width, cnt->imgs.height, quality);
-	}
-
-	return 0;
-}
-
-void put_picture_fd(struct context *cnt, FILE *picture, unsigned char *image, int quality)
-{
-	if (cnt->conf.ppm) {
-		put_ppm_bgr24_file(picture, image, cnt->imgs.width, cnt->imgs.height);
-	} else {
-		switch (cnt->imgs.type) {
-			case VIDEO_PALETTE_YUV420P:
-				put_jpeg_yuv420p_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality);
-				break;
-			case VIDEO_PALETTE_GREY:
-				put_jpeg_grey_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality);
-				break;
-		}
-	}
-}
-
-
-void put_picture(struct context *cnt, char *file, unsigned char *image, int ftype)
-{
-	FILE *picture;
-
-	picture = myfopen(file, "w");
-	
-	if (!picture) {
-		/* Report to syslog - suggest solution if the problem is access rights to target dir */
-		if (errno ==  EACCES) {
-			motion_log(LOG_ERR, 1,
-			           "Can't write picture to file %s - check access rights to target directory", file);
-			motion_log(LOG_ERR, 1, "Thread is going to finish due to this fatal error");
-			cnt->finish = 1;
-			return;
-		} else {
-			/* If target dir is temporarily unavailable we may survive */
-			motion_log(LOG_ERR, 1, "Can't write picture to file %s", file);
-			return;
-		}
-	}
-
-	put_picture_fd(cnt, picture, image, cnt->conf.quality);
-	fclose(picture);
-	event(cnt, EVENT_FILECREATE, NULL, file, (void *)(unsigned long)ftype, NULL);
-}
-
-/* Get the pgm file used as fixed mask */
-unsigned char *get_pgm(FILE *picture, int width, int height)
-{
-	int x = 0 ,y = 0, maxval;
-	char line[256];
-	unsigned char *image;
-
-	line[255]=0;
-	
-	if (!fgets(line, 255, picture)) {
-		motion_log(LOG_ERR, 1, "Could not read from ppm file");
-		return NULL;
-	}
-	
-	if (strncmp(line, "P5", 2)) {
-		motion_log(LOG_ERR, 1, "This is not a ppm file, starts with '%s'", line);
-		return NULL;
-	}
-	
-	/* skip comment */
-	line[0] = '#';
-	while (line[0] == '#')
-		if (!fgets(line, 255, picture))
-			return NULL;
-
-	/* check size */
-	if (sscanf(line, "%d %d", &x, &y)!=2) {
-		motion_log(LOG_ERR, 1, "Failed reading size in pgm file");
-		return NULL;
-	}
-	
-	if (x != width || y != height) {
-		motion_log(LOG_ERR, 1, "Wrong image size %dx%d should be %dx%d", x, y, width, height);
-		return NULL;
-	}
-
-	/* Maximum value */
-	line[0] = '#';
-	while (line[0] == '#')
-		if (!fgets(line, 255, picture))
-			return NULL;
-	
-	if (sscanf(line, "%d", &maxval)!=1) {
-		motion_log(LOG_ERR, 1, "Failed reading maximum value in pgm file");
-		return NULL;
-	}
-	
-	/* read data */
-	
-	image = mymalloc(width * height);
-	
-	for (y=0; y<height; y++) {
-		if ((int)fread(&image[y * width], 1, width, picture) != width)
-			motion_log(LOG_ERR, 1, "Failed reading image data from pgm file");
-		
-		for (x=0; x<width; x++) {
-			image[y * width + x] = (int)image[y * width + x] * 255 / maxval;
-		}
-	}	
-
-	return image;
-}
-
-/* If a mask file is asked for but does not exist this function
- * creates an empty mask file in the right binary pgm format and
- * and the right size - easy to edit with Gimp or similar tool.
- */
-void put_fixed_mask(struct context *cnt, const char *file)
-{
-	FILE *picture;
-
-	picture=myfopen(file, "w");
-	if (!picture) {
-		/* Report to syslog - suggest solution if the problem is access rights to target dir */
-		if (errno ==  EACCES) {
-			motion_log(LOG_ERR, 1,
-			           "can't write mask file %s - check access rights to target directory", file);
-		} else {
-			/* If target dir is temporarily unavailable we may survive */
-			motion_log(LOG_ERR, 1, "can't write mask file %s", file);
-		}
-		return;
-	}
-	memset(cnt->imgs.out, 255, cnt->imgs.motionsize); /* initialize to unset */
-	
-	/* Write pgm-header */
-	fprintf(picture, "P5\n");
-	fprintf(picture, "%d %d\n", cnt->conf.width, cnt->conf.height);
-	fprintf(picture, "%d\n", 255);
-	
-	/* write pgm image data at once */
-	if ((int)fwrite(cnt->imgs.out, cnt->conf.width, cnt->conf.height, picture) != cnt->conf.height) {
-		motion_log(LOG_ERR, 1, "Failed writing default mask as pgm file");
-		return;
-	}
-	
-	fclose(picture);
-
-	motion_log(LOG_ERR, 0, "Creating empty mask %s",cnt->conf.mask_file);
-	motion_log(LOG_ERR, 0, "Please edit this file and re-run motion to enable mask feature");
-}
-
-/* save preview_shot */
-void preview_save(struct context *cnt)
-{
-#ifdef HAVE_FFMPEG
-	int use_jpegpath;
-#endif /* HAVE_FFMPEG */
-	const char *jpegpath;
-	static char previewname[PATH_MAX];
-	char filename[PATH_MAX];
-	struct tm tmptime;
-	int tmpshots;
-
-	if(cnt->preview_max){
-#ifdef HAVE_FFMPEG
-		/* Use filename of movie i.o. jpeg_filename when set to 'preview' */
-		use_jpegpath=strcmp(cnt->conf.jpegpath, "preview");
-	
-		if (cnt->ffmpeg_new && !use_jpegpath){
-			/* Replace avi/mpg with jpg/ppm and keep the rest of the filename */
-			strncpy(previewname, cnt->newfilename, strlen(cnt->newfilename)-3);
-			strcat(previewname, imageext(cnt));
-			put_picture(cnt, previewname, cnt->imgs.preview_buffer , FTYPE_IMAGE);
-			return;
-		}
-#endif /* HAVE_FFMPEG */
-		/* Save shots while processing preview image */
-		tmpshots = cnt->shots;
-		cnt->shots = cnt->preview_shots;
-		localtime_r(&cnt->preview_time, &tmptime);
-
-		/* Save best preview-shot also when no movies are recorded or jpegpath
-		   is used. Filename has to be generated - nothing available to reuse! */
-		//printf("preview_shot: different filename or picture only!\n");
-
-		/* conf.jpegpath would normally be defined but if someone deleted it by control interface
-		   it is better to revert to the default than fail */
-		if (cnt->conf.jpegpath)
-			jpegpath = cnt->conf.jpegpath;
-		else
-			jpegpath = (char *)DEF_JPEGPATH;
-			
-		mystrftime(cnt, filename, sizeof(filename), jpegpath, &tmptime, NULL, 0);
-		snprintf(previewname, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt));
-		put_picture(cnt, previewname, cnt->imgs.preview_buffer , FTYPE_IMAGE);
-
-		/* Restore shots */
-		cnt->shots = tmpshots;
-	}
-}
+/*	picture.c
+ *
+ *	Various funtions for saving/loading pictures.
+ *	Copyright 2002 by Jeroen Vreeken (pe1rxq@amsat.org)
+ *	Portions of this file are Copyright by Lionnel Maugis
+ *	This software is distributed under the GNU public license version 2
+ *	See also the file 'COPYING'.
+ *
+ */
+
+#include "picture.h"
+#include "event.h"
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+/* The following declarations and 5 functions are jpeg related 
+ * functions used by put_jpeg_grey_memory and put_jpeg_yuv420p_memory
+ */
+typedef struct {
+        struct jpeg_destination_mgr pub;
+        JOCTET *buf;
+        size_t bufsize;
+        size_t jpegsize;
+} mem_destination_mgr;
+
+typedef mem_destination_mgr *mem_dest_ptr;
+
+
+METHODDEF(void) init_destination(j_compress_ptr cinfo)
+{
+        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+        dest->pub.next_output_byte = dest->buf;
+        dest->pub.free_in_buffer = dest->bufsize;
+        dest->jpegsize = 0;
+}
+
+METHODDEF(boolean) empty_output_buffer(j_compress_ptr cinfo)
+{
+        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+        dest->pub.next_output_byte = dest->buf;
+        dest->pub.free_in_buffer = dest->bufsize;
+
+        return FALSE;
+        ERREXIT(cinfo, JERR_BUFFER_SIZE);
+}
+
+METHODDEF(void) term_destination(j_compress_ptr cinfo)
+{
+        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+        dest->jpegsize = dest->bufsize - dest->pub.free_in_buffer;
+}
+
+static GLOBAL(void) jpeg_mem_dest(j_compress_ptr cinfo, JOCTET* buf, size_t bufsize)
+{
+        mem_dest_ptr dest;
+
+        if (cinfo->dest == NULL) {
+                cinfo->dest = (struct jpeg_destination_mgr *)
+                    (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
+                    sizeof(mem_destination_mgr));
+        }
+
+        dest = (mem_dest_ptr) cinfo->dest;
+
+        dest->pub.init_destination    = init_destination;
+        dest->pub.empty_output_buffer = empty_output_buffer;
+        dest->pub.term_destination    = term_destination;
+
+        dest->buf      = buf;
+        dest->bufsize  = bufsize;
+        dest->jpegsize = 0;
+}
+
+static GLOBAL(int) jpeg_mem_size(j_compress_ptr cinfo)
+{
+        mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+        return dest->jpegsize;
+}
+
+
+/* put_jpeg_yuv420p_memory converts an input image in the YUV420P format into a jpeg image and puts
+ * it in a memory buffer.
+ * Inputs:
+ * - image_size is the size of the input image buffer.
+ * - input_image is the image in YUV420P format.
+ * - width and height are the dimensions of the image
+ * - quality is the jpeg encoding quality 0-100%
+ * Output:
+ * - dest_image is a pointer to the jpeg image buffer
+ * Returns buffer size of jpeg image     
+ */
+static int put_jpeg_yuv420p_memory(unsigned char *dest_image, int image_size,
+                            unsigned char *input_image, int width, int height, int quality)
+{
+	int i, j, jpeg_image_size;
+
+	JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane)
+	JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5
+
+	struct jpeg_compress_struct cinfo;
+	struct jpeg_error_mgr jerr;
+
+	data[0] = y;
+	data[1] = cb;
+	data[2] = cr;
+
+	cinfo.err = jpeg_std_error(&jerr);  // errors get written to stderr 
+	
+	jpeg_create_compress(&cinfo);
+	cinfo.image_width = width;
+	cinfo.image_height = height;
+	cinfo.input_components = 3;
+	jpeg_set_defaults (&cinfo);
+
+	jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+
+	cinfo.raw_data_in = TRUE; // supply downsampled data
+	cinfo.comp_info[0].h_samp_factor = 2;
+	cinfo.comp_info[0].v_samp_factor = 2;
+	cinfo.comp_info[1].h_samp_factor = 1;
+	cinfo.comp_info[1].v_samp_factor = 1;
+	cinfo.comp_info[2].h_samp_factor = 1;
+	cinfo.comp_info[2].v_samp_factor = 1;
+
+	jpeg_set_quality(&cinfo, quality, TRUE);
+	cinfo.dct_method = JDCT_FASTEST;
+
+	jpeg_mem_dest(&cinfo, dest_image, image_size);	// data written to mem
+	
+	jpeg_start_compress (&cinfo, TRUE);
+
+	for (j=0; j<height; j+=16) {
+		for (i=0; i<16; i++) {
+			y[i] = input_image + width*(i+j);
+			if (i%2 == 0) {
+				cb[i/2] = input_image + width*height + width/2*((i+j)/2);
+				cr[i/2] = input_image + width*height + width*height/4 + width/2*((i+j)/2);
+			}
+		}
+		jpeg_write_raw_data(&cinfo, data, 16);
+	}
+
+	jpeg_finish_compress(&cinfo);
+	jpeg_image_size = jpeg_mem_size(&cinfo);
+	jpeg_destroy_compress(&cinfo);
+	
+	return jpeg_image_size;
+}
+
+/* put_jpeg_grey_memory converts an input image in the grayscale format into a jpeg image
+ * Inputs:
+ * - image_size is the size of the input image buffer.
+ * - input_image is the image in grayscale format.
+ * - width and height are the dimensions of the image
+ * - quality is the jpeg encoding quality 0-100%
+ * Output:
+ * - dest_image is a pointer to the jpeg image buffer
+ * Returns buffer size of jpeg image     
+ */
+static int put_jpeg_grey_memory(unsigned char *dest_image, int image_size, unsigned char *input_image, int width, int height, int quality)
+{
+	int y, dest_image_size;
+	JSAMPROW row_ptr[1];
+	struct jpeg_compress_struct cjpeg;
+	struct jpeg_error_mgr jerr;
+
+	cjpeg.err = jpeg_std_error(&jerr);
+	jpeg_create_compress(&cjpeg);
+	cjpeg.image_width = width;
+	cjpeg.image_height = height;
+	cjpeg.input_components = 1; /* one colour component */
+	cjpeg.in_color_space = JCS_GRAYSCALE;
+
+	jpeg_set_defaults(&cjpeg);
+
+	jpeg_set_quality(&cjpeg, quality, TRUE);
+	cjpeg.dct_method = JDCT_FASTEST;
+	jpeg_mem_dest(&cjpeg, dest_image, image_size);  // data written to mem
+
+	jpeg_start_compress (&cjpeg, TRUE);
+
+	row_ptr[0] = input_image;
+	
+	for (y=0; y<height; y++) {
+		jpeg_write_scanlines(&cjpeg, row_ptr, 1);
+		row_ptr[0] += width;
+	}
+	
+	jpeg_finish_compress(&cjpeg);
+	dest_image_size = jpeg_mem_size(&cjpeg);
+	jpeg_destroy_compress(&cjpeg);
+
+	return dest_image_size;
+}
+
+/* put_jpeg_yuv420p_file converts an YUV420P coded image to a jpeg image and writes
+ * it to an already open file.
+ * Inputs:
+ * - image is the image in YUV420P format.
+ * - width and height are the dimensions of the image
+ * - quality is the jpeg encoding quality 0-100%
+ * Output:
+ * - The jpeg is written directly to the file given by the file pointer fp
+ * Returns nothing
+ */
+static void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image, int width, int height, int quality)
+{
+	int i,j;
+
+	JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane)
+	JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5
+
+	struct jpeg_compress_struct cinfo;
+	struct jpeg_error_mgr jerr;
+
+	data[0] = y;
+	data[1] = cb;
+	data[2] = cr;
+
+	cinfo.err = jpeg_std_error(&jerr);  // errors get written to stderr 
+	
+	jpeg_create_compress(&cinfo);
+	cinfo.image_width = width;
+	cinfo.image_height = height;
+	cinfo.input_components = 3;
+	jpeg_set_defaults(&cinfo);
+
+	jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+
+	cinfo.raw_data_in = TRUE; // supply downsampled data
+	cinfo.comp_info[0].h_samp_factor = 2;
+	cinfo.comp_info[0].v_samp_factor = 2;
+	cinfo.comp_info[1].h_samp_factor = 1;
+	cinfo.comp_info[1].v_samp_factor = 1;
+	cinfo.comp_info[2].h_samp_factor = 1;
+	cinfo.comp_info[2].v_samp_factor = 1;
+
+	jpeg_set_quality(&cinfo, quality, TRUE);
+	cinfo.dct_method = JDCT_FASTEST;
+
+	jpeg_stdio_dest(&cinfo, fp);  	  // data written to file
+	jpeg_start_compress(&cinfo, TRUE);
+
+	for (j=0;j<height;j+=16) {
+		for (i=0;i<16;i++) {
+			y[i] = image + width*(i+j);
+			if (i%2 == 0) {
+				cb[i/2] = image + width*height + width/2*((i+j)/2);
+				cr[i/2] = image + width*height + width*height/4 + width/2*((i+j)/2);
+			}
+		}
+		jpeg_write_raw_data(&cinfo, data, 16);
+	}
+
+	jpeg_finish_compress(&cinfo);
+	jpeg_destroy_compress(&cinfo);
+}
+
+
+/* put_jpeg_grey_file converts an greyscale image to a jpeg image and writes
+ * it to an already open file.
+ * Inputs:
+ * - image is the image in greyscale format.
+ * - width and height are the dimensions of the image
+ * - quality is the jpeg encoding quality 0-100%
+ * Output:
+ * - The jpeg is written directly to the file given by the file pointer fp
+ * Returns nothing
+ */
+static void put_jpeg_grey_file(FILE *picture, unsigned char *image, int width, int height, int quality)
+{
+	int y;
+	JSAMPROW row_ptr[1];
+	struct jpeg_compress_struct cjpeg;
+	struct jpeg_error_mgr jerr;
+
+	cjpeg.err = jpeg_std_error(&jerr);
+	jpeg_create_compress(&cjpeg);
+	cjpeg.image_width = width;
+	cjpeg.image_height = height;
+	cjpeg.input_components = 1; /* one colour component */
+	cjpeg.in_color_space = JCS_GRAYSCALE;
+
+	jpeg_set_defaults(&cjpeg);
+
+	jpeg_set_quality(&cjpeg, quality, TRUE);
+	cjpeg.dct_method = JDCT_FASTEST;
+	jpeg_stdio_dest(&cjpeg, picture);
+
+	jpeg_start_compress(&cjpeg, TRUE);
+
+	row_ptr[0]=image;
+	for (y=0; y<height; y++) {
+		jpeg_write_scanlines(&cjpeg, row_ptr, 1);
+		row_ptr[0]+=width;
+	}
+	jpeg_finish_compress(&cjpeg);
+	jpeg_destroy_compress(&cjpeg);
+}
+
+
+/* put_ppm_bgr24_file converts an greyscale image to a PPM image and writes
+ * it to an already open file.
+ * Inputs:
+ * - image is the image in YUV420P format.
+ * - width and height are the dimensions of the image
+ * Output:
+ * - The PPM is written directly to the file given by the file pointer fp
+ * Returns nothing
+ */
+static void put_ppm_bgr24_file(FILE *picture, unsigned char *image, int width, int height)
+{
+	int x, y;
+	unsigned char *l=image;
+	unsigned char *u=image+width*height;
+	unsigned char *v=u+(width*height)/4;
+	int r, g, b;
+	int warningkiller;
+	unsigned char rgb[3];
+	
+	/*	ppm header
+	 *	width height
+	 *	maxval
+	 */
+	fprintf(picture, "P6\n");
+	fprintf(picture, "%d %d\n", width, height);
+	fprintf(picture, "%d\n", 255);
+	for (y=0; y<height; y++) {
+		
+		for (x=0; x<width; x++) {
+			r = 76283*(((int)*l)-16)+104595*(((int)*u)-128);
+			g = 76283*(((int)*l)-16)- 53281*(((int)*u)-128)-25625*(((int)*v)-128);
+			b = 76283*(((int)*l)-16)+132252*(((int)*v)-128);
+			r = r>>16;
+			g = g>>16;
+			b = b>>16;
+			if (r<0)
+				r=0;
+			else if (r>255)
+				r=255;
+			if (g<0)
+				g=0;
+			else if (g>255)
+				g=255;
+			if (b<0)
+				b=0;
+			else if (b>255)
+				b=255;
+
+			rgb[0]=b;
+			rgb[1]=g;
+			rgb[2]=r;
+
+			l++;
+			if (x&1) {
+				u++;
+				v++;
+			}
+			/* ppm is rgb not bgr */
+			warningkiller=fwrite(rgb, 1, 3, picture);
+		}
+		if (y&1) {
+			u-=width/2;
+			v-=width/2;
+		}
+	}
+}
+
+/* copy smartmask as an overlay into motion images and movies */
+void overlay_smartmask(struct context *cnt, unsigned char *out)
+{
+	int i, x, v, width, height, line;
+	struct images *imgs = &cnt->imgs;
+	unsigned char *smartmask = imgs->smartmask_final;
+	unsigned char *out_y, *out_u, *out_v;
+	
+	i = imgs->motionsize;
+	v = i + ((imgs->motionsize) / 4);
+	width = imgs->width;
+	height = imgs->height;
+
+	/* set V to 255 to make smartmask appear red */
+	out_v = out + v;
+	out_u = out + i;
+	for ( i = 0; i < height; i += 2){
+		line = i * width;
+		for (x = 0; x < width; x += 2){
+			if (smartmask[line + x] == 0 ||
+				smartmask[line + x + 1] == 0 ||
+				smartmask[line + width + x] == 0 ||
+				smartmask[line + width + x + 1] == 0){
+					*out_v = 255;
+					*out_u = 128;
+			}
+			out_v++;
+			out_u++;
+		}
+	}
+	out_y = out;
+	/* set colour intensity for smartmask */
+	for (i = 0; i < imgs->motionsize; i++){
+		if (smartmask[i] == 0)
+			*out_y = 0;
+		out_y++;
+	}
+}
+
+/* copy fixed mask as an overlay into motion images and movies */
+void overlay_fixed_mask(struct context *cnt, unsigned char *out)
+{
+	int i;
+	struct images *imgs=&cnt->imgs;
+	unsigned char *motion_img=imgs->out;
+	unsigned char *mask=imgs->mask;
+	int pixel;
+	
+	/* set y to mask + motion-pixel to keep motion pixels visible on grey background*/
+	for (i=0; i<imgs->motionsize; i++){
+		pixel=255-mask[i]+motion_img[i];
+		if (pixel>255)
+			*out=255;
+		else
+			*out=pixel;
+		out++;
+	}
+}
+
+/* copy largest label as an overlay into motion images and movies */
+void overlay_largest_label(struct context *cnt, unsigned char *out)
+{
+	int i, x, v, width, height, line;
+	struct images *imgs=&cnt->imgs;
+	int *labels=imgs->labels;
+	unsigned char *out_y, *out_u, *out_v;
+	
+	i=imgs->motionsize;
+	v=i+((imgs->motionsize)/4);
+	width=imgs->width;
+	height=imgs->height;
+
+	/* set U to 255 to make label appear blue */
+	out_u=out+i;
+	out_v=out+v;
+	for ( i=0; i<height; i+=2){
+		line=i*width;
+		for (x=0; x<width; x+=2){
+			if (labels[line+x] & 32768 ||
+				labels[line+x+1] & 32768 ||
+				labels[line+width+x] & 32768 ||
+				labels[line+width+x+1] & 32768) {
+					*out_u=255;
+					*out_v=128;
+			}
+			out_u++;
+			out_v++;
+		}
+	}
+	out_y=out;
+	/* set intensity for coloured label to have better visibility */
+	for (i=0; i<imgs->motionsize; i++) {
+		if (*labels++ & 32768)
+			*out_y=0;
+		out_y++;
+	}
+}
+
+/* put_picture_mem is used for the webcam feature. Depending on the image type
+ * (colour YUV420P or greyscale) the corresponding put_jpeg_X_memory function is called.
+ * Inputs:
+ * - cnt is the global context struct and only cnt->imgs.type is used.
+ * - image_size is the size of the input image buffer
+ * - *image points to the image buffer that contains the YUV420P or Grayscale image about to be put
+ * - quality is the jpeg quality setting from the config file.
+ * Output:
+ * - **dest_image is a pointer to a pointer that points to the destination buffer in which the
+ *   converted image it put
+ * Function returns the dest_image_size if successful. Otherwise 0.
+ */ 
+int put_picture_memory(struct context *cnt, unsigned char* dest_image, int image_size,
+                       unsigned char *image, int quality)
+{
+	switch (cnt->imgs.type) {
+		case VIDEO_PALETTE_YUV420P:
+			return put_jpeg_yuv420p_memory(dest_image, image_size, image,
+			                               cnt->imgs.width, cnt->imgs.height, quality);
+		case VIDEO_PALETTE_GREY:
+			return put_jpeg_grey_memory(dest_image, image_size, image,
+			                            cnt->imgs.width, cnt->imgs.height, quality);
+	}
+
+	return 0;
+}
+
+void put_picture_fd(struct context *cnt, FILE *picture, unsigned char *image, int quality)
+{
+	if (cnt->conf.ppm) {
+		put_ppm_bgr24_file(picture, image, cnt->imgs.width, cnt->imgs.height);
+	} else {
+		switch (cnt->imgs.type) {
+			case VIDEO_PALETTE_YUV420P:
+				put_jpeg_yuv420p_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality);
+				break;
+			case VIDEO_PALETTE_GREY:
+				put_jpeg_grey_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality);
+				break;
+		}
+	}
+}
+
+
+void put_picture(struct context *cnt, char *file, unsigned char *image, int ftype)
+{
+	FILE *picture;
+
+	picture = myfopen(file, "w");
+	
+	if (!picture) {
+		/* Report to syslog - suggest solution if the problem is access rights to target dir */
+		if (errno ==  EACCES) {
+			motion_log(LOG_ERR, 1,
+			           "Can't write picture to file %s - check access rights to target directory", file);
+			motion_log(LOG_ERR, 1, "Thread is going to finish due to this fatal error");
+			cnt->finish = 1;
+			return;
+		} else {
+			/* If target dir is temporarily unavailable we may survive */
+			motion_log(LOG_ERR, 1, "Can't write picture to file %s", file);
+			return;
+		}
+	}
+
+	put_picture_fd(cnt, picture, image, cnt->conf.quality);
+	fclose(picture);
+	event(cnt, EVENT_FILECREATE, NULL, file, (void *)(unsigned long)ftype, NULL);
+}
+
+/* Get the pgm file used as fixed mask */
+unsigned char *get_pgm(FILE *picture, int width, int height)
+{
+	int x = 0 ,y = 0, maxval;
+	char line[256];
+	unsigned char *image;
+
+	line[255]=0;
+	
+	if (!fgets(line, 255, picture)) {
+		motion_log(LOG_ERR, 1, "Could not read from ppm file");
+		return NULL;
+	}
+	
+	if (strncmp(line, "P5", 2)) {
+		motion_log(LOG_ERR, 1, "This is not a ppm file, starts with '%s'", line);
+		return NULL;
+	}
+	
+	/* skip comment */
+	line[0] = '#';
+	while (line[0] == '#')
+		if (!fgets(line, 255, picture))
+			return NULL;
+
+	/* check size */
+	if (sscanf(line, "%d %d", &x, &y)!=2) {
+		motion_log(LOG_ERR, 1, "Failed reading size in pgm file");
+		return NULL;
+	}
+	
+	if (x != width || y != height) {
+		motion_log(LOG_ERR, 1, "Wrong image size %dx%d should be %dx%d", x, y, width, height);
+		return NULL;
+	}
+
+	/* Maximum value */
+	line[0] = '#';
+	while (line[0] == '#')
+		if (!fgets(line, 255, picture))
+			return NULL;
+	
+	if (sscanf(line, "%d", &maxval)!=1) {
+		motion_log(LOG_ERR, 1, "Failed reading maximum value in pgm file");
+		return NULL;
+	}
+	
+	/* read data */
+	
+	image = mymalloc(width * height);
+	
+	for (y=0; y<height; y++) {
+		if ((int)fread(&image[y * width], 1, width, picture) != width)
+			motion_log(LOG_ERR, 1, "Failed reading image data from pgm file");
+		
+		for (x=0; x<width; x++) {
+			image[y * width + x] = (int)image[y * width + x] * 255 / maxval;
+		}
+	}	
+
+	return image;
+}
+
+/* If a mask file is asked for but does not exist this function
+ * creates an empty mask file in the right binary pgm format and
+ * and the right size - easy to edit with Gimp or similar tool.
+ */
+void put_fixed_mask(struct context *cnt, const char *file)
+{
+	FILE *picture;
+
+	picture=myfopen(file, "w");
+	if (!picture) {
+		/* Report to syslog - suggest solution if the problem is access rights to target dir */
+		if (errno ==  EACCES) {
+			motion_log(LOG_ERR, 1,
+			           "can't write mask file %s - check access rights to target directory", file);
+		} else {
+			/* If target dir is temporarily unavailable we may survive */
+			motion_log(LOG_ERR, 1, "can't write mask file %s", file);
+		}
+		return;
+	}
+	memset(cnt->imgs.out, 255, cnt->imgs.motionsize); /* initialize to unset */
+	
+	/* Write pgm-header */
+	fprintf(picture, "P5\n");
+	fprintf(picture, "%d %d\n", cnt->conf.width, cnt->conf.height);
+	fprintf(picture, "%d\n", 255);
+	
+	/* write pgm image data at once */
+	if ((int)fwrite(cnt->imgs.out, cnt->conf.width, cnt->conf.height, picture) != cnt->conf.height) {
+		motion_log(LOG_ERR, 1, "Failed writing default mask as pgm file");
+		return;
+	}
+	
+	fclose(picture);
+
+	motion_log(LOG_ERR, 0, "Creating empty mask %s",cnt->conf.mask_file);
+	motion_log(LOG_ERR, 0, "Please edit this file and re-run motion to enable mask feature");
+}
+
+/* save preview_shot */
+void preview_save(struct context *cnt)
+{
+#ifdef HAVE_FFMPEG
+	int use_jpegpath;
+#endif /* HAVE_FFMPEG */
+	const char *jpegpath;
+	static char previewname[PATH_MAX];
+	char filename[PATH_MAX];
+
+	if(cnt->imgs.preview_image.diffs){
+		/* put_picture sends an event, in the events there is still
+		 * some code that use cnt->current_image
+	 	 * so set it temporary to our preview image */
+		struct image_data *saved_current_image = cnt->current_image;
+		cnt->current_image = &cnt->imgs.preview_image;
+
+#ifdef HAVE_FFMPEG
+		/* Use filename of movie i.o. jpeg_filename when set to 'preview' */
+		use_jpegpath=strcmp(cnt->conf.jpegpath, "preview");
+	
+		if (cnt->ffmpeg_new && !use_jpegpath){
+			/* Replace avi/mpg with jpg/ppm and keep the rest of the filename */
+			strncpy(previewname, cnt->newfilename, strlen(cnt->newfilename)-3);
+			strcat(previewname, imageext(cnt));
+			put_picture(cnt, previewname, cnt->imgs.preview_image.image , FTYPE_IMAGE);
+		} else
+#endif /* HAVE_FFMPEG */
+		{
+			/* Save best preview-shot also when no movies are recorded or jpegpath
+			 * is used. Filename has to be generated - nothing available to reuse! */
+			//printf("preview_shot: different filename or picture only!\n");
+
+			/* conf.jpegpath would normally be defined but if someone deleted it by control interface
+			 * it is better to revert to the default than fail */
+			if (cnt->conf.jpegpath)
+				jpegpath = cnt->conf.jpegpath;
+			else
+				jpegpath = (char *)DEF_JPEGPATH;
+			
+			mystrftime(cnt, filename, sizeof(filename), jpegpath, &cnt->imgs.preview_image.timestamp_tm, NULL, 0);
+			snprintf(previewname, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt));
+
+			put_picture(cnt, previewname, cnt->imgs.preview_image.image, FTYPE_IMAGE);
+		}
+
+		/* restore global context values */
+		cnt->current_image = saved_current_image;
+	}
+}
