Index: motion.h =================================================================== --- motion.h (revision 172) +++ motion.h (working copy) @@ -137,6 +137,8 @@ #define DEF_TIMESTAMP "%Y-%m-%d\\n%T" #define DEF_EVENTSTAMP "%Y%m%d%H%M%S" +#define STATS_FPS_AVERAGE_CALC_TIME 6 /* 6 secs of averaging */ + #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" @@ -246,8 +248,8 @@ #include "netcam.h" +char my_machine_name[300]; // Used when finding machine's IP address - /* these used to be global variables but now each thread will have its own context @@ -302,6 +304,19 @@ int missing_frame_counter; /* counts failed attempts to fetch picture frame from camera */ int lost_connection; + unsigned long total_diffs; /* for statistics, how many pixels different so far? */ + unsigned long total_frames; /* for statistics, how many frames seen so far? */ + /* Next we reserve 1 more data store to hold the end of the 10th second */ + float stats_fps_average_data[1 + STATS_FPS_AVERAGE_CALC_TIME]; + float stats_averagefps; /* for statistics, average camera fps over time */ + float stats_motion_thruput; /* average motion fps over time */ + int stats_motion_accum; /* accumulator for above */ + long int stats_frame_delay; /* for illustration of idle time in thread */ + int v4l2_dev; + + unsigned int pid; /* for statistics, our pid. */ + + #if (defined(BSD)) int tuner_dev; #endif Index: motion.c =================================================================== --- motion.c (revision 172) +++ motion.c (working copy) @@ -21,6 +21,7 @@ #include "event.h" #include "picture.h" #include "rotate.h" +#include /** * tls_key_threadnr @@ -68,6 +69,8 @@ */ int restart=0; +time_t restart_time, program_run_time; /* Times recorded for last restart and when actually started */ + /** * context_init * @@ -100,6 +103,9 @@ cnt->pipe = -1; cnt->mpipe = -1; + cnt->total_diffs=0; + cnt->total_frames=0; + } /** @@ -417,6 +423,8 @@ /* Store thread number in TLS. */ pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); + syscall(SYS_getpid, cnt->pid); + cnt->diffs = 0; cnt->currenttime_tm = mymalloc(sizeof(struct tm)); cnt->eventtime_tm = mymalloc(sizeof(struct tm)); @@ -671,6 +679,17 @@ int minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; /* time in seconds to skip between capturing images */ int get_image = 1; /* Flag used to signal that we capture new image when we run the loop */ + /* Next variables are for statistics average fps calculation */ + float stats_frame_accum; + float stats_fps; + int stats_rolling_frame = 0; + + float rate_req; /* For netcam throttling */ + + /* Initialise the fps average data store to 0 */ + for (j=0; j< STATS_FPS_AVERAGE_CALC_TIME; j++) + cnt->stats_fps_average_data[j]=0; + /* Next two variables are used for snapshot and timelapse feature * time_last_frame is set to 1 so that first coming timelapse or second=0 * is acted upon. @@ -777,9 +796,109 @@ } else get_image = 1; + + /* Once a second Statistics actions */ + + cnt->netcam->secstart = 1; /* Signal to netcam raw fps measurement */ + cnt->netcam->dump_count = 0; + + stats_rolling_frame++; + if (stats_rolling_frame >= STATS_FPS_AVERAGE_CALC_TIME) + { + stats_rolling_frame = 0; + } + + /* Netcam throttling actions, but first test for netcam and streaming type */ + /* We use the 'raw camera fps' to control throttling, not the 'average' */ + /* as it would lead to a cause-effect loop. */ + if (cnt->conf.netcam_url && cnt->netcam->caps.streaming) { + cnt->netcam->dump_count = 0; + cnt->netcam->dump_keep = 0; + /* Work out the required fps taking into account minimum_frame_time */ + if (cnt->conf.minimum_frame_time > 0) + rate_req=1.0/cnt->conf.minimum_frame_time; + else + rate_req=cnt->conf.frame_limit; + /* Calculate the necessary interval and quantity to be dumped */ + if (cnt->netcam->cam_raw_fps > (int) (rate_req+0.25)) { + cnt->netcam->dump_qty = (int) cnt->netcam->cam_raw_fps - rate_req; + if (cnt->netcam->dump_qty > 0) + cnt->netcam->dump_interval = (float)cnt->netcam->cam_raw_fps / (float)rate_req; + else + cnt->netcam->dump_interval = 0.0; + } else { + cnt->netcam->dump_qty = 0; + cnt->netcam->dump_interval = 0.0; + } + + if (cnt->conf.setup_mode) + motion_log(LOG_DEBUG, 0, "throttle settings: fps req'd=%.1f last cam fps = %d dump_qty = %d dump_interval = %.1f", rate_req, cnt->netcam->cam_raw_fps, cnt->netcam->dump_qty, cnt->netcam->dump_interval); + } + + /* Still in once a second if */ + + /* Calculate average of the elements */ + stats_frame_accum = 0.0; + for (j=0; j < STATS_FPS_AVERAGE_CALC_TIME; j++) + { + stats_frame_accum += cnt->stats_fps_average_data[j]; + } + stats_fps = (float) stats_frame_accum / STATS_FPS_AVERAGE_CALC_TIME; + cnt->stats_averagefps = stats_fps; + + /* Enter the last second's frame count into the array */ + /* and add the number of frames to the tally, ... but */ + /* only if we have a good connection to camera */ + if (cnt->lost_connection == 0) { + if (cnt->conf.netcam_url) + cnt->stats_fps_average_data[stats_rolling_frame]=(float) 1000000/cnt->netcam->av_frame_time; + else + cnt->stats_fps_average_data[stats_rolling_frame]=(float) cnt->lastrate; + cnt->total_frames += (int) (cnt->stats_fps_average_data[stats_rolling_frame] + 0.5); + } + else + cnt->stats_fps_average_data[stats_rolling_frame]=0; + + + /* Once every 6 seconds Statistics actions + * Here we work out the 'motion throughput' (see below) + * and if in setup mode, report some useful information to the + * screen, ie. the average camera fps, the motion fps, and + * warn if configured fps not achieved. + */ + + if (cnt->currenttime % 6 == 0) { + + /* Find the 'Motion throughput' every 6 seconds. This is + * defined as the number of frames compared for motion + * which may be less than the fps of the camera, + * on a slow machine. This is because the Jpeg-decompress + * task, colourspace conversion, despeckle and actual + * compare can take a considerable time (in machine + * terms) and the rate of frames compared for motion + * will be less than the received fps of the camera. + * This metric is an attempt to show how much of a speed + * reduction the machine is causing. Causes of this can be: + * raw clock speed ; architecture ; optimization ; etc + * We find it every 6 secs to give better accuracy. + */ + + cnt->stats_motion_thruput = cnt->stats_motion_accum / 6.0 ; + cnt->stats_motion_accum = 0; + + if (cnt->conf.setup_mode) { + char temp[80]; + temp[0]='\0'; + for (j=0; j < STATS_FPS_AVERAGE_CALC_TIME; j++) + { + sprintf(&temp[strlen(temp)],"%.1f ", cnt->stats_fps_average_data[j]); + } + motion_log(LOG_DEBUG, 0, "Calculated average CAMERA fps as %.2f (last %d sec, data '%s').", stats_fps, STATS_FPS_AVERAGE_CALC_TIME, temp); + motion_log(LOG_DEBUG, 0, "Calculated average MOTION fps as %.2f (last 6 sec).", cnt->stats_motion_thruput); + } + } } - /* Increase the shots variable for each frame captured within this second */ cnt->shots++; @@ -1011,6 +1130,8 @@ }else if (cnt->imgs.labelsize_max) cnt->imgs.labelsize_max = 0; /* Disable labeling if enabled */ + cnt->stats_motion_accum++; /* Tally motion-detected frames */ + } else if (!cnt->conf.setup_mode) cnt->diffs = 0; @@ -1272,6 +1393,15 @@ } /* get_image end */ + /***** MOTION LOOP - STATISTICS SECTION *****/ + + /* Tally diffs for statistics. */ + cnt->total_diffs+=cnt->diffs; + + /* Count of total frames viewed moved to 'if get_next_frame' success (above) */ + + + /***** MOTION LOOP - SNAPSHOT FEATURE SECTION *****/ /* Did we get triggered to make a snapshot from control http? Then shoot a snap @@ -1292,13 +1422,10 @@ cnt->snapshot = 0; } - /***** MOTION LOOP - TIMELAPSE FEATURE SECTION *****/ #ifdef HAVE_FFMPEG - - if (cnt->conf.timelapse) { /* Check to see if we should start a new timelapse file. We start one when @@ -1487,6 +1614,8 @@ frame_delay = required_frame_time-elapsedtime - (rolling_average - required_frame_time); if (frame_delay > 0) { + cnt->stats_frame_delay = frame_delay; /* For stats reporting */ + /* Apply delay to meet frame time */ if (frame_delay > required_frame_time) frame_delay = required_frame_time; @@ -1499,6 +1628,10 @@ /* SLEEP as defined in motion.h A safe sleep using nanosleep */ SLEEP(0, delay_time_nsec); + + } else { + /* There was no delay time needed, or more likely it was negative. */ + cnt->stats_frame_delay = 0; } /* This will limit the framerate to 1 frame while not detecting @@ -1859,6 +1992,10 @@ /* Create the TLS key for thread number. */ pthread_key_create(&tls_key_threadnr, NULL); + /* Record the actual program start time (record restart time later) */ + program_run_time=time(NULL); + restart_time=time(NULL); /* Set this too, if first time through */ + do { if (restart) { /* Handle the restart situation. Currently the approach is to @@ -1871,6 +2008,9 @@ #ifndef WITHOUT_V4L SLEEP(5,0); // maybe some cameras needs less time #endif + /* Record the most recent restart time */ + restart_time=time(NULL); + motion_startup(0, argc, argv); /* 0 = skip daemon init */ } Index: netcam_jpeg.c =================================================================== --- netcam_jpeg.c (revision 172) +++ netcam_jpeg.c (working copy) @@ -301,6 +301,9 @@ if (debug_level > 3) motion_log(-1, 0, "***new pic delay successful***"); + } else { + if (debug_level > 3) + motion_log(-1,0,"no need to wait."); } netcam->imgcnt_last = netcam->imgcnt; @@ -437,8 +440,10 @@ ret = netcam_init_jpeg(netcam, &cinfo); - if (ret != 0) + if (ret != 0) { + motion_log(LOG_INFO, 0, "netcam_proc_jpeg: netcam_init_jpeg returned %d",ret); return ret; + } /* Do a sanity check on dimensions * If dimensions have changed we throw an Index: video_common.c =================================================================== --- video_common.c (revision 172) +++ video_common.c (working copy) @@ -715,6 +715,8 @@ cnt->imgs.height = height; } + cnt->v4l2_dev = viddevs[i]->v4l2; + cnt->imgs.type = viddevs[i]->v4l_fmt; switch (cnt->imgs.type) { Index: webhttpd.h =================================================================== --- webhttpd.h (revision 172) +++ webhttpd.h (working copy) @@ -15,5 +15,11 @@ void * motion_web_control(void *arg); void httpd_run(struct context **); +char *get_my_ip_address(void); + +extern time_t program_run_time, restart_time; + + + #endif Index: webhttpd.c =================================================================== --- webhttpd.c (revision 172) +++ webhttpd.c (working copy) @@ -17,12 +17,20 @@ #include #include +//Following are for get_my_ip_address +#include +#include +#include +#include +#include +#include + pthread_mutex_t httpd_mutex; int warningkill; // This is a dummy variable use to kill warnings when not checking sscanf and similar functions static const char* ini_template = - "\n" + "Motion HTTP Server\n" "\n"; static const char *set_template = @@ -1685,6 +1693,14 @@ static int handle_get(int client_socket, const char* url, void *userdata) { struct context **cnt=userdata; + char tmpin1[] = "%Y-%m-%d %T"; /* Format string in Stats page 'Generated' and start times */ + char tmpin2[] = "%Y-%m-%d %T"; /* Format string in ConnLost line, same as on grey image */ + char tmpout[80]; /* Contains result of mystrftime with above inputs */ + struct tm tmptime; + float req_rate; /* Calculated taking into account minimum_frame_time */ + char in_event[10]; /* To hold 'No' or 'Yes' plus \0 */ + int event_timer; + if (*url == '/' ){ int i=0; char *res=NULL; @@ -1704,6 +1720,8 @@ sprintf(res,"Thread %d
\n", y, y); send_template(client_socket, res); } + sprintf(res,"Statistics\n"); + send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); @@ -1713,8 +1731,235 @@ sprintf(res, "%d\n", y); send_template_raw(client_socket, res); } + sprintf(res,"Statistics\n"); + send_template_raw(client_socket, res); } } + else if (! (strcmp (url, "/statistics/")) ) { + int y=0; + int j; + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket,ini_template); + sprintf(res,"
Motion Statistics Page
"); + send_template(client_socket, res); + if (i==1) + localtime_r(&cnt[0]->currenttime, &tmptime); + else + localtime_r(&cnt[1]->currenttime, &tmptime); + mystrftime(cnt[0], tmpout, sizeof(tmpout), tmpin1, &tmptime, NULL, 0); + sprintf(res,"
Generated at %s
",tmpout); + send_template(client_socket, res); + sprintf(res,"
"); + send_template(client_socket, res); + + localtime_r(&program_run_time, &tmptime); + mystrftime(cnt[0], tmpout, sizeof(tmpout), tmpin1, &tmptime, NULL, 0); + sprintf(res,"MotionStart='%s'
\n", tmpout); + send_template(client_socket, res); + localtime_r(&restart_time, &tmptime); + mystrftime(cnt[0], tmpout, sizeof(tmpout), tmpin1, &tmptime, NULL, 0); + sprintf(res,"LastRestart='%s'
\n
\n", tmpout); + send_template(client_socket, res); + + for (y=0; yAll
\n
\n"); + send_template(client_socket, res); + continue; /* No further fields for the 'all' line */ + } else { + sprintf(res,"Thread %d ", y, y); + send_template(client_socket, res); + } + strcpy(tmpout,cnt[y]->conf.text_event); + /* motion_log(LOG_DEBUG, 0, "webhttpd: text_event='%s'",tmpout); */ + sprintf(res," \"%s\" ",tmpout); + send_template(client_socket, res); + if (cnt[y]->conf.netcam_url) { + sprintf(res,"(Netcam) RawFps=%d ", cnt[y]->netcam->cam_raw_fps); + send_template(client_socket, res); + if (cnt[y]->netcam->caps.streaming && cnt[y]->netcam->dump_qty > 0 && cnt[y]->netcam->dump_interval > 0.0 ) { + sprintf(res,"(Throttle %.0f%%) ", 100.0/cnt[y]->netcam->dump_interval); + send_template(client_socket, res); + } +#ifdef MOTION_V4L2 + } else if (!cnt[y]->v4l2_dev) { + sprintf(res,"(V4L1) "); + send_template(client_socket, res); + } else if (cnt[y]->v4l2_dev) { + sprintf(res,"(V4L2) "); + send_template(client_socket, res); +#endif + +#ifndef WITHOUT_V4L + } else if (cnt[y]->video_dev != -1) { + sprintf(res,"(V4L1/2) "); + send_template(client_socket, res); +#endif + } else { + sprintf(res,"(None) "); + send_template(client_socket, res); + } + if (cnt[y]->event_nr-1 == 0) /* No events yet, print a sensible time */ + strcpy(tmpout,"None"); + else + mystrftime(cnt[y], tmpout, sizeof(tmpout), tmpin1, cnt[y]->eventtime_tm, NULL, 0); + event_timer=cnt[y]->currenttime - cnt[y]->lasttime; + if (event_timer > cnt[y]->conf.gap) { + strcpy(in_event,"No"); + event_timer = 0; + } else { + strcpy(in_event,"Yes"); + } + sprintf(res,"Events=%d LastEvent='%s' Gap=%d InEvent=%s GapTimer=%d
\n", + cnt[y]->event_nr-1, tmpout,cnt[y]->conf.gap, in_event, event_timer); + send_template(client_socket, res); + if (cnt[y]->conf.minimum_frame_time > 0) + req_rate=1.0/cnt[y]->conf.minimum_frame_time; + else + req_rate=cnt[y]->conf.frame_limit; + sprintf(res,"Thread %d Frames=%lu fps=%.2f Lastrate=%d Req.Rate=%.1f FrameDelay[ms]=%.0f fps[6s]='", + y, y, cnt[y]->total_frames, cnt[y]->stats_averagefps, cnt[y]->lastrate, req_rate, cnt[y]->stats_frame_delay/1000.0); + send_template(client_socket, res); + + for (j=0; j < STATS_FPS_AVERAGE_CALC_TIME ; j++){ + sprintf(res,"%.1f ", cnt[y]->stats_fps_average_data[j] ); + send_template(client_socket, res); + } + + sprintf(res,"'
\n"); + send_template(client_socket, res); + sprintf(res,"Thread %d MotionThruPut=%.1f Size=%dx%d Diff=%d Thresh=%d Total=%lu Pid=%u MissFrameCtr=%d ", + y, y, cnt[y]->stats_motion_thruput, cnt[y]->imgs.width, cnt[y]->imgs.height, + cnt[y]->diffs, cnt[y]->threshold, cnt[y]->total_diffs, cnt[y]->pid, cnt[y]->missing_frame_counter); + send_template(client_socket, res); + sprintf(res,"ConnLost="); + send_template(client_socket, res); + if (cnt[y]->lost_connection) { + localtime_r(&cnt[y]->connectionlosttime, &tmptime); + mystrftime(cnt[y], tmpout, sizeof(tmpout), tmpin2, &tmptime, NULL, 0); + sprintf(res,"'%s'", tmpout); + } else { + sprintf(res,"'No'"); + } + send_template(client_socket, res); + sprintf(res,"
\n"); + send_template(client_socket, res); + if (cnt[y]->conf.webcam_port > 0) { + sprintf(res,"Thread %d Webcam Image", get_my_ip_address(), cnt[y]->conf.webcam_port, y); + } else { + sprintf(res,"Thread %d Webcam Image not configured", y); + } + send_template(client_socket, res); + sprintf(res,"

\n"); + send_template(client_socket, res); + } + sprintf(res,"
"); + send_template(client_socket, res); + sprintf(res,"<- back\n"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + /* Raw output should now be the same as HTML output */ + send_template_ini_client_raw(client_socket); + sprintf(res,"Motion Statistics Page\n\n"); + send_template_raw(client_socket, res); + localtime_r(&cnt[1]->currenttime, &tmptime); + mystrftime(cnt[1], tmpout, sizeof(tmpout), tmpin1, &tmptime, NULL, 0); + sprintf(res,"Generated at %s\n\n",tmpout); + send_template_raw(client_socket, res); + + localtime_r(&program_run_time, &tmptime); + mystrftime(cnt[0], tmpout, sizeof(tmpout), tmpin1, &tmptime, NULL, 0); + sprintf(res,"MotionStart='%s'\n", tmpout); + send_template(client_socket, res); + localtime_r(&restart_time, &tmptime); + mystrftime(cnt[0], tmpout, sizeof(tmpout), tmpin1, &tmptime, NULL, 0); + sprintf(res,"LastRestart='%s'\n\n", tmpout); + send_template(client_socket, res); + + for (y=1; yconf.text_event); + /* motion_log(LOG_DEBUG, 0, "webhttpd: text_event='%s'",tmpout); */ + sprintf(res,"%d \"%s\" ", y, tmpout); + send_template(client_socket, res); + + if (cnt[y]->conf.netcam_url) { + sprintf(res,"(Netcam) RawFps=%d ", cnt[y]->netcam->cam_raw_fps); + send_template(client_socket, res); + if (cnt[y]->netcam->caps.streaming && cnt[y]->netcam->dump_qty > 0 && cnt[y]->netcam->dump_interval > 0.0 ) { + sprintf(res,"(Throttle %.0f%%) ", 100.0/cnt[y]->netcam->dump_interval); + send_template(client_socket, res); + } +#ifdef MOTION_V4L2 + } else if (!cnt[y]->v4l2_dev) { + sprintf(res,"(V4L1) "); + send_template(client_socket, res); + } else if (cnt[y]->v4l2_dev) { + sprintf(res,"(V4L2) "); + send_template(client_socket, res); +#endif + +#ifndef WITHOUT_V4L + } else if (cnt[y]->video_dev != -1) { + sprintf(res,"(V4L1/2) "); + send_template(client_socket, res); +#endif + } else { + sprintf(res,"(None) "); + send_template(client_socket, res); + } + + if (cnt[y]->event_nr-1 == 0) /* No events yet, print a sensible time */ + strcpy(tmpout,"None"); + else + mystrftime(cnt[y], tmpout, sizeof(tmpout), tmpin1, cnt[y]->eventtime_tm, NULL, 0); + event_timer=cnt[y]->currenttime - cnt[y]->lasttime; + if (event_timer > cnt[y]->conf.gap) { + strcpy(in_event,"No"); + event_timer = 0; + } else { + strcpy(in_event,"Yes"); + } + + sprintf(res,"Events=%d LastEvent='%s' Gap=%d InEvent=%s GapTimer=%d\n", + cnt[y]->event_nr-1, tmpout, cnt[y]->conf.gap, in_event, event_timer); + send_template_raw(client_socket, res); + if (cnt[y]->conf.minimum_frame_time > 0) + req_rate=1.0/cnt[y]->conf.minimum_frame_time; + else + req_rate=cnt[y]->conf.frame_limit; + sprintf(res, "%d Frames=%lu fps=%.2f Lastrate=%d Req.Rate=%.1f FrameDelay[ms]=%.3f fps[6s]='", y, cnt[y]->total_frames, cnt[y]->stats_averagefps, cnt[y]->lastrate, req_rate, cnt[y]->stats_frame_delay/1000.0); + send_template_raw(client_socket, res); + + for (j=0; j < STATS_FPS_AVERAGE_CALC_TIME ; j++){ + sprintf(res,"%.1f ", cnt[y]->stats_fps_average_data[j] ); + send_template(client_socket, res); + } + + sprintf(res,"'\n"); + send_template(client_socket, res); + + sprintf(res, "%d MotionThruPut=%.1f Size=%dx%d Diff=%d Thresh=%d Total=%lu Pid=%u MissFrameCntr=%d ", + y, cnt[y]->stats_motion_thruput, cnt[y]->imgs.width, cnt[y]->imgs.height, + cnt[y]->diffs, cnt[y]->threshold, cnt[y]->total_diffs, cnt[y]->pid, cnt[y]->missing_frame_counter); + send_template_raw(client_socket, res); + sprintf(res,"ConnLost="); + send_template_raw(client_socket, res); + if (cnt[y]->lost_connection) { + localtime_r(&cnt[y]->connectionlosttime, &tmptime); + mystrftime(cnt[y], tmpout, sizeof(tmpout), tmpin2, &tmptime, NULL, 0); + sprintf(res,"'%s'", tmpout); + } else { + sprintf(res,"'No'"); + } + send_template_raw(client_socket, res); + sprintf(res,"\n\n"); + send_template_raw(client_socket, res); + } + } + } else { char command[256] = {'\0'}; char slash; @@ -2211,3 +2456,45 @@ httpd_run(cnt); pthread_exit(NULL); } + +/* To get machine's IP address */ +/* This is very useful when connecting to the Statistics page from a remote */ +/* machine, as 'localhost' then refers to the browsing machine, not the one */ +/* running Motion. */ + + +char *get_my_ip_address(void) +{ + struct hostent *he; + struct sockaddr_in sin; + //char my_machine_name[ 300 ]; // Defined in motion.h + + if (gethostname (my_machine_name, sizeof (my_machine_name))) + { + //motion_log(LOG_DEBUG, 0, "get_my_ip_address: gethostname error, using localhost ip address."); + strcpy(my_machine_name,"127.0.0.1"); + return my_machine_name; + } + + he = gethostbyname (my_machine_name); + if (he == NULL) { + if (errno == TRY_AGAIN){ + motion_log(LOG_DEBUG, 0, "get_my_ip_address: gethostbyname gave Try Again, using localhost ip address."); + } else { + motion_log(LOG_DEBUG, 0, "get_my_ip_address: gethostbyname error, using localhost ip address."); + } + strcpy(my_machine_name,"127.0.0.1"); + return my_machine_name; + } + + bzero (&sin, sizeof (struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons (80); + bcopy (he->h_addr_list[ 0 ], &sin.sin_addr, he->h_length); + + motion_log(LOG_DEBUG, 0, "get_my_ip_address: ip address is %s", inet_ntoa (sin.sin_addr)); + strcpy(my_machine_name,inet_ntoa (sin.sin_addr)); + + return my_machine_name; +} + Index: netcam.h =================================================================== --- netcam.h (revision 172) +++ netcam.h (working copy) @@ -204,6 +204,15 @@ int imgcnt; /* count for # of received jpegs */ int imgcnt_last; /* remember last count to check if a new image arrived */ + + int secstart; /* Start of second marker for raw fps measurement */ + int cam_raw_fps; /* Used to determine throttling */ + int cam_raw_fps_counter; + int dump_count; /* simple count of frames dumped this fps throttle cycle */ + int dump_keep; + float dump_accum; + float dump_interval; /* interval of frames to count BEFORE dumping a frame */ + int dump_qty; /* number of frames to dump. Set to 0 to prevent. */ int error_count; /* simple count of number of errors since last good frame was received */ Index: netcam.c =================================================================== --- netcam.c (revision 172) +++ netcam.c (working copy) @@ -802,6 +802,7 @@ * * * Returns: 0 for success, -1 for error + * -2 for framed dropped by throttling (non fatal) * */ static int netcam_read_html_jpeg(netcam_context_ptr netcam) @@ -993,6 +994,44 @@ remaining -= retval; } } + + /* read is complete - maintain the 'raw camera fps' counter */ + /* This is needed since if we are throttling, the other fps counter */ + /* is the 'throttled' figure. We need this one to calculate throttle */ + /* parameters */ + + netcam->cam_raw_fps_counter++; + if (netcam->secstart==1) { + netcam->cam_raw_fps = netcam->cam_raw_fps_counter; + //motion_log(-1,0,"throttle: raw cam fps = %d", netcam->cam_raw_fps); + netcam->cam_raw_fps_counter = 0; + netcam->secstart = 0; + } + + /* read is complete - but if we are throttling (only if streaming camera) */ + /* we work out if it's time to dump a frame */ + + if (netcam->caps.streaming && netcam->dump_qty>0 && netcam->dump_interval>0.0) { + /* Netcam throttling code */ + //motion_log(-1,0,"throttle: dump variables are dump_accum = %.1f dump_interval= %.1f.", netcam->dump_accum, netcam->dump_interval); + if (netcam->dump_count < netcam->dump_qty) { + /* Need to dump more frames in this second */ + + if (netcam->dump_accum >= 1.0) { + netcam->dump_count++; /* Dump the frame */ + netcam->dump_accum-=1.0; + //motion_log(-1,0,"throttle: new pic arrived but dumped it (dump_count = %d ).", netcam->dump_count); + return -2; /* Exit code for throttling */ + } else { + /* It is not time to dump a frame yet */ + netcam->dump_keep++; + netcam->dump_accum+=netcam->dump_interval; + } + } + /* Else drop through to process frame (in case maths incorrect) */ + //motion_log(-1,0,"throttle: new pic arrived and retained (dump_keep = %d ).", netcam->dump_keep); + } + /* * read is complete - set the current 'receiving' buffer atomically @@ -1225,8 +1264,9 @@ } } } - if (netcam->get_image(netcam) < 0) { - motion_log(LOG_ERR, 0, "Error getting jpeg image"); + if ((retval=netcam->get_image(netcam)) < 0) { + if (retval!=-2) /* -2 indicates throtting has dropped a frame so do not log error */ + motion_log(LOG_ERR, 0, "Error getting jpeg image"); /* if FTP connection, attempt to re-connect to server */ if (netcam->ftp) { close(netcam->ftp->control_file_desc); @@ -1235,7 +1275,7 @@ } } continue; - } + } /* * FIXME * Need to check whether the image was received / decoded @@ -1725,7 +1765,7 @@ } return NETCAM_NOTHING_NEW_ERROR; } - + /* * If we are controlling a non-streaming camera, we synchronize the * motion main-loop with the camera-handling thread through a signal, @@ -1736,7 +1776,7 @@ netcam->start_capture = 1; pthread_cond_signal(&netcam->cap_cond); pthread_mutex_unlock(&netcam->mutex); - } + } /* * If an error occurs in the JPEG decompression which follows this,