Index: conf.c
===================================================================
--- conf.c (revision 275)
+++ conf.c (working copy)
@@ -117,6 +117,7 @@
on_motion_detected: NULL,
on_movie_start: NULL,
on_movie_end: NULL,
+ on_camera_lost: NULL,
motionvidpipe: NULL,
netcam_url: NULL,
netcam_userpass: NULL,
@@ -1115,6 +1116,17 @@
copy_string,
print_string
},
+ {
+ "on_camera_lost",
+ "# Command to be executed when a camera can't be opened or if it is lost\n"
+ "# NOTE: There is situations when motion don't detect a lost camera!\n"
+ "# It depends on the driver, some drivers dosn't detect a lost camera at all\n"
+ "# Some hangs the motion thread. Some even hangs the PC! (default: none)\n",
+ 0,
+ CONF_OFFSET(on_camera_lost),
+ copy_string,
+ print_string
+ },
#ifdef HAVE_FFMPEG
{
"on_movie_start",
Index: conf.h
===================================================================
--- conf.h (revision 275)
+++ conf.h (working copy)
@@ -101,6 +101,7 @@
char *on_motion_detected;
char *on_movie_start;
char *on_movie_end;
+ char *on_camera_lost;
const char *motionvidpipe;
const char *netcam_url;
const char *netcam_userpass;
Index: event.c
===================================================================
--- event.c (revision 275)
+++ event.c (working copy)
@@ -329,6 +329,14 @@
cnt->snapshot = 0;
}
+static void event_camera_lost(struct context *cnt, int type ATTRIBUTE_UNUSED,
+ unsigned char *img ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
+ void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm)
+{
+ if (cnt->conf.on_camera_lost)
+ exec_command(cnt, cnt->conf.on_camera_lost, NULL, 0);
+}
+
#ifdef HAVE_FFMPEG
static void grey2yuv420p(unsigned char *u, unsigned char *v, int width, int height)
{
@@ -660,6 +668,10 @@
},
#endif /* HAVE_FFMPEG */
{
+ EVENT_CAMERA_LOST,
+ event_camera_lost
+ },
+ {
EVENT_STOP,
event_stop_webcam
},
Index: event.h
===================================================================
--- event.h (revision 275)
+++ event.h (working copy)
@@ -28,6 +28,7 @@
#define EVENT_DEBUG 65536
#define EVENT_CRITICAL 131072
#define EVENT_AREA_DETECTED 262144
+#define EVENT_CAMERA_LOST 524288
#
typedef void(* event_handler)(struct context *, int, unsigned char *, char *, void *, struct tm *);
Index: motion.c
===================================================================
--- motion.c (revision 275)
+++ motion.c (working copy)
@@ -22,6 +22,11 @@
#include "picture.h"
#include "rotate.h"
+/* Forward declarations */
+static int motion_init(struct context *cnt);
+static void motion_cleanup(struct context *cnt);
+
+
/**
* tls_key_threadnr
*
@@ -59,6 +64,10 @@
*/
unsigned short int debug_level;
+/* Set this when we want main to end or restart
+ */
+volatile unsigned short int finish=0;
+
/**
* restart
*
@@ -235,50 +244,6 @@
{
unsigned short int j;
- if (cnt->imgs.out)
- free(cnt->imgs.out);
- if (cnt->imgs.ref)
- free(cnt->imgs.ref);
- if (cnt->imgs.ref_dyn)
- free(cnt->imgs.ref_dyn);
- if (cnt->imgs.image_virgin)
- free(cnt->imgs.image_virgin);
- if (cnt->imgs.labels)
- free(cnt->imgs.labels);
- if (cnt->imgs.labelsize)
- free(cnt->imgs.labelsize);
- if (cnt->imgs.smartmask)
- free(cnt->imgs.smartmask);
- if (cnt->imgs.smartmask_final)
- free(cnt->imgs.smartmask_final);
- if (cnt->imgs.smartmask_buffer)
- free(cnt->imgs.smartmask_buffer);
- if (cnt->imgs.common_buffer)
- free(cnt->imgs.common_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)
- close(cnt->pipe);
- if(cnt->mpipe != -1)
- close(cnt->mpipe);
-
- /* Cleanup the netcam part */
- if(cnt->netcam)
- netcam_cleanup(cnt->netcam, 0);
-
- /* Cleanup the current time structure */
- if (cnt->currenttime_tm)
- free(cnt->currenttime_tm);
-
- /* Cleanup the event time structure */
- if (cnt->eventtime_tm)
- free(cnt->eventtime_tm);
-
/* Free memory allocated for config parameters */
for (j = 0; config_params[j].param_name != NULL; j++) {
if (config_params[j].copy == copy_string) {
@@ -342,8 +307,15 @@
while (cnt_list[++i]) {
cnt_list[i]->makemovie=1;
cnt_list[i]->finish=1;
+ /* don't restart thread when it ends,
+ * all threads restarts if global restart is set
+ */
+ cnt_list[i]->restart=0;
}
}
+ /* Set flag we want to quit main check threads loop
+ * if restart is set (above) we start up again */
+ finish = 1;
break;
case SIGSEGV:
exit(0);
@@ -435,8 +407,6 @@
/* always save first motion frame as preview-shot, may be changed to an other one later */
if (cnt->new_img & (NEWIMG_FIRST | NEWIMG_BEST | NEWIMG_CENTER)) {
image_save_as_preview(cnt, img);
- cnt->preview_time = cnt->currenttime;
- cnt->preview_shots = cnt->shots;
/* If we set output_all to yes and during the event
* there is no image with motion, diffs is 0, we are not going to save the preview event */
@@ -460,8 +430,6 @@
if (cnt->new_img & NEWIMG_BEST) {
if (img->diffs > cnt->imgs.preview_image.diffs) {
image_save_as_preview(cnt, img);
- cnt->preview_time = cnt->currenttime;
- cnt->preview_shots = cnt->shots;
/* If we have locate on it is already dine above */
if (cnt->locate == LOCATE_PREVIEW) {
@@ -473,8 +441,6 @@
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 we have locate on it is already dine above */
if (cnt->locate == LOCATE_PREVIEW) {
@@ -573,9 +539,11 @@
*
* cnt Pointer to the motion context structure
*
- * Returns: nothing
+ * Returns: 0 OK
+ * -1 Fatal error, open loopback error
+ * -2 Fatal error, open SQL database error
*/
-static void motion_init(struct context *cnt)
+static int motion_init(struct context *cnt)
{
int i;
FILE *picture;
@@ -585,6 +553,10 @@
cnt->currenttime_tm = mymalloc(sizeof(struct tm));
cnt->eventtime_tm = mymalloc(sizeof(struct tm));
+ /* Init frame time */
+ cnt->currenttime = time(NULL);
+ localtime_r(&cnt->currenttime, cnt->currenttime_tm);
+
cnt->smartmask_speed = 0;
/* We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so
@@ -593,6 +565,7 @@
cnt->prev_event = 0;
cnt->lightswitch_framecounter = 0;
cnt->detecting_motion = 0;
+ cnt->makemovie = 0;
motion_log(LOG_DEBUG, 0, "Thread %d started", (unsigned long)pthread_getspecific(tls_key_threadnr));
@@ -602,22 +575,12 @@
/* set the device settings */
cnt->video_dev = vid_start(cnt);
- /* We still cannot handle a V4L type camera not being available
- * during startup. We have no other option than to die
- */
- if (cnt->video_dev == -1 && !cnt->conf.netcam_url) {
- motion_log(LOG_ERR, 0, "Capture error calling vid_start");
- motion_log(-1 , 0, "Thread finishing...");
- motion_remove_pid();
- exit(1);
- }
-
- /* We failed to get an initial image from a network camera
+ /* We failed to get an initial image from a camera
* So we need to guess height and width based on the config
* file options.
*/
- if (cnt->video_dev == -1) {
- motion_log(LOG_ERR, 0, "Could not fetch initial image from network camera");
+ if (cnt->video_dev < 0) {
+ motion_log(LOG_ERR, 0, "Could not fetch initial image from camera");
motion_log(LOG_ERR, 0, "Motion continues using width and height from config file(s)");
cnt->imgs.width = cnt->conf.width;
cnt->imgs.height = cnt->conf.height;
@@ -632,7 +595,6 @@
cnt->imgs.out = mymalloc(cnt->imgs.size);
cnt->imgs.ref_dyn = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.ref_dyn)); /* contains the moving objects of ref. frame */
cnt->imgs.image_virgin = mymalloc(cnt->imgs.size);
- memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */
cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize);
cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize);
cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.smartmask_buffer));
@@ -656,23 +618,18 @@
*/
rotate_init(cnt); /* rotate_deinit is called in main */
- /* Allow videodevice to settle in */
-
/* Capture first image, or we will get an alarm on start */
if (cnt->video_dev > 0) {
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 5; i++) {
if (vid_next(cnt, cnt->imgs.image_virgin) == 0)
break;
SLEEP(2,0);
}
- /* We still cannot handle a V4L type camera not being available
- * during startup. We have no other option than to die
- */
- if (i >= 10) {
+ if (i >= 5) {
+ memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */
+ draw_text(cnt->imgs.image_virgin, 10, 20, cnt->imgs.width,
+ "Error capturing first image", cnt->conf.text_double);
motion_log(LOG_ERR, 0, "Error capturing first image");
- motion_log(-1, 0, "Thread finishing...");
- motion_remove_pid();
- exit(1);
}
}
@@ -689,9 +646,7 @@
cnt->pipe = vid_startpipe(cnt->conf.vidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type);
if (cnt->pipe < 0) {
motion_log(LOG_ERR, 0, "Failed to open video loopback");
- motion_log(-1, 0, "Thread finishing...");
- motion_remove_pid();
- exit(1);
+ return -1;
}
}
if (cnt->conf.motionvidpipe) {
@@ -701,9 +656,7 @@
cnt->mpipe = vid_startpipe(cnt->conf.motionvidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type);
if (cnt->mpipe < 0) {
motion_log(LOG_ERR, 0, "Failed to open video loopback");
- motion_log(-1, 0, "Thread finishing...");
- motion_remove_pid();
- exit(1);
+ return -1;
}
}
#endif /* BSD */
@@ -718,9 +671,7 @@
motion_log(LOG_ERR, 0, "Cannot connect to MySQL database %s on host %s with user %s",
cnt->conf.mysql_db, cnt->conf.mysql_host, cnt->conf.mysql_user);
motion_log(LOG_ERR, 0, "MySQL error was %s", mysql_error(cnt->database));
- motion_log(-1, 0, "Thread finishing...");
- motion_remove_pid();
- exit(1);
+ return -2;
}
#if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID > 50012)
my_bool my_true = TRUE;
@@ -748,9 +699,7 @@
if (PQstatus(cnt->database_pg) == CONNECTION_BAD) {
motion_log(LOG_ERR, 0, "Connection to PostgreSQL database '%s' failed: %s",
cnt->conf.pgsql_db, PQerrorMessage(cnt->database_pg));
- motion_log(-1, 0, "Thread finishing...");
- motion_remove_pid();
- exit(1);
+ return -2;
}
}
#endif /* HAVE_PGSQL */
@@ -805,15 +754,109 @@
if ( webcam_init(cnt) == -1 ) {
motion_log(LOG_ERR, 1, "Problem enabling stream server in port %d", cnt->conf.webcam_port);
cnt->finish = 1;
- cnt->makemovie = 0;
}else motion_log(LOG_DEBUG, 0, "Started stream webcam server in port %d", cnt->conf.webcam_port);
}
/* Prevent first few frames from triggering motion... */
cnt->moved = 8;
+
+ return 0;
}
/**
+ * motion_cleanup
+ *
+ * This routine is called from motion_loop when thread ends to
+ * cleanup all memory etc. that motion_init did.
+ *
+ * Parameters:
+ *
+ * cnt Pointer to the motion context structure
+ *
+ * Returns: nothing
+ */
+static void motion_cleanup(struct context *cnt)
+{
+ /* Stop webcam */
+ event(cnt, EVENT_STOP, NULL, NULL, NULL, NULL);
+
+#ifndef WITHOUT_V4L
+ if (cnt->video_dev >= 0)
+ vid_close(cnt);
+#endif
+
+ if (cnt->imgs.out) {
+ free(cnt->imgs.out);
+ cnt->imgs.out = NULL;
+ }
+ if (cnt->imgs.ref) {
+ free(cnt->imgs.ref);
+ cnt->imgs.ref = NULL;
+ }
+ if (cnt->imgs.ref_dyn) {
+ free(cnt->imgs.ref_dyn);
+ cnt->imgs.ref_dyn = NULL;
+ }
+ if (cnt->imgs.image_virgin) {
+ free(cnt->imgs.image_virgin);
+ cnt->imgs.image_virgin = NULL;
+ }
+ if (cnt->imgs.labels) {
+ free(cnt->imgs.labels);
+ cnt->imgs.labels = NULL;
+ }
+ if (cnt->imgs.labelsize) {
+ free(cnt->imgs.labelsize);
+ cnt->imgs.labelsize = NULL;
+ }
+ if (cnt->imgs.smartmask) {
+ free(cnt->imgs.smartmask);
+ cnt->imgs.smartmask = NULL;
+ }
+ if (cnt->imgs.smartmask_final) {
+ free(cnt->imgs.smartmask_final);
+ cnt->imgs.smartmask_final = NULL;
+ }
+ if (cnt->imgs.smartmask_buffer) {
+ free(cnt->imgs.smartmask_buffer);
+ cnt->imgs.smartmask_buffer = NULL;
+ }
+ if (cnt->imgs.common_buffer) {
+ free(cnt->imgs.common_buffer);
+ cnt->imgs.common_buffer = NULL;
+ }
+ if (cnt->imgs.preview_image.image) {
+ free(cnt->imgs.preview_image.image);
+ cnt->imgs.preview_image.image = NULL;
+ }
+
+ image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */
+
+ rotate_deinit(cnt); /* cleanup image rotation data */
+
+ if(cnt->pipe != -1) {
+ close(cnt->pipe);
+ cnt->pipe = -1;
+ }
+ if(cnt->mpipe != -1) {
+ close(cnt->mpipe);
+ cnt->mpipe = -1;
+ }
+
+ /* Cleanup the current time structure */
+ if (cnt->currenttime_tm) {
+ free(cnt->currenttime_tm);
+ cnt->currenttime_tm = NULL;
+ }
+
+ /* Cleanup the event time structure */
+ if (cnt->eventtime_tm) {
+ free(cnt->eventtime_tm);
+ cnt->eventtime_tm = NULL;
+ }
+}
+
+/**
* motion_loop
*
* Thread function for the motion handling threads.
@@ -834,7 +877,7 @@
int previous_diffs = 0, previous_location_x = 0, previous_location_y = 0;
int text_size_factor;
int passflag = 0;
- long int *rolling_average_data;
+ long int *rolling_average_data = NULL;
long int rolling_average_limit, required_frame_time, frame_delay, delay_time_nsec;
int rolling_frame = 0;
struct timeval tv1, tv2;
@@ -850,7 +893,11 @@
*/
unsigned long int time_last_frame=1, time_current_frame;
- motion_init(cnt);
+ cnt->running = 1;
+
+ if (motion_init(cnt) < 0) {
+ goto err;
+ }
/* Initialize the double sized characters if needed. */
if(cnt->conf.text_double)
@@ -892,6 +939,7 @@
while (!cnt->finish || cnt->makemovie) {
/***** MOTION LOOP - PREPARE FOR NEW FRAME SECTION *****/
+ cnt->watchdog = WATCHDOG_TMO;
/* Get current time and preserver last time for frame interval calc. */
timebefore = timenow;
@@ -918,7 +966,6 @@
/* Get time for current frame */
cnt->currenttime = time(NULL);
-
/* localtime returns static data and is not threadsafe
* so we use localtime_r which is reentrant and threadsafe
*/
@@ -985,28 +1032,29 @@
cnt->current_image->total_labels = 0;
}
- /***** MOTION LOOP - RETRY INITIALIZING NETCAM SECTION *****/
- /* If a network camera is not available we keep on retrying every 10 seconds
+ /***** MOTION LOOP - RETRY INITIALIZING SECTION *****/
+ /* If a camera is not available we keep on retrying every 10 seconds
* until it shows up.
*/
- if (cnt->video_dev == -1 && cnt->conf.netcam_url &&
+ if (cnt->video_dev < 0 &&
cnt->currenttime % 10 == 0 && cnt->shots == 0) {
motion_log(LOG_ERR, 0,
- "Retrying until successful initial connection with network camera");
- netcam_cleanup(cnt->netcam, 1);
- cnt->netcam = NULL;
+ "Retrying until successful connection with camera");
cnt->video_dev = vid_start(cnt);
/* if the netcam has different dimensions than in the config file
* we need to restart Motion to re-allocate all the buffers
*/
- if (cnt->imgs.width != cnt->imgs.width || cnt->imgs.height != cnt->conf.height) {
- motion_log(LOG_ERR, 0, "Network camera has finally become available");
- motion_log(LOG_ERR, 0, "Network camera image has different width and height "
+ if (cnt->imgs.width != cnt->conf.width || cnt->imgs.height != cnt->conf.height) {
+ motion_log(LOG_ERR, 0, "Camera has finally become available");
+ motion_log(LOG_ERR, 0, "Camera image has different width and height "
"from what is in the config file. You should fix that");
- motion_log(LOG_ERR, 0, "Restarting Motion to reinitialize all "
+ motion_log(LOG_ERR, 0, "Restarting Motion thread to reinitialize all "
"image buffers to new picture dimensions");
- kill(getpid(), 1);
+ cnt->conf.width = cnt->imgs.width;
+ cnt->conf.height = cnt->imgs.height;
+ /* Break out of main loop terminating thread
+ * watchdog will start us again */
break;
}
}
@@ -1021,11 +1069,15 @@
* <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, cnt->current_image->image);
+ if (cnt->video_dev >= 0)
+ vid_return_code = vid_next(cnt, cnt->current_image->image);
+ else
+ vid_return_code = 1; /* Non fatal error */
// VALID PICTURE
if (vid_return_code == 0) {
cnt->lost_connection = 0;
+ cnt->connectionlosttime = 0;
/* If all is well reset missing_frame_counter */
if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) {
@@ -1058,12 +1110,17 @@
}
// FATAL ERROR - leave the thread by breaking out of the main loop
} else if (vid_return_code < 0) {
- /* Fatal error - break out of main loop terminating thread */
- motion_log(LOG_ERR, 0, "Video device fatal error - terminating camera thread");
- break;
+ /* Fatal error - Close video device */
+ motion_log(LOG_ERR, 0, "Video device fatal error - Closing video device");
+ vid_close(cnt);
+ /* Use virgin image, if we are not able to open it again next loop
+ * a gray image with message is applied
+ */
+ memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size);
// NO FATAL ERROR - copy last image or show grey image with message
} else {
cnt->lost_connection = 1;
+
if (debug_level)
motion_log(-1, 0, "vid_return_code %d", vid_return_code);
@@ -1073,36 +1130,36 @@
* other way
*/
if (vid_return_code == NETCAM_RESTART_ERROR) {
- motion_log(LOG_ERR, 0, "Restarting Motion to reinitialize all "
+ motion_log(LOG_ERR, 0, "Restarting Motion thread to reinitialize all "
"image buffers");
- kill(getpid(), 1);
+ /* Break out of main loop terminating thread
+ * watchdog will start us again */
break;
}
- /* First missed frame - store timestamp */
- if (!cnt->missing_frame_counter)
+ /* First missed frame - store timestamp
+ * Don't reset time when thread restarts*/
+ if (cnt->connectionlosttime == 0)
cnt->connectionlosttime = cnt->currenttime;
- /* If we are waiting for first image prevent the
- * cnt->connectionlosttime from being updated each time we come back
- */
- if (cnt->video_dev == -1)
- cnt->missing_frame_counter = 1;
-
/* Increase missing_frame_counter
* The first MISSING_FRAMES_TIMEOUT seconds we copy previous virgin image
* After 30 seconds we put a grey error image in the buffer
* If we still have not yet received the initial image from a camera
* we go straight for the grey error image.
*/
- if (cnt->video_dev != -1 &&
- ++cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit)) {
+ ++cnt->missing_frame_counter;
+ if (cnt->video_dev >= 0 &&
+ cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit)) {
memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size);
-
} else {
+ const char *tmpin;
char tmpout[80];
- char tmpin[] = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T";
struct tm tmptime;
+ if (cnt->video_dev >= 0)
+ tmpin = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T";
+ else
+ tmpin = "UNABLE TO OPEN VIDEO DEVICE\\nSINCE %Y-%m-%d %T";
localtime_r(&cnt->connectionlosttime, &tmptime);
memset(cnt->current_image->image, 0x80, cnt->imgs.size);
mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tmptime, NULL, 0);
@@ -1113,7 +1170,17 @@
if (cnt->missing_frame_counter == MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) {
motion_log(LOG_ERR, 0, "Video signal lost - Adding grey image");
// Event for lost video signal can be called from here
+ event(cnt, EVENT_CAMERA_LOST, NULL, NULL,
+ NULL, cnt->currenttime_tm);
}
+
+ /* If we don't get a valid frame for a long time, try to close/reopen device
+ * Only try this when a device is open */
+ if ( (cnt->video_dev > 0) &&
+ (cnt->missing_frame_counter == (MISSING_FRAMES_TIMEOUT * 4) * cnt->conf.frame_limit) ) {
+ motion_log(LOG_ERR, 0, "Video signal still lost - Trying to close video device");
+ vid_close(cnt);
+ }
}
}
@@ -1143,8 +1210,9 @@
* This can happen due to change of light conditions or due to a sudden change of the camera
* sensitivity. If alg_lightswitch detects lightswitch we suspend motion detection the next
* 5 frames to allow the camera to settle.
+ * Don't check if we have lost connection, we detect "Lost signal" frame as lightswitch
*/
- if (cnt->conf.lightswitch) {
+ if (cnt->conf.lightswitch && !cnt->lost_connection) {
if (alg_lightswitch(cnt, cnt->current_image->diffs)) {
if (cnt->conf.setup_mode)
motion_log(-1, 0, "Lightswitch detected");
@@ -1742,25 +1810,22 @@
/* END OF MOTION MAIN LOOP
* If code continues here it is because the thread is exiting or restarting
*/
-
- if (cnt->netcam) {
- netcam_cleanup(cnt->netcam, 0);
- cnt->netcam = NULL;
- }
+err:
if (rolling_average_data)
free(rolling_average_data);
cnt->lost_connection = 1;
motion_log(-1, 0, "Thread exiting");
- if (!cnt->finish)
- motion_log(LOG_ERR, 1, "Somebody stole the video device, lets hope we got his picture");
- event(cnt, EVENT_STOP, NULL, NULL, NULL, NULL);
+ motion_cleanup(cnt);
pthread_mutex_lock(&global_lock);
threads_running--;
pthread_mutex_unlock(&global_lock);
+ cnt->running = 0;
+ cnt->finish = 0;
+
pthread_exit(NULL);
}
@@ -1923,8 +1988,8 @@
context_destroy(cnt_list[i]);
}
free(cnt_list);
+ cnt_list = NULL;
#ifndef WITHOUT_V4L
- vid_close();
vid_cleanup();
#endif
}
@@ -2011,6 +2076,74 @@
}
/**
+ * start_motion_thread
+ *
+ * Called from main when start a motion thread
+ *
+ * Parameters:
+ *
+ * cnt - Thread context pointer
+ * thread_attr - pointer to thread attributes
+ *
+ * Returns: nothing
+ */
+static void start_motion_thread(struct context *cnt, pthread_attr_t *thread_attr)
+{
+ int i;
+
+ /* Check the webcam port number for conflicts.
+ * First we check for conflict with the control port.
+ * Second we check for that two threads does not use the same port number
+ * for the webcam. If a duplicate port is found the webcam feature gets disabled (port =0)
+ * for this thread and a warning is written to console and syslog.
+ */
+
+ if (cnt->conf.webcam_port != 0) {
+ /* Compare against the control port. */
+ if (cnt_list[0]->conf.control_port == cnt->conf.webcam_port) {
+ motion_log(LOG_ERR, 0,
+ "Webcam port number %d for thread %d conflicts with the control port",
+ cnt->conf.webcam_port, cnt->threadnr);
+ motion_log(LOG_ERR, 0, "Webcam feature for thread %d is disabled.", cnt->threadnr);
+ cnt->conf.webcam_port = 0;
+ }
+
+ /* Compare against webcam ports of other threads. */
+ for (i = 1; cnt_list[i]; i++) {
+ if (cnt_list[i] == cnt)
+ continue;
+ if (cnt_list[i]->conf.webcam_port == cnt->conf.webcam_port) {
+ motion_log(LOG_ERR, 0,
+ "Webcam port number %d for thread %d conflicts with thread %d",
+ cnt->conf.webcam_port, cnt->threadnr, cnt_list[i]->threadnr);
+ motion_log(LOG_ERR, 0,
+ "Webcam feature for thread %d is disabled.", cnt->threadnr);
+ cnt->conf.webcam_port = 0;
+ }
+ }
+ }
+
+ /* Update how many threads we have running. This is done within a
+ * mutex lock to prevent multiple simultaneous updates to
+ * 'threads_running'.
+ */
+ pthread_mutex_lock(&global_lock);
+ threads_running++;
+ pthread_mutex_unlock(&global_lock);
+
+ /* Set a flag that we want this thread running */
+ cnt->restart = 1;
+
+ /* Give the thread 30s to start */
+ cnt->watchdog = 30;
+
+ /* Create the actual thread. Use 'motion_loop' as the thread
+ * function.
+ */
+ pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt);
+}
+
+/**
* main
*
* Main entry point of Motion. Launches all the motion threads and contains
@@ -2025,8 +2158,7 @@
*/
int main (int argc, char **argv)
{
- int i, j;
- int webcam_port;
+ int i;
pthread_attr_t thread_attr;
pthread_t thread_id;
@@ -2080,55 +2212,11 @@
motion_startup(0, argc, argv); /* 0 = skip daemon init */
}
- /* Check the webcam port number for conflicts.
- * First we check for conflict with the control port.
- * Second we check for that two threads does not use the same port number
- * for the webcam. If a duplicate port is found the webcam feature gets disabled (port =0)
- * for this thread and a warning is written to console and syslog.
- */
- for (i = 1; cnt_list[i]; i++) {
- /* Get the webcam port for thread 'i', may be 0. */
- webcam_port = cnt_list[i]->conf.webcam_port;
- if (cnt_list[0]->conf.setup_mode)
- motion_log(LOG_ERR, 0, "Webcam port %d", webcam_port);
-
- /* Compare against the control port. */
- if (cnt_list[0]->conf.control_port == webcam_port && webcam_port != 0) {
- cnt_list[i]->conf.webcam_port = 0;
- motion_log(LOG_ERR, 0,
- "Webcam port number %d for thread %d conflicts with the control port",
- webcam_port, i);
- motion_log(LOG_ERR, 0, "Webcam feature for thread %d is disabled.", i);
- }
-
- /* Compare against webcam ports of other threads. */
- j = i;
- while (cnt_list[++j]) {
- if (cnt_list[j]->conf.webcam_port == webcam_port && webcam_port != 0) {
- cnt_list[j]->conf.webcam_port = 0;
- motion_log(LOG_ERR, 0,
- "Webcam port number %d for thread %d conflicts with thread %d",
- webcam_port, j, i);
- motion_log(LOG_ERR, 0,
- "Webcam feature for thread %d is disabled.", j);
- }
- }
- }
-
/* Start the motion threads. First 'cnt_list' item is global if 'thread'
* option is used, so start at 1 then and 0 otherwise.
*/
for (i = cnt_list[1] != NULL ? 1 : 0; cnt_list[i]; i++) {
-
- /* Assign the thread number for this thread. This is done within a
- * mutex lock to prevent multiple simultaneous updates to
- * 'threads_running'.
- */
- pthread_mutex_lock(&global_lock);
- threads_running++;
- pthread_mutex_unlock(&global_lock);
-
/* If i is 0 it means no thread files and we then set the thread number to 1 */
cnt_list[i]->threadnr = i ? i : 1;
@@ -2136,16 +2224,16 @@
motion_log(LOG_INFO, 0, "Thread %d is from %s", cnt_list[i]->threadnr, cnt_list[i]->conf_filename );
if (cnt_list[0]->conf.setup_mode) {
- motion_log(-1, 0, "Thread %d is device: %s input %d", threads_running,
+ motion_log(-1, 0, "Thread %d is device: %s input %d", cnt_list[i]->threadnr,
cnt_list[i]->conf.netcam_url ? cnt_list[i]->conf.netcam_url : cnt_list[i]->conf.video_device,
cnt_list[i]->conf.netcam_url ? -1 : cnt_list[i]->conf.input
);
}
- /* Create the actual thread. Use 'motion_loop' as the thread
- * function.
- */
- pthread_create(&thread_id, &thread_attr, &motion_loop, cnt_list[i]);
+ if (cnt_list[0]->conf.setup_mode)
+ motion_log(LOG_ERR, 0, "Webcam port %d", cnt_list[i]->conf.webcam_port);
+
+ start_motion_thread(cnt_list[i], &thread_attr);
}
/* Create a thread for the control interface if requested. Create it
@@ -2160,9 +2248,37 @@
/* Crude way of waiting for all threads to finish - check the thread
* counter (because we cannot do join on the detached threads).
*/
- while(threads_running > 0) {
+ while( (threads_running > 0) || (!finish) ) {
SLEEP(1,0);
+ for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) {
+ /* Check if threads wants to be restarted */
+ if ( (!cnt_list[i]->running) && (cnt_list[i]->restart) ) {
+ motion_log(LOG_INFO, 0, "Motion thread %d restart", cnt_list[i]->threadnr);
+ start_motion_thread(cnt_list[i], &thread_attr);
+ }
+ if (cnt_list[i]->watchdog > WATCHDOG_OFF) {
+ cnt_list[i]->watchdog--;
+ if (cnt_list[i]->watchdog == 0) {
+ motion_log(LOG_ERR, 0, "Thread %d - Watchdog timeout, trying to do a graceful restart",
+ cnt_list[i]->threadnr);
+ cnt_list[i]->finish = 1;
+ }
+ if (cnt_list[i]->watchdog == -60) {
+ motion_log(LOG_ERR, 0, "Thread %d - Watchdog timeout, did NOT restart graceful, killing it!",
+ cnt_list[i]->threadnr);
+ pthread_cancel(cnt_list[i]->thread_id);
+ pthread_mutex_lock(&global_lock);
+ threads_running--;
+ pthread_mutex_unlock(&global_lock);
+ motion_cleanup(cnt_list[i]);
+ cnt_list[i]->running = 0;
+ cnt_list[i]->finish = 0;
+ }
+ }
+ }
}
+ /* Reset end main loop flag */
+ finish = 0;
if (cnt_list[0]->conf.setup_mode)
motion_log(LOG_DEBUG, 0, "Threads finished");
Index: motion.h
===================================================================
--- motion.h (revision 275)
+++ motion.h (working copy)
@@ -130,6 +130,9 @@
* and then we show a grey image instead
*/
+#define WATCHDOG_TMO 30 /* 10 sec max motion_loop interval */
+#define WATCHDOG_OFF -127 /* Turn off watchdog, used when we wants to quit a thread */
+
#define CONNECTION_KO "Lost connection"
#define CONNECTION_OK "Connection OK"
@@ -305,8 +308,6 @@
struct netcam_context *netcam;
struct image_data *current_image; /* Pointer to a structure where the image, diffs etc is stored */
unsigned short int new_img;
- time_t preview_time; /* Timestamp of preview image */
- unsigned short int preview_shots; /* Shot of preview buffer image */
int locate;
struct rotdata rotate_data; /* rotation data is thread-specific */
@@ -316,10 +317,17 @@
int diffs_last[THRESHOLD_TUNE_LENGTH];
int smartmask_speed;
- unsigned short int snapshot;
- unsigned short int makemovie;
- unsigned short int finish;
+ /* Commands to the motion thread */
+ volatile unsigned short int snapshot; /* Make a snapshot */
+ volatile unsigned short int makemovie; /* End a movie */
+ volatile unsigned short int finish; /* End the thread */
+ volatile unsigned short int restart; /* Restart the thread when it ends */
+ /* Is the motion thread running */
+ volatile unsigned short int running;
+ volatile int watchdog;
+ pthread_t thread_id;
+
int event_nr;
int prev_event;
int lightswitch_framecounter;
Index: video.h
===================================================================
--- video.h (revision 275)
+++ video.h (working copy)
@@ -35,6 +35,8 @@
#define VIDEO_DEVICE "/dev/video0"
struct video_dev {
+ struct video_dev *next;
+ int usage_count;
int fd;
const char *video_device;
int input;
@@ -70,7 +72,7 @@
/* video functions, video_common.c */
int vid_start(struct context *);
int vid_next(struct context *, unsigned char *map);
-void vid_close(void);
+void vid_close(struct context *cnt);
void vid_cleanup(void);
void vid_init(void);
void conv_yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height);
Index: video_common.c
===================================================================
--- video_common.c (revision 275)
+++ video_common.c (working copy)
@@ -514,69 +514,100 @@
/* Here we setup the viddevs structure which is used globally in the vid_*
* functions.
*/
-static struct video_dev **viddevs = NULL;
+static struct video_dev *viddevs = NULL;
/**
* vid_init
*
* Called from motion.c at the very beginning before setting up the threads.
- * Function prepares the viddevs struct for the threads and the vid_mutex
+ * Function prepares the vid_mutex
*/
void vid_init(void)
{
- if (!viddevs) {
- viddevs = mymalloc(sizeof(struct video_dev *));
- viddevs[0] = NULL;
- }
-
pthread_mutex_init(&vid_mutex, NULL);
}
/**
- * vid_close
+ * vid_cleanup
*
- * vid_close is called from motion.c when Motion is stopped or restarted
- * It gets rid of all open video devices. It is called BEFORE vid_cleanup.
+ * vid_cleanup is called from motion.c when Motion is stopped or restarted
*/
-void vid_close(void)
+void vid_cleanup(void)
{
- int i = -1;
-
- if (viddevs) {
- while (viddevs[++i]) {
-#ifdef MOTION_V4L2
- if (viddevs[i]->v4l2)
- v4l2_close(viddevs[i]);
-#else
- int tmp = viddevs[i]->fd;
- viddevs[i]->fd = -1;
- close(tmp);
-#endif
- }
- }
+ pthread_mutex_destroy(&vid_mutex);
}
/**
- * vid_cleanup
+ * vid_close
*
- * vid_cleanup is called from motion.c when Motion is stopped or restarted
- * It free all the memory held by the viddevs structs.
+ * vid_close is called from motion.c when a Motion thread is stopped or restarted
*/
-void vid_cleanup(void)
+void vid_close(struct context *cnt)
{
- int i = -1;
- if (viddevs) {
- while (viddevs[++i]) {
+ struct video_dev *dev = viddevs;
+ struct video_dev *prev = NULL;
+
+ /* Cleanup the netcam part */
+ if(cnt->netcam) {
+ netcam_cleanup(cnt->netcam, 0);
+ cnt->netcam = NULL;
+ return;
+ }
+
+ /* Cleanup the v4l part */
+ pthread_mutex_lock(&vid_mutex);
+ while (dev) {
+ if (dev->fd == cnt->video_dev)
+ break;
+ prev = dev;
+ dev = dev->next;
+ }
+ pthread_mutex_unlock(&vid_mutex);
+
+ /* Set it as closed in thread context */
+ cnt->video_dev = -1;
+
+ if (dev == NULL) {
+ motion_log(LOG_ERR, 0, "vid_close: Unable to find video device");
+ return;
+ }
+
+ if( --dev->usage_count == 0) {
+ motion_log(LOG_INFO, 0, "Closing video device %s", dev->video_device);
#ifdef MOTION_V4L2
- if (viddevs[i]->v4l2)
- v4l2_cleanup(viddevs[i]);
- else
+ if (dev->v4l2) {
+ v4l2_close(dev);
+ v4l2_cleanup(dev);
+ } else {
#endif
- munmap(viddevs[i]->v4l_buffers[0], viddevs[i]->size_map);
- free(viddevs[i]);
+ close(dev->fd);
+ munmap(viddevs->v4l_buffers[0], viddevs->size_map);
+ munmap(viddevs->v4l_buffers[1], viddevs->size_map);
+#ifdef MOTION_V4L2
}
- free(viddevs);
- viddevs = NULL;
+#endif
+ dev->fd = -1;
+ pthread_mutex_lock(&vid_mutex);
+ /* Remove from list */
+ if (prev == NULL)
+ viddevs = dev->next;
+ else
+ prev->next = dev->next;
+ pthread_mutex_unlock(&vid_mutex);
+
+ pthread_mutexattr_destroy(&dev->attr);
+ pthread_mutex_destroy(&dev->mutex);
+ free(dev);
+ } else {
+ motion_log(LOG_INFO, 0, "Still %d users of video device %s, so we don't close it now", dev->usage_count, dev->video_device);
+ /* There is still at least one thread using this device
+ * If we own it, release it
+ */
+ if (dev->owner == cnt->threadnr) {
+ dev->frames = 0;
+ dev->owner = -1;
+ pthread_mutex_unlock(&dev->mutex);
+ }
}
}
@@ -608,180 +639,190 @@
* device number
* -1 if failed to open device.
*/
-
static int vid_v4lx_start(struct context *cnt)
{
struct config *conf = &cnt->conf;
- int dev = -1;
+ int fd = -1;
+ struct video_dev *dev;
- /* Start a new block so we can make declarations without breaking good old
- * gcc 2.95 or older.
+ int width, height, input, norm, tuner_number;
+ unsigned long frequency;
+
+ /* We use width and height from conf in this function. They will be assigned
+ * to width and height in imgs here, and cap_width and cap_height in
+ * rotate_data won't be set until in rotate_init.
+ * Motion requires that width and height is a multiple of 16 so we check
+ * for this first.
*/
- {
- int i = -1;
- int width, height, input, norm, tuner_number;
- unsigned long frequency;
+ if (conf->width % 16) {
+ motion_log(LOG_ERR, 0, "config image width (%d) is not modulo 16", conf->width);
+ return -1;
+ }
- /* We use width and height from conf in this function. They will be assigned
- * to width and height in imgs here, and cap_width and cap_height in
- * rotate_data won't be set until in rotate_init.
- * Motion requires that width and height is a multiple of 16 so we check
- * for this first.
- */
- if (conf->width % 16) {
- motion_log(LOG_ERR, 0, "config image width (%d) is not modulo 16", conf->width);
- return -1;
- }
+ if (conf->height % 16) {
+ motion_log(LOG_ERR, 0, "config image height (%d) is not modulo 16", conf->height);
+ return -1;
+ }
- if (conf->height % 16) {
- motion_log(LOG_ERR, 0, "config image height (%d) is not modulo 16", conf->height);
- return -1;
- }
+ width = conf->width;
+ height = conf->height;
+ input = conf->input;
+ norm = conf->norm;
+ frequency = conf->frequency;
+ tuner_number = conf->tuner_number;
- width = conf->width;
- height = conf->height;
- input = conf->input;
- norm = conf->norm;
- frequency = conf->frequency;
- tuner_number = conf->tuner_number;
+ pthread_mutex_lock(&vid_mutex);
- pthread_mutex_lock(&vid_mutex);
+ /* Transfer width and height from conf to imgs. The imgs values are the ones
+ * that is used internally in Motion. That way, setting width and height via
+ * http remote control won't screw things up.
+ */
+ cnt->imgs.width = width;
+ cnt->imgs.height = height;
- /* Transfer width and height from conf to imgs. The imgs values are the ones
- * that is used internally in Motion. That way, setting width and height via
- * http remote control won't screw things up.
- */
- cnt->imgs.width = width;
- cnt->imgs.height = height;
-
- /* First we walk through the already discovered video devices to see
- * if we have already setup the same device before. If this is the case
- * the device is a Round Robin device and we set the basic settings
- * and return the file descriptor
- */
- while (viddevs[++i]) {
- if (!strcmp(conf->video_device, viddevs[i]->video_device)) {
- int fd;
- cnt->imgs.type = viddevs[i]->v4l_fmt;
- switch (cnt->imgs.type) {
- case VIDEO_PALETTE_GREY:
- cnt->imgs.motionsize = width * height;
- cnt->imgs.size = width * height;
- break;
- case VIDEO_PALETTE_YUYV:
- case VIDEO_PALETTE_RGB24:
- case VIDEO_PALETTE_YUV422:
- cnt->imgs.type = VIDEO_PALETTE_YUV420P;
- case VIDEO_PALETTE_YUV420P:
- cnt->imgs.motionsize = width * height;
- cnt->imgs.size = (width * height * 3) / 2;
- break;
- }
- fd = viddevs[i]->fd;
- pthread_mutex_unlock(&vid_mutex);
- return fd;
+ /* First we walk through the already discovered video devices to see
+ * if we have already setup the same device before. If this is the case
+ * the device is a Round Robin device and we set the basic settings
+ * and return the file descriptor
+ */
+ dev = viddevs;
+ while (dev) {
+ if (!strcmp(conf->video_device, dev->video_device)) {
+ dev->usage_count++;
+ cnt->imgs.type = dev->v4l_fmt;
+ switch (cnt->imgs.type) {
+ case VIDEO_PALETTE_GREY:
+ cnt->imgs.motionsize = width * height;
+ cnt->imgs.size = width * height;
+ break;
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_YUV422:
+ cnt->imgs.type = VIDEO_PALETTE_YUV420P;
+ case VIDEO_PALETTE_YUV420P:
+ cnt->imgs.motionsize = width * height;
+ cnt->imgs.size = (width * height * 3) / 2;
+ break;
}
+ pthread_mutex_unlock(&vid_mutex);
+ return dev->fd;
}
+ dev = dev->next;
+ }
- viddevs = myrealloc(viddevs, sizeof(struct video_dev *) * (i + 2), "vid_start");
- viddevs[i] = mymalloc(sizeof(struct video_dev));
- memset(viddevs[i], 0, sizeof(struct video_dev));
- viddevs[i + 1] = NULL;
+ dev = mymalloc(sizeof(struct video_dev));
+ memset(dev, 0, sizeof(struct video_dev));
- pthread_mutexattr_init(&viddevs[i]->attr);
- pthread_mutex_init(&viddevs[i]->mutex, NULL);
+ dev->video_device = conf->video_device;
- viddevs[i]->video_device = conf->video_device;
+ fd = open(dev->video_device, O_RDWR);
- dev = open(viddevs[i]->video_device, O_RDWR);
+ if (fd < 0) {
+ motion_log(LOG_ERR, 1, "Failed to open video device %s", conf->video_device);
+ free(dev);
+ pthread_mutex_unlock(&vid_mutex);
+ return -1;
+ }
- if (dev < 0) {
- motion_log(LOG_ERR, 1, "Failed to open video device %s", conf->video_device);
- return -1;
- }
+ pthread_mutexattr_init(&dev->attr);
+ pthread_mutex_init(&dev->mutex, &dev->attr);
- viddevs[i]->fd = dev;
- viddevs[i]->input = input;
- viddevs[i]->height = height;
- viddevs[i]->width = width;
- viddevs[i]->freq = frequency;
- viddevs[i]->tuner_number = tuner_number;
+ dev->usage_count = 1;
+ dev->fd = fd;
+ dev->input = input;
+ dev->height = height;
+ dev->width = width;
+ dev->freq = frequency;
+ dev->tuner_number = tuner_number;
- /* We set brightness, contrast, saturation and hue = 0 so that they only get
- * set if the config is not zero.
- */
- viddevs[i]->brightness = 0;
- viddevs[i]->contrast = 0;
- viddevs[i]->saturation = 0;
- viddevs[i]->hue = 0;
- viddevs[i]->owner = -1;
- viddevs[i]->v4l_fmt = VIDEO_PALETTE_YUV420P;
+ /* We set brightness, contrast, saturation and hue = 0 so that they only get
+ * set if the config is not zero.
+ */
+ dev->brightness = 0;
+ dev->contrast = 0;
+ dev->saturation = 0;
+ dev->hue = 0;
+ dev->owner = -1;
+ dev->v4l_fmt = VIDEO_PALETTE_YUV420P;
#ifdef MOTION_V4L2
- /* First lets try V4L2 and if it's not supported V4L1 */
+ /* First lets try V4L2 and if it's not supported V4L1 */
- viddevs[i]->v4l2 = 1;
+ dev->v4l2 = 1;
- if (!v4l2_start(cnt, viddevs[i], width, height, input, norm, frequency, tuner_number)) {
- /* restore width & height because could be changed in v4l2_start ()*/
- viddevs[i]->width = width;
- viddevs[i]->height = height;
+ if (!v4l2_start(cnt, dev, width, height, input, norm, frequency, tuner_number)) {
+ /* restore width & height before test with v4l
+ * because could be changed in v4l2_start ()
+ */
+ dev->width = width;
+ dev->height = height;
#endif
- if (!v4l_start(cnt, viddevs[i], width, height, input, norm, frequency, tuner_number)) {
- pthread_mutex_unlock(&vid_mutex);
- return -1;
- }
+ if (!v4l_start(cnt, dev, width, height, input, norm, frequency, tuner_number)) {
+ close(dev->fd);
+ pthread_mutexattr_destroy(&dev->attr);
+ pthread_mutex_destroy(&dev->mutex);
+ free(dev);
+
+ pthread_mutex_unlock(&vid_mutex);
+ return -1;
+ }
#ifdef MOTION_V4L2
- viddevs[i]->v4l2 = 0;
- }
+ dev->v4l2 = 0;
+ }
#endif
- if (viddevs[i]->v4l2 == 0) {
- motion_log(-1, 0, "Using V4L1");
- } else {
- motion_log(-1, 0, "Using V4L2");
- /* Update width & height because could be changed in v4l2_start () */
- width = viddevs[i]->width;
- height = viddevs[i]->height;
- cnt->conf.width = width;
- cnt->conf.height = height;
- cnt->imgs.width = width;
- cnt->imgs.height = height;
- }
+ if (dev->v4l2 == 0) {
+ motion_log(-1, 0, "Using V4L1");
+ } else {
+ motion_log(-1, 0, "Using V4L2");
+ /* Update width & height because could be changed in v4l2_start () */
+ width = dev->width;
+ height = dev->height;
+ cnt->imgs.width = width;
+ cnt->imgs.height = height;
+ }
- cnt->imgs.type = viddevs[i]->v4l_fmt;
+ cnt->imgs.type = dev->v4l_fmt;
- switch (cnt->imgs.type) {
- case VIDEO_PALETTE_GREY:
- cnt->imgs.size = width * height;
- cnt->imgs.motionsize = width * height;
- break;
- case VIDEO_PALETTE_YUYV:
- case VIDEO_PALETTE_RGB24:
- case VIDEO_PALETTE_YUV422:
- cnt->imgs.type = VIDEO_PALETTE_YUV420P;
- case VIDEO_PALETTE_YUV420P:
- cnt->imgs.size = (width * height * 3) / 2;
- cnt->imgs.motionsize = width * height;
- break;
- }
-
- pthread_mutex_unlock(&vid_mutex);
+ switch (cnt->imgs.type) {
+ case VIDEO_PALETTE_GREY:
+ cnt->imgs.size = width * height;
+ cnt->imgs.motionsize = width * height;
+ break;
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_YUV422:
+ cnt->imgs.type = VIDEO_PALETTE_YUV420P;
+ case VIDEO_PALETTE_YUV420P:
+ cnt->imgs.size = (width * height * 3) / 2;
+ cnt->imgs.motionsize = width * height;
+ break;
}
- return dev;
+ /* Insert into linked list */
+ dev->next = viddevs;
+ viddevs = dev;
+
+ pthread_mutex_unlock(&vid_mutex);
+
+return fd;
}
#endif /*WITHOUT_V4L */
int vid_start(struct context *cnt)
{
struct config *conf = &cnt->conf;
- int dev = -1;
+ int dev;
if (conf->netcam_url) {
- return netcam_start(cnt);
+ dev = netcam_start(cnt);
+ if (dev < 0) {
+ netcam_cleanup(cnt->netcam, 1);
+ cnt->netcam = NULL;
+ }
}
#ifndef WITHOUT_V4L
- dev = vid_v4lx_start(cnt);
+ else
+ dev = vid_v4lx_start(cnt);
#endif /*WITHOUT_V4L */
return dev;
@@ -809,60 +850,63 @@
*/
int vid_next(struct context *cnt, unsigned char *map)
{
+ int ret;
struct config *conf = &cnt->conf;
- int ret = -1;
if (conf->netcam_url) {
if (cnt->video_dev == -1)
return NETCAM_GENERAL_ERROR;
- ret = netcam_next(cnt, map);
- return ret;
+ return netcam_next(cnt, map);
}
#ifndef WITHOUT_V4L
-
/* We start a new block so we can make declarations without breaking
* gcc 2.95 or older
*/
{
- int i = -1;
+ struct video_dev *dev;
int width, height;
- int dev = cnt->video_dev;
/* NOTE: Since this is a capture, we need to use capture dimensions. */
width = cnt->rotate_data.cap_width;
height = cnt->rotate_data.cap_height;
- while (viddevs[++i])
- if (viddevs[i]->fd == dev)
+ pthread_mutex_lock(&vid_mutex);
+ dev = viddevs;
+ while (dev) {
+ if (dev->fd == cnt->video_dev)
break;
+ dev = dev->next;
+ }
+ pthread_mutex_unlock(&vid_mutex);
- if (!viddevs[i])
+ if (dev == NULL)
return V4L_FATAL_ERROR;
- if (viddevs[i]->owner != cnt->threadnr) {
- pthread_mutex_lock(&viddevs[i]->mutex);
- viddevs[i]->owner = cnt->threadnr;
- viddevs[i]->frames = conf->roundrobin_frames;
+ if (dev->owner != cnt->threadnr) {
+ pthread_mutex_lock(&dev->mutex);
+ dev->owner = cnt->threadnr;
+ dev->frames = conf->roundrobin_frames;
cnt->switched = 1;
}
#ifdef MOTION_V4L2
- if (viddevs[i]->v4l2) {
- v4l2_set_input(cnt, viddevs[i], map, width, height, conf);
+ if (dev->v4l2) {
+ v4l2_set_input(cnt, dev, map, width, height, conf);
- ret = v4l2_next(cnt, viddevs[i], map, width, height);
+ ret = v4l2_next(cnt, dev, map, width, height);
} else {
#endif
- v4l_set_input(cnt, viddevs[i], map, width, height, conf->input, conf->norm,
+ v4l_set_input(cnt, dev, map, width, height, conf->input, conf->norm,
conf->roundrobin_skip, conf->frequency, conf->tuner_number);
- ret = v4l_next(viddevs[i], map, width, height);
+ ret = v4l_next(dev, map, width, height);
#ifdef MOTION_V4L2
}
#endif
- if (--viddevs[i]->frames <= 0) {
- viddevs[i]->owner = -1;
- pthread_mutex_unlock(&viddevs[i]->mutex);
+ if (--dev->frames <= 0) {
+ dev->owner = -1;
+ dev->frames = 0;
+ pthread_mutex_unlock(&dev->mutex);
}
if (cnt->rotate_data.degrees > 0) {
Index: webhttpd.c
===================================================================
--- webhttpd.c (revision 275)
+++ webhttpd.c (working copy)
@@ -922,22 +922,41 @@
pointer = pointer + 7;
length_uri = length_uri - 7;
if (length_uri == 0) {
- do {
+ /*call restart*/
+
+ if (thread == 0) {
motion_log(LOG_DEBUG, 0, "httpd restart");
- kill(getpid(),1);
- } while (cnt[++i]);
-
- if (cnt[0]->conf.control_html_output) {
- send_template_ini_client(client_socket, ini_template);
- sprintf(res,"restart in progress ... bye
\nHome");
- send_template(client_socket, res);
- send_template_end_client(client_socket);
+ kill(getpid(),SIGHUP);
+ if (cnt[0]->conf.control_html_output) {
+ send_template_ini_client(client_socket, ini_template);
+ sprintf(res,"restart in progress ... bye
\nHome");
+ send_template(client_socket, res);
+ send_template_end_client(client_socket);
+ } else {
+ send_template_ini_client_raw(client_socket);
+ sprintf(res,"restart in progress ...\nDone\n");
+ send_template_raw(client_socket, res);
+ }
+ return 0; // to restart
} else {
- send_template_ini_client_raw(client_socket);
- sprintf(res,"restart in progress ...\nDone\n");
- send_template_raw(client_socket, res);
+ motion_log(LOG_DEBUG, 0, "httpd restart thread %d", thread);
+ if (cnt[thread]->running) {
+ cnt[thread]->makemovie=1;
+ cnt[thread]->finish=1;
+ }
+ cnt[thread]->restart=1;
+ if (cnt[0]->conf.control_html_output) {
+ send_template_ini_client(client_socket, ini_template);
+ sprintf(res,"<- back
\n"
+ "restart for thread %hu done
\n", thread, thread);
+ send_template(client_socket, res);
+ send_template_end_client(client_socket);
+ } else {
+ send_template_ini_client_raw(client_socket);
+ sprintf(res,"restart for thread %hu\nDone\n", thread);
+ send_template_raw(client_socket, res);
+ }
}
- return 0; // to restart
} else {
if (cnt[0]->conf.control_html_output)
response_client(client_socket,not_found_response_valid_command,NULL);
@@ -949,23 +968,39 @@
length_uri = length_uri - 4;
if (length_uri == 0) {
/*call quit*/
- do {
- motion_log(LOG_DEBUG, 0, "httpd quitting");
- cnt[i]->makemovie = 1;
- cnt[i]->finish = 1;
- } while (cnt[++i]);
- if (cnt[0]->conf.control_html_output) {
- send_template_ini_client(client_socket, ini_template);
- sprintf(res,"quit in progress ... bye");
- send_template(client_socket, res);
- send_template_end_client(client_socket);
+ if (thread == 0) {
+ motion_log(LOG_DEBUG, 0, "httpd quit");
+ kill(getpid(),SIGQUIT);
+ if (cnt[0]->conf.control_html_output) {
+ send_template_ini_client(client_socket, ini_template);
+ sprintf(res,"quit in progress ... bye");
+ send_template(client_socket, res);
+ send_template_end_client(client_socket);
+ } else {
+ send_template_ini_client_raw(client_socket);
+ sprintf(res,"quit in progress ... bye\nDone\n");
+ send_template_raw(client_socket, res);
+ }
+ return 0; // to quit
} else {
- send_template_ini_client_raw(client_socket);
- sprintf(res,"quit in progress ... bye\nDone\n");
- send_template_raw(client_socket, res);
+ motion_log(LOG_DEBUG, 0, "httpd quit thread %d", thread);
+ cnt[thread]->restart=0;
+ cnt[thread]->makemovie=1;
+ cnt[thread]->finish=1;
+ cnt[thread]->watchdog=WATCHDOG_OFF;
+ if (cnt[0]->conf.control_html_output) {
+ send_template_ini_client(client_socket, ini_template);
+ sprintf(res,"<- back
\n"
+ "quit for thread %hu done
\n", thread, thread);
+ send_template(client_socket, res);
+ send_template_end_client(client_socket);
+ } else {
+ send_template_ini_client_raw(client_socket);
+ sprintf(res,"quit for thread %hu\nDone\n", thread);
+ send_template_raw(client_socket, res);
+ }
}
- return 0; // to quit
} else {
/*error*/
if (cnt[0]->conf.control_html_output)
@@ -1006,11 +1041,12 @@
if (cnt[0]->conf.control_html_output) {
send_template_ini_client(client_socket, ini_template);
sprintf(res, "<- back
Thread %hu Detection status %s\n",
- thread, thread, (cnt[thread]->pause)? "PAUSE":"ACTIVE");
+ thread, thread, (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE");
send_template(client_socket, res);
send_template_end_client(client_socket);
} else {
- sprintf(res, "Thread %hu Detection status %s\n",thread, (cnt[thread]->pause)? "PAUSE":"ACTIVE");
+ sprintf(res, "Thread %hu Detection status %s\n",thread,
+ (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->pause)? "PAUSE":"ACTIVE");
send_template_ini_client_raw(client_socket);
send_template_raw(client_socket, res);
}
@@ -1098,12 +1134,12 @@
if (thread == 0){
do{
sprintf(res,"Thread %hu %s
\n",i,
- (cnt[i]->lost_connection)?CONNECTION_KO:CONNECTION_OK);
+ (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)?CONNECTION_KO:CONNECTION_OK);
send_template(client_socket,res);
}while (cnt[++i]);
}else{
- sprintf(res,"Thread %hu %s\n",
- thread, (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK);
+ sprintf(res,"Thread %hu %s\n", thread,
+ (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK);
send_template(client_socket,res);
}
send_template_end_client(client_socket);
@@ -1112,11 +1148,12 @@
if (thread == 0){
do{
sprintf(res,"Thread %hu %s\n", i,
- (cnt[i]->lost_connection)? CONNECTION_KO: CONNECTION_OK);
+ (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)? CONNECTION_KO: CONNECTION_OK);
send_template_raw(client_socket, res);
}while (cnt[++i]);
}else{
- sprintf(res,"Thread %hu %s\n", thread,(cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK);
+ sprintf(res,"Thread %hu %s\n", thread,
+ (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK);
send_template_raw(client_socket, res);
}