Index: conf.c
===================================================================
--- conf.c	(revisión: 468)
+++ conf.c	(copia de trabajo)
@@ -82,6 +82,7 @@
     ffmpeg_output:                  0,
     extpipe:                        NULL,
     useextpipe:                     0,
+    smooth_video:                   0,
     ffmpeg_output_debug:            0,
     ffmpeg_bps:                     DEF_FFMPEG_BPS,
     ffmpeg_vbr:                     DEF_FFMPEG_VBR,
@@ -148,6 +149,7 @@
     text_left:                      NULL,
     text_right:                     DEF_TIMESTAMP,
     text_event:                     DEF_EVENTSTAMP,
+    cameraname:                     "CAMERA NAME",
     text_double:                    0,
     despeckle_filter:               NULL,
     area_detect:                    NULL,
@@ -332,6 +334,17 @@
     print_int
     },
     {
+    "force_framerate",
+    "# Force the number of frames per second set in framerate.\n"
+    "# This is useful for capture cards on busy system where motion\n"
+    "# often failed to detect the correct framerate.\n"
+    "# Valid range: 2-100. Default: 100 (almost no limit).",
+    0,
+    CONF_OFFSET(force_framerate),
+    copy_bool,
+    print_bool
+    },
+    {
     "minimum_frame_time",
     "# Minimum time in seconds between capturing picture frames from the camera.\n"
     "# Default: 0 = disabled - the capture rate is given by the camera framerate.\n"
@@ -765,6 +778,19 @@
     print_string
     },
     {
+    "smooth_video",
+    "\n############################################################\n"
+    "# Normally, video is recorded as __precap+motion+postcap____\n"
+    "# This option changes the default behavior of the video \n"
+    "# capturing so that it is __precap+event-start-->event-end__ \n"
+    "############################################################\n"
+    "# Use smooth_video on to enable this feature (default: off)\n",
+    0,
+    CONF_OFFSET(smooth_video),
+    copy_bool,
+    print_bool
+    },
+    {
     "snapshot_interval",
     "\n############################################################\n"
     "# Snapshots (Traditional Periodic Webcam File Output)\n"
@@ -841,6 +867,16 @@
     print_bool
     },
     {
+    "cameraname",
+     "# This option defines the value of the special event conversion specifier %e\n"
+     "# Default: Camera 1\n"
+     "# The idea is that %e can be used as part of the filename to identify a camera\n",
+    0,
+    CONF_OFFSET(cameraname),
+    copy_string,
+    print_string
+    },
+    {
     "text_event",
     "# This option defines the value of the special event conversion specifier %C\n"
     "# You can use any conversion specifier in this option except %C. Date and time\n"
@@ -1038,7 +1074,7 @@
     CONF_OFFSET(webcontrol_authentication),
     copy_string,
     print_string
-    },   
+    },    
     {
     "track_type",
     "\n############################################################\n"
Index: conf.h
===================================================================
--- conf.h	(revisión: 468)
+++ conf.h	(copia de trabajo)
@@ -38,7 +38,9 @@
     int input;
     int norm;
     int frame_limit;
+    int force_framerate;
     int quiet;
+    int smooth_video;
     int useextpipe; /* ext_pipe on or off */
     const char *extpipe; /* full command line for pipe -- must accept YUV420P images  */
     const char *picture_type;
@@ -120,6 +122,7 @@
     const char *text_left;
     const char *text_right;
     const char *text_event;
+    const char *cameraname;
     int text_double;
     const char *despeckle_filter;
     const char *area_detect;
Index: motion.c
===================================================================
--- motion.c	(revisión: 468)
+++ motion.c	(copia de trabajo)
@@ -418,7 +418,7 @@
         } else if (cnt->locate_motion_style == LOCATE_REDCROSS) {
             alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_REDCROSS, 
                                   LOCATE_BOTH, cnt->process_thisframe);
-        }    
+        }
     }
 
     /* Calculate how centric motion is if configured preview center*/
@@ -539,49 +539,48 @@
                           cnt->imgs.width, t, cnt->conf.text_double);
             }
 
-            /* 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);
+//smoothvideo: payne 10/15/2008
+//located in two places
+            /* process this only if we're outputting to a movie */
+            if (cnt->conf.smooth_video && (cnt->ffmpeg_output || cnt->conf.useextpipe)) {
+                if (cnt->prevtv.tv_sec > 0) {
+                    time_t elapsedus = ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec - cnt->prevtv.tv_sec) * 1000000) +
+                                        (cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec - cnt->prevtv.tv_usec);
+                    int myinsertnum = ((elapsedus + cnt->leftovers) / cnt->usinterval);
+                    /* the larger loop we're in is only called to process
+                     * motion -- there will be one frame processed later
+                     * REGARDLESS of what we do here, so, account for it */
+                    cnt->leftovers += elapsedus - (cnt->usinterval*(myinsertnum+1));
 
-//#ifdef HAVE_FFMPEG
-            /* Check if we must add any "filler" frames into movie to keep up fps */
-            if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) {
-                /* movie_last_shoot is -1 when file is created,
-                 * we don't know how many frames there is in first sec */
-                if (cnt->movie_last_shot >= 0) {
-                    if (debug_level >= CAMERA_DEBUG) {
-                        int frames = cnt->movie_fps - (cnt->movie_last_shot + 1);
-                        if (frames > 0) {
-                            char tmp[15];
-                            motion_log(LOG_DEBUG, 0, "%s: Added %d fillerframes into movie", 
-                                       __FUNCTION__, frames);
-                            sprintf(tmp, "Fillerframes %d", frames);
-                            draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 40, 
-                                      cnt->imgs.width, tmp, cnt->conf.text_double);
+                    if (debug_level >= 2)
+                        motion_log(LOG_INFO, 0, "current, prev, fps, us, interval, insertnum, leftovers: "
+                                   "%d.%06ld, %d.%06ld, %d, %d, %d, %d, %d",
+                                   cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec,
+                                   cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec,
+                                   cnt->prevtv.tv_sec, cnt->prevtv.tv_usec, cnt->movie_fps, elapsedus,
+                                   cnt->usinterval, myinsertnum, cnt->leftovers);
+
+
+                    while (myinsertnum > 0) {
+                        event(cnt, EVENT_FFMPEG_PUT, cnt->previmg, NULL, NULL,
+                              &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm);
+                        myinsertnum--;
+                    }
                         }
+                cnt->prevtv = cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv;
+                memcpy(cnt->previmg, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, cnt->imgs.size);
                     }
-                    /* Check how many frames it was last sec */
-                    while ((cnt->movie_last_shot + 1) < cnt->movie_fps) {
-                        /* Add a filler frame into encoder */
-                        event(cnt, EVENT_FFMPEG_PUT,
-                              cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, 
-                              &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm);
-
-                        cnt->movie_last_shot++;
-                    }
-                }
-                cnt->movie_last_shot = 0;
-            } else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot != (cnt->movie_last_shot + 1)) {
-                /* We are out of sync! Properbly we got motion - no motion - motion */
-                cnt->movie_last_shot = -1;
+            /* Output the picture to jpegs (if motion), ffmpeg and extpipe (regardless)*/
+            if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) && 
+                ! (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_PRECAP)) {
+                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);
+            } else {
+                event(cnt, EVENT_FFMPEG_PUT,
+                      cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, 
+                      &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm);
             }
-
-            /* Save last shot added to movie
-             * only when we not are within first sec */
-            if (cnt->movie_last_shot >= 0)
-                cnt->movie_last_shot = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot;
-//#endif            
         }
 
         /* Mark the image as saved */
@@ -682,6 +681,10 @@
         cnt->imgs.type = VIDEO_PALETTE_YUV420P;
     }
 
+    /* setup cnt->previmg (previous img for ffmpeg streaming) */
+    cnt->previmg = mymalloc(cnt->imgs.size);
+    memset(cnt->previmg, 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);
@@ -1019,6 +1022,12 @@
     if (cnt->conf.sqlite3_db)
         sqlite3_close(cnt->database_sqlite3);
 #endif /* HAVE_SQLITE3 */
+
+     /* Cleanup the previmg buffer */
+     if (cnt->previmg) {
+         free(cnt->previmg);
+         cnt->previmg = NULL;
+     }
 }
 
 /**
@@ -1155,8 +1164,10 @@
             image_ring_resize(cnt, frame_buffer_size);
         
         /* Get time for current frame */
-        cnt->currenttime = time(NULL);
+        gettimeofday(&cnt->tv, NULL);
+        cnt->currenttime = cnt->tv.tv_sec;
 
+
         /* localtime returns static data and is not threadsafe
          * so we use localtime_r which is reentrant and threadsafe
          */
@@ -1233,6 +1244,8 @@
 
             /* Store time with pre_captured image */
             cnt->current_image->timestamp = cnt->currenttime;
+            cnt->current_image->tv = cnt->tv;
+
             localtime_r(&cnt->current_image->timestamp, &cnt->current_image->timestamp_tm);
 
             /* Store shot number with pre_captured image */
@@ -1796,12 +1809,58 @@
                      * images get a timestamp from previous event.
                      */
                     cnt->text_event_string[0] = '\0';
+
+
+                    cnt->gapfix = 0;
+                    if ((cnt->currenttime - cnt->lasttime < cnt->conf.event_gap) && cnt->conf.event_gap > 0) {
+                        cnt->current_image->flags |= (IMAGE_TRIGGER);
+                        cnt->postcap = cnt->conf.post_capture;
+                        motion_detected(cnt, cnt->video_dev, cnt->current_image);
+                        cnt->gapfix = 1;
+                    } else {
+                        /* And, finally again, we reset the prevtv structure. */
+                        cnt->prevtv.tv_sec = 0;
+                        cnt->prevtv.tv_usec = 0;
+                    }
                 }
             }
 
             /* Save/send to movie some images */
             process_image_ring(cnt, 2);
 
+            /* For movie realtime sync.  code needed here so we don't run into issues
+            with high numbers of queued images, which would slow down processing */
+            if (cnt->conf.smooth_video && (cnt->ffmpeg_output || (cnt->conf.useextpipe))) {
+                /* process this if we're outputting to a movie only.. */
+                if (cnt->prevtv.tv_sec > 0) { /* only run through this block of code if we're in an event --
+                                               minimal processing is KEY =) */
+                    time_t elapsedus = ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec - cnt->prevtv.tv_sec) * 1000000) +
+                                        (cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec - cnt->prevtv.tv_usec);
+                    int myinsertnum = ((elapsedus + cnt->leftovers) / cnt->usinterval);
+
+                    if (myinsertnum > 0) {
+                        cnt->leftovers += elapsedus - (cnt->usinterval*(myinsertnum));
+
+                        if (debug_level >= 2)
+                            motion_log(LOG_INFO, 0, "KEEPUP: current, prev, fps, us, interval, insertnum, leftovers: "
+                                       "%d.%06ld, %d.%06ld, %d, %d, %d, %d, %d",
+                                       cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec,
+                                       cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec,
+                                       cnt->prevtv.tv_sec, cnt->prevtv.tv_usec, cnt->movie_fps, elapsedus,
+                                       cnt->usinterval, myinsertnum, cnt->leftovers);
+
+                        while (myinsertnum > 0) {
+                            event(cnt, EVENT_FFMPEG_PUT, cnt->previmg, NULL, NULL,
+                                  &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm);
+                            myinsertnum--;
+                        }
+
+                        cnt->prevtv = cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv;
+                        memcpy(cnt->previmg, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, cnt->imgs.size);
+                    }
+                }
+            }
+
         /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/
 
             /* If CAMERA_VERBOSE enabled output some numbers to console */
@@ -2152,8 +2211,8 @@
             motion_log(LOG_ERR, 1, "%s: Exit motion, cannot create process id file (pid file) %s",
                        __FUNCTION__, cnt_list[0]->conf.pid_file);
             if (ptr_logfile) 
-                myfclose(ptr_logfile);    
-            exit(0);    
+                myfclose(ptr_logfile);
+            exit(0);
         }
     }
 
@@ -2735,7 +2794,7 @@
 
         if (!start)
             motion_log(LOG_INFO, 0, "%s: creating directory %s", __FUNCTION__, buffer);
-        
+
         free(buffer);
     }
 
@@ -2965,6 +3024,13 @@
                         pthread_getspecific(tls_key_threadnr));
                 break;
 
+            case 'e': // cameraname
+                if (cnt->conf.cameraname && cnt->conf.cameraname[0])
+                    snprintf(tempstr, PATH_MAX, "%s", cnt->conf.cameraname);
+                else
+                    ++pos_userformat;
+                break;
+
             case 'C': // text_event
                 if (cnt->text_event_string && cnt->text_event_string[0])
                     snprintf(tempstr, PATH_MAX, "%s", cnt->text_event_string);
@@ -2992,6 +3058,26 @@
                     ++pos_userformat;
                 break;
 
+            case 'u': // tv_usec -- really "us" for micro time, returns microsecond part
+                      // ux = two decimals
+                      // ug = gap?
+                switch(*++pos_userformat) {
+                    case 's': // %us, just return microseconds (6 digits padded)
+                        sprintf(tempstr, "%06ld",cnt->current_image->tv.tv_usec);
+                        break;
+                    case 'x': // %ux, just return microseconds (2 digits padded)
+                        sprintf(tempstr, "%02ld",cnt->current_image->tv.tv_usec/4);
+                        break;
+                    case 'g': // %ug, gap?
+                        sprintf(tempstr, "%d",cnt->gapfix);
+                        break;
+                }
+                if (tempstr[0])  //if %ut or %us
+                    break;
+                else
+                    --pos_userformat;
+
+
             default: // Any other code is copied with the %-sign
                 *format++ = '%';
                 *format++ = *pos_userformat;
Index: motion.h
===================================================================
--- motion.h	(revisión: 468)
+++ motion.h	(copia de trabajo)
@@ -88,8 +88,8 @@
                 while (nanosleep(&tv, &tv) == -1); \
         } 
 
+
 #define CLEAR(x) memset(&(x), 0, sizeof(x))
-
 #if defined(WITHOUT_V4L) || defined(BSD)
  
 #define VIDEO_PALETTE_GREY      1       /* Linear greyscale */
@@ -223,6 +223,7 @@
 struct image_data {
     unsigned char *image;
     int diffs;
+    struct timeval tv;
     time_t timestamp;           /* Timestamp when image was captured */
     struct tm timestamp_tm;
     int shot;                   /* Sub second timestamp count */
@@ -328,6 +329,7 @@
 
     struct config conf;
     struct images imgs;
+    unsigned char *previmg;
     struct trackoptions track;
     struct netcam_context *netcam;
     struct image_data *current_image;        /* Pointer to a structure where the image, diffs etc is stored */
@@ -367,6 +369,11 @@
     struct tm *eventtime_tm;
 
     time_t currenttime;
+    struct timeval tv;                      /* store current time of image */
+    struct timeval prevtv;                  /* previous pic tv.. needs to stick around */
+    struct timeval prevmotiontv;            /* previous pic detection tv.. needs to stick around */
+    int gapfix;                             /* set to 1 if gap not fulfilled; used for output */
+    time_t leftovers;                       /* leftover microseconds */
     time_t lasttime;
     time_t eventtime;
     time_t connectionlosttime;               /* timestamp from connection lost */
@@ -405,6 +412,8 @@
 #endif
 
     int movie_fps;
+    time_t usinterval;                 /* microsecond interval, 1000000 / movie_fps */
+
     char newfilename[PATH_MAX];
     char extpipefilename[PATH_MAX];
     int movie_last_shot;
Index: motion-dist.conf.in
===================================================================
--- motion-dist.conf.in	(revisión: 468)
+++ motion-dist.conf.in	(copia de trabajo)
@@ -89,6 +89,12 @@
 # Valid range: 2-100. Default: 100 (almost no limit).
 framerate 2
 
+# Force the number of frames per second set in framerate.
+# This is useful for capture cards on busy system where motion
+# often failed to detect the correct framerate.
+# Valid range: 2-100. Default: 100 (almost no limit).
+force_framerate 100
+
 # Minimum time in seconds between capturing picture frames from the camera.
 # Default: 0 = disabled - the capture rate is given by the camera framerate.
 # This option is used when you want to capture images at a rate lower than 2 per second.
@@ -316,8 +322,14 @@
 # Generally, use '-' for STDIN...
 ;extpipe mencoder -demuxer rawvideo -rawvideo w=320:h=240:i420 -ovc x264 -x264encopts bframes=4:frameref=1:subq=1:scenecut=-1:nob_adapt:threads=1:keyint=1000:8x8dct:vbv_bufsize=4000:crf=24:partitions=i8x8,i4x4:vbv_maxrate=800:no-chroma-me -vf denoise3d=16:12:48:4,pp=lb -of   avi -o %f.avi - -fps %fps
 
+############################################################
+# Normally, video is recorded as __precap+motion+postcap____
+# This option changes the default behavior of the video 
+# capturing so that it is __precap+event-start-->event-end__ 
+############################################################
+# Use smooth_video on to enable this feature (default: off) 
+;smooth_video
 
-
 ############################################################
 # Snapshots (Traditional Periodic Webcam File Output)
 ############################################################
@@ -362,6 +374,11 @@
 # Text is placed in lower left corner
 ; text_left CAMERA %t
 
+# This option defines the value of the special event conversion specifier %e
+# Default: Camera 1
+# The idea is that %e can be used as part of the filename to identify a camera
+;cameraname 
+
 # Draw the number of changed pixed on the images (default: off)
 # Will normally be set to off except when you setup and adjust the motion settings
 # Text is placed in upper right corner
Index: event.c
===================================================================
--- event.c	(revisión: 468)
+++ event.c	(copia de trabajo)
@@ -172,7 +172,7 @@
             int res;
             char *errmsg = 0;
             res = sqlite3_exec(cnt->database_sqlite3, sqlquery, NULL, 0, &errmsg);
-            if (res != SQLITE_OK ) {
+            if (res != SQLITE_OK) {
                 motion_log(LOG_ERR, 0, "%s: SQLite error was %s", __FUNCTION__,  errmsg);
                 sqlite3_free(errmsg);
             }
@@ -467,10 +467,14 @@
     if (debug_level >= CAMERA_INFO) 
         motion_log(LOG_DEBUG, 0, "%s FPS %d", __FUNCTION__, cnt->movie_fps);
 
-    if (cnt->movie_fps > 30) 
+    if (cnt->conf.force_framerate)
+        cnt->movie_fps = cnt->conf.frame_limit;
+    else if (cnt->movie_fps > 30)
         cnt->movie_fps = 30;
     else if (cnt->movie_fps < 2) 
         cnt->movie_fps = 2;
+
+    cnt->usinterval = 1000000 / cnt->movie_fps;  /* less calculations are good... */
 }
 
 #ifdef HAVE_FFMPEG
@@ -766,7 +770,7 @@
     EVENT_IMAGE_SNAPSHOT,
     event_image_snapshot
     },
-#if !defined(WITHOUT_V4L) && !defined(BSD)
+#if !defined(WITHOUT_V4L) && !defined(BSD)    
     {
     EVENT_IMAGE,
     event_vid_putpipe
@@ -775,7 +779,7 @@
     EVENT_IMAGEM,
     event_vid_putpipe     
     },
-#endif /* !WITHOUT_V4L && !BSD */
+#endif /* !WITHOUT_V4L && !BSD */    
     {
     EVENT_STREAM,
     event_stream_put
Index: alg.c
===================================================================
--- alg.c	(revisión: 468)
+++ alg.c	(copia de trabajo)
@@ -1242,7 +1242,8 @@
  *
  */
 /* Seconds */
-#define ACCEPT_STATIC_OBJECT_TIME 10
+// -- changed from 10 to 2
+#define ACCEPT_STATIC_OBJECT_TIME 2
 #define EXCLUDE_LEVEL_PERCENT 20
 void alg_update_reference_frame(struct context *cnt, int action) 
 {
