diff --git a/CHANGELOG b/CHANGELOG index f3eb2ddd..5f54a2ce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -89,6 +89,9 @@ Features * Bug fix as part of warnings in webhttpd.c fixed(Mr-Dave) * Removed compiler warning regarding ffmpeg being newer than 0.4 version(Mr-Dave) * New configure script and identification of ffmpeg version and additional libs. (Mr-Dave) + * Allow text format specifiers to take a width like printf would. (David Fries) + * Allow text format specifiers to take a width like printf would. (David Fries) + * Add power_line_frequency configuration item to improve image quality. (David Fries) * Resolve additional compiler warnings in ffmpeg (Mr-Dave) * Revised INSTALL with samples(Mr-Dave) * Revisions for RTSP and code standard.(Mr-Dave) @@ -147,6 +150,7 @@ Bugfixes * Fixed leak in vloopback. * Fixed a build of motion for some kernel version with not good videodev.h * Netcam Modulo 8 + * Fix webhttpd race condition crash with SIGHUP, add it to running thread counter (David Fries) 3.2.12 Summary of Changes diff --git a/CREDITS b/CREDITS index 89390a73..02434b12 100644 --- a/CREDITS +++ b/CREDITS @@ -473,6 +473,11 @@ Mark Feenstra Miguel Freitas * Came up with the round robing idea. +David Fries + * Fix webhttpd race condition crash with SIGHUP, add it to running thread counter + * Allow text format specifiers to take a width like printf would. + * Add power_line_frequency configuration item to improve image quality. + Aaron Gage * Pointed me to the vid_mmap/int problem when calling SYNC in video.c diff --git a/alg.c b/alg.c index 343ff47e..226ebd79 100644 --- a/alg.c +++ b/alg.c @@ -526,6 +526,8 @@ static int alg_labeling(struct context *cnt) int height = imgs->height; int labelsize = 0; int current_label = 2; + /* Keep track of the area just under the threshold. */ + int max_under = 0; cnt->current_image->total_labels = 0; imgs->labelsize_max = 0; @@ -561,7 +563,8 @@ static int alg_labeling(struct context *cnt) labelsize = iflood(ix, iy, width, height, out, labels, current_label + 32768, current_label); imgs->labelgroup_max += labelsize; imgs->labels_above++; - } + } else if(max_under < labelsize) + max_under = labelsize; if (imgs->labelsize_max < labelsize) { imgs->labelsize_max = labelsize; @@ -579,8 +582,11 @@ static int alg_labeling(struct context *cnt) "Largest Label: %i", imgs->largest_label, imgs->labelsize_max, cnt->current_image->total_labels); - /* Return group of significant labels. */ - return imgs->labelgroup_max; + /* Return group of significant labels or if that's none, the next largest + * group (which is under the threshold, but especially for setup gives an + * idea how close it was). + */ + return imgs->labelgroup_max ? imgs->labelgroup_max : max_under; } /** diff --git a/conf.c b/conf.c index 8454ac1b..f47bfffb 100644 --- a/conf.c +++ b/conf.c @@ -71,6 +71,7 @@ struct config conf_template = { contrast: 0, saturation: 0, hue: 0, + power_line_frequency: -1, roundrobin_frames: 1, roundrobin_skip: 1, pre_capture: 0, @@ -470,6 +471,22 @@ config_param config_params[] = { print_int }, { + "power_line_frequency", + "# Set the power line frequency to help cancel flicker by compensating\n" + "# for light intensity ripple. (default: -1).\n" + "# This can help reduce power line light flicker.\n" + "# Valuse :\n" + "# do not modify the device setting : -1\n" + "# V4L2_CID_POWER_LINE_FREQUENCY_DISABLED : 0\n" + "# V4L2_CID_POWER_LINE_FREQUENCY_50HZ : 1\n" + "# V4L2_CID_POWER_LINE_FREQUENCY_60HZ : 2\n" + "# V4L2_CID_POWER_LINE_FREQUENCY_AUTO : 3", + 0, + CONF_OFFSET(power_line_frequency), + copy_int, + print_int + }, + { "roundrobin_frames", "\n############################################################\n" "# Round Robin (multiple inputs on same video device name)\n" @@ -661,6 +678,15 @@ config_param config_params[] = { print_string }, { + "ffmpeg_duplicate_frames", + "# True to duplicate frames to achieve \"framerate\" fps, but enough\n" + "duplicated frames and the video appears to freeze once a second.", + 0, + CONF_OFFSET(ffmpeg_duplicate_frames), + copy_bool, + print_bool + }, + { "output_debug_pictures", "# Output pictures with only the pixels moving object (ghost images) (default: off)", 0, diff --git a/conf.h b/conf.h index c487b9e9..a6ad5c58 100644 --- a/conf.h +++ b/conf.h @@ -30,6 +30,7 @@ struct config { int max_changes; int threshold_tune; const char *output_pictures; + int ffmpeg_duplicate_frames; int motion_img; int emulate_motion; int event_gap; @@ -53,6 +54,7 @@ struct config { int contrast; int saturation; int hue; + int power_line_frequency; int roundrobin_frames; int roundrobin_skip; int pre_capture; diff --git a/jpegutils.c b/jpegutils.c index 89980346..7447c9dc 100644 --- a/jpegutils.c +++ b/jpegutils.c @@ -818,7 +818,7 @@ int decode_jpeg_gray_raw(unsigned char *jpeg_data, int len, guarantee_huff_tables(&dinfo); jpeg_start_decompress (&dinfo); - vsf[0]= 1; vsf[1] = 1; vsf[2] = 1; + vsf[0] = 1; vsf[1] = 1; vsf[2] = 1; /* Height match image height or be exact twice the image height. */ diff --git a/motion.c b/motion.c index 7f6e6fdf..e6f11cfc 100644 --- a/motion.c +++ b/motion.c @@ -134,6 +134,7 @@ static void image_ring_resize(struct context *cnt, int new_size) /* Point to the new ring */ cnt->imgs.image_ring = tmp; + cnt->current_image = NULL; cnt->imgs.image_ring_size = new_size; } @@ -168,13 +169,14 @@ static void image_ring_destroy(struct context *cnt) free(cnt->imgs.image_ring); cnt->imgs.image_ring = NULL; + cnt->current_image = NULL; cnt->imgs.image_ring_size = 0; } /** * image_save_as_preview * - * This routine is called when we detect motion and want to save a image in the preview buffer + * This routine is called when we detect motion and want to save an image in the preview buffer * * Parameters: * @@ -341,6 +343,7 @@ static void sig_handler(int signo) while (cnt_list[++i]) { cnt_list[i]->makemovie = 1; cnt_list[i]->finish = 1; + cnt_list[i]->webcontrol_finish = 1; /* * Don't restart thread when it ends, * all threads restarts if global restart is set @@ -356,6 +359,9 @@ static void sig_handler(int signo) break; case SIGSEGV: exit(0); + case SIGVTALRM: + printf("SIGVTALRM went off\n"); + break; } } @@ -565,8 +571,13 @@ static void process_image_ring(struct context *cnt, unsigned int max_images) /* * Check if we must add any "filler" frames into movie to keep up fps * Only if we are recording videos ( ffmpeg or extenal pipe ) + * While the overall elapsed time might be correct, if there are + * many duplicated frames, say 10 fps, 5 duplicated, the video will + * look like it is frozen every second for half a second. */ - if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) && + if (!cnt->conf.ffmpeg_duplicate_frames) { + /* don't duplicate frames */ + } else if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) && #ifdef HAVE_FFMPEG (cnt->ffmpeg_output || (cnt->conf.useextpipe && cnt->extpipe))) { #else @@ -1088,8 +1099,6 @@ static void *motion_loop(void *arg) */ unsigned long int time_last_frame = 1, time_current_frame; - cnt->running = 1; - if (motion_init(cnt) < 0) goto err; @@ -2526,6 +2535,10 @@ static void setup_signals(struct sigaction *sig_handler_action, struct sigaction sigaction(SIGQUIT, sig_handler_action, NULL); sigaction(SIGTERM, sig_handler_action, NULL); sigaction(SIGUSR1, sig_handler_action, NULL); + + /* use SIGVTALRM as a way to break out of the ioctl, don't restart */ + sig_handler_action->sa_flags = 0; + sigaction(SIGVTALRM, sig_handler_action, NULL); } /** @@ -2595,11 +2608,22 @@ static void start_motion_thread(struct context *cnt, pthread_attr_t *thread_attr /* Give the thread WATCHDOG_TMO to start */ cnt->watchdog = WATCHDOG_TMO; + /* Flag it as running outside of the thread, otherwise if the main loop + * checked if it is was running before the thread set it to 1, it would + * start another thread for this device. */ + cnt->running = 1; + /* * Create the actual thread. Use 'motion_loop' as the thread * function. */ - pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt); + if (pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt)) { + /* thread create failed, undo running state */ + cnt->running = 0; + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + } } /** @@ -2714,8 +2738,21 @@ int main (int argc, char **argv) * Create a thread for the control interface if requested. Create it * detached and with 'motion_web_control' as the thread function. */ - if (cnt_list[0]->conf.webcontrol_port) - pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list); + if (cnt_list[0]->conf.webcontrol_port) { + pthread_mutex_lock(&global_lock); + threads_running++; + /* set outside the loop to avoid thread set vs main thread check */ + cnt_list[0]->webcontrol_running = 1; + pthread_mutex_unlock(&global_lock); + if (pthread_create(&thread_id, &thread_attr, &motion_web_control, + cnt_list)) { + /* thread create failed, undo running state */ + pthread_mutex_lock(&global_lock); + threads_running--; + cnt_list[0]->webcontrol_running = 0; + pthread_mutex_unlock(&global_lock); + } + } MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Waiting for threads to finish, pid: %d", getpid()); @@ -2728,7 +2765,7 @@ int main (int argc, char **argv) SLEEP(1, 0); /* - * Calculate how many threads runnig or wants to run + * Calculate how many threads are running or wants to run * if zero and we want to finish, break out */ int motion_threads_running = 0; @@ -2737,6 +2774,9 @@ int main (int argc, char **argv) if (cnt_list[i]->running || cnt_list[i]->restart) motion_threads_running++; } + if (cnt_list[0]->conf.webcontrol_port && + cnt_list[0]->webcontrol_running) + motion_threads_running++; if (((motion_threads_running == 0) && finish) || ((motion_threads_running == 0) && (threads_running == 0))) { @@ -2754,24 +2794,42 @@ int main (int argc, char **argv) } if (cnt_list[i]->watchdog > WATCHDOG_OFF) { - cnt_list[i]->watchdog--; + if (cnt_list[i]->watchdog == WATCHDOG_KILL) { + /* if 0 then it finally did clean up (and will restart without any further action here) + * kill(, 0) == ESRCH means the thread is no longer running + * if it is no longer running with running set, then cleanup here so it can restart + */ + if(cnt_list[i]->running && pthread_kill(cnt_list[i]->thread_id, 0) == ESRCH) { + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: cleaning Thread %d", cnt_list[i]->threadnr); + 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; + } else { + /* keep sending signals so it doesn't get stuck in a blocking call */ + pthread_kill(cnt_list[i]->thread_id, SIGVTALRM); + } + } else { + cnt_list[i]->watchdog--; - if (cnt_list[i]->watchdog == 0) { - MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: 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(ERR, TYPE_ALL, NO_ERRNO, "%s: 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; + if (cnt_list[i]->watchdog == 0) { + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Thread %d - Watchdog timeout, trying to do " + "a graceful restart", cnt_list[i]->threadnr); + cnt_list[i]->finish = 1; + } + + if (cnt_list[i]->watchdog == WATCHDOG_KILL) { + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Thread %d - Watchdog timeout, did NOT restart graceful," + "killing it!", cnt_list[i]->threadnr); + /* The problem is pthead_cancel might just wake up the thread so it runs to completition + * or it might not. In either case don't rip the carpet out under it by + * doing motion_cleanup until it no longer is running. + */ + pthread_cancel(cnt_list[i]->thread_id); + } } } } @@ -2796,7 +2854,7 @@ int main (int argc, char **argv) // Be sure that http control exits fine - cnt_list[0]->finish = 1; + cnt_list[0]->webcontrol_finish = 1; SLEEP(1, 0); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion terminating"); @@ -3098,6 +3156,7 @@ size_t mystrftime(const struct context *cnt, char *s, size_t max, const char *us char tempstring[PATH_MAX] = ""; char *format, *tempstr; const char *pos_userformat; + int width; format = formatstring; @@ -3117,6 +3176,12 @@ size_t mystrftime(const struct context *cnt, char *s, size_t max, const char *us */ tempstr = tempstring; tempstr[0] = '\0'; + width = 0; + while ('0' <= pos_userformat[1] && pos_userformat[1] <= '9') { + width *= 10; + width += pos_userformat[1] - '0'; + ++pos_userformat; + } switch (*++pos_userformat) { case '\0': // end of string @@ -3124,81 +3189,86 @@ size_t mystrftime(const struct context *cnt, char *s, size_t max, const char *us break; case 'v': // event - sprintf(tempstr, "%02d", cnt->event_nr); + sprintf(tempstr, "%0*d", width ? width : 2, cnt->event_nr); break; case 'q': // shots - sprintf(tempstr, "%02d", cnt->current_image->shot); + sprintf(tempstr, "%0*d", width ? width : 2, + cnt->current_image->shot); break; case 'D': // diffs - sprintf(tempstr, "%d", cnt->current_image->diffs); + sprintf(tempstr, "%*d", width, cnt->current_image->diffs); break; case 'N': // noise - sprintf(tempstr, "%d", cnt->noise); + sprintf(tempstr, "%*d", width, cnt->noise); break; case 'i': // motion width - sprintf(tempstr, "%d", cnt->current_image->location.width); + sprintf(tempstr, "%*d", width, + cnt->current_image->location.width); break; case 'J': // motion height - sprintf(tempstr, "%d", cnt->current_image->location.height); + sprintf(tempstr, "%*d", width, + cnt->current_image->location.height); break; case 'K': // motion center x - sprintf(tempstr, "%d", cnt->current_image->location.x); + sprintf(tempstr, "%*d", width, cnt->current_image->location.x); break; case 'L': // motion center y - sprintf(tempstr, "%d", cnt->current_image->location.y); + sprintf(tempstr, "%*d", width, cnt->current_image->location.y); break; case 'o': // threshold - sprintf(tempstr, "%d", cnt->threshold); + sprintf(tempstr, "%*d", width, cnt->threshold); break; case 'Q': // number of labels - sprintf(tempstr, "%d", cnt->current_image->total_labels); + sprintf(tempstr, "%*d", width, + cnt->current_image->total_labels); break; case 't': // thread number - sprintf(tempstr, "%d",(int)(unsigned long) + sprintf(tempstr, "%*d", width, (int)(unsigned long) pthread_getspecific(tls_key_threadnr)); break; case 'C': // text_event if (cnt->text_event_string && cnt->text_event_string[0]) - snprintf(tempstr, PATH_MAX, "%s", cnt->text_event_string); + snprintf(tempstr, PATH_MAX, "%*s", width, + cnt->text_event_string); else ++pos_userformat; break; case 'w': // picture width - sprintf(tempstr, "%d", cnt->imgs.width); + sprintf(tempstr, "%*d", width, cnt->imgs.width); break; case 'h': // picture height - sprintf(tempstr, "%d", cnt->imgs.height); + sprintf(tempstr, "%*d", width, cnt->imgs.height); break; case 'f': // filename -- or %fps if ((*(pos_userformat+1) == 'p') && (*(pos_userformat+2) == 's')) { - sprintf(tempstr, "%d", cnt->movie_fps); + sprintf(tempstr, "%*d", width, cnt->movie_fps); pos_userformat += 2; break; } if (filename) - snprintf(tempstr, PATH_MAX, "%s", filename); + snprintf(tempstr, PATH_MAX, "%*s", width, filename); else ++pos_userformat; break; case 'n': // sqltype if (sqltype) - sprintf(tempstr, "%d", sqltype); + sprintf(tempstr, "%*d", width, sqltype); else ++pos_userformat; break; diff --git a/motion.h b/motion.h index c08d84f1..65d26986 100644 --- a/motion.h +++ b/motion.h @@ -149,6 +149,7 @@ */ #define WATCHDOG_TMO 30 /* 30 sec max motion_loop interval */ +#define WATCHDOG_KILL -60 /* -60 sec grace period before calling thread cancel */ #define WATCHDOG_OFF -127 /* Turn off watchdog, used when we wants to quit a thread */ #define CONNECTION_KO "Lost connection" @@ -370,6 +371,9 @@ struct context { volatile unsigned int restart; /* Restart the thread when it ends */ /* Is the motion thread running */ volatile unsigned int running; + /* Is the web control thread running */ + volatile unsigned int webcontrol_running; + volatile unsigned int webcontrol_finish; /* End the thread */ volatile int watchdog; pthread_t thread_id; diff --git a/netcam_ftp.c b/netcam_ftp.c index 841c3ad8..df7055d0 100644 --- a/netcam_ftp.c +++ b/netcam_ftp.c @@ -786,12 +786,11 @@ int ftp_get_socket(ftp_context_pointer ctxt) */ int ftp_send_type(ftp_context_pointer ctxt, char type) { - char buf[100]; + char buf[100], utype; int len, res; - toupper(type); - /* Assure transfer will be in "image" mode. */ - snprintf(buf, sizeof(buf), "TYPE I\r\n"); + utype = toupper(type); + snprintf(buf, sizeof(buf), "TYPE %c\r\n", utype); len = strlen(buf); res = send(ctxt->control_file_desc, buf, len, 0); diff --git a/video.h b/video.h index 193087bd..1ac45534 100644 --- a/video.h +++ b/video.h @@ -65,6 +65,7 @@ struct video_dev { int contrast; int saturation; int hue; + int power_line_frequency; unsigned long freq; int tuner_number; int fps; diff --git a/video2.c b/video2.c index e1a617a7..bca75356 100644 --- a/video2.c +++ b/video2.c @@ -144,6 +144,10 @@ static const u32 queried_ctrls[] = { V4L2_CID_CONTRAST, V4L2_CID_SATURATION, V4L2_CID_HUE, +/* first added in Linux kernel v2.6.26 */ +#ifdef V4L2_CID_POWER_LINE_FREQUENCY + V4L2_CID_POWER_LINE_FREQUENCY, +#endif V4L2_CID_RED_BALANCE, V4L2_CID_BLUE_BALANCE, @@ -173,6 +177,7 @@ typedef struct { u32 ctrl_flags; struct v4l2_queryctrl *controls; + volatile unsigned int *finish; /* End the thread */ } src_v4l2_t; @@ -180,16 +185,16 @@ typedef struct { * xioctl */ #ifdef __OpenBSD__ -static int xioctl(int fd, unsigned long request, void *arg) +static int xioctl(src_v4l2_t *vid_source, unsigned long request, void *arg) #else -static int xioctl(int fd, int request, void *arg) +static int xioctl(src_v4l2_t *vid_source, int request, void *arg) #endif { int ret; do - ret = ioctl(fd, request, arg); - while (-1 == ret && EINTR == errno); + ret = ioctl(vid_source->fd, request, arg); + while (-1 == ret && EINTR == errno && !vid_source->finish); return ret; } @@ -199,7 +204,7 @@ static int xioctl(int fd, int request, void *arg) */ static int v4l2_get_capability(src_v4l2_t * vid_source) { - if (xioctl(vid_source->fd, VIDIOC_QUERYCAP, &vid_source->cap) < 0) { + if (xioctl(vid_source, VIDIOC_QUERYCAP, &vid_source->cap) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Not a V4L2 device?"); return -1; } @@ -262,7 +267,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, input.index = IN_TV; else input.index = in; - if (xioctl(vid_source->fd, VIDIOC_ENUMINPUT, &input) == -1) { + if (xioctl(vid_source, VIDIOC_ENUMINPUT, &input) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Unable to query input %d." " VIDIOC_ENUMINPUT, if you use a WEBCAM change input value in conf by -1", input.index); @@ -278,7 +283,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, if (input.type & V4L2_INPUT_TYPE_CAMERA) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - CAMERA"); - if (xioctl(vid_source->fd, VIDIOC_S_INPUT, &input.index) == -1) { + if (xioctl(vid_source, VIDIOC_S_INPUT, &input.index) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error selecting input %d" " VIDIOC_S_INPUT", input.index); return -1; @@ -290,7 +295,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, * Set video standard usually webcams doesn't support the ioctl or * return V4L2_STD_UNKNOWN */ - if (xioctl(vid_source->fd, VIDIOC_G_STD, &std_id) == -1) { + if (xioctl(vid_source, VIDIOC_G_STD, &std_id) == -1) { MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Device doesn't support VIDIOC_G_STD"); norm = std_id = 0; // V4L2_STD_UNKNOWN = 0 } @@ -299,7 +304,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, memset(&standard, 0, sizeof(standard)); standard.index = 0; - while (xioctl(vid_source->fd, VIDIOC_ENUMSTD, &standard) == 0) { + while (xioctl(vid_source, VIDIOC_ENUMSTD, &standard) == 0) { if (standard.id & std_id) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - video standard %s", standard.name); @@ -318,7 +323,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, std_id = V4L2_STD_PAL; } - if (xioctl(vid_source->fd, VIDIOC_S_STD, &std_id) == -1) + if (xioctl(vid_source, VIDIOC_S_STD, &std_id) == -1) MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error selecting standard" " method %d VIDIOC_S_STD", (int)std_id); @@ -338,7 +343,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, memset(&tuner, 0, sizeof(struct v4l2_tuner)); tuner.index = input.tuner; - if (xioctl(vid_source->fd, VIDIOC_G_TUNER, &tuner) == -1) { + if (xioctl(vid_source, VIDIOC_G_TUNER, &tuner) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: tuner %d VIDIOC_G_TUNER", tuner.index); return 0; @@ -353,7 +358,7 @@ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, freq.type = V4L2_TUNER_ANALOG_TV; freq.frequency = (freq_ / 1000) * 16; - if (xioctl(vid_source->fd, VIDIOC_S_FREQUENCY, &freq) == -1) { + if (xioctl(vid_source, VIDIOC_S_FREQUENCY, &freq) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: freq %ul VIDIOC_S_FREQUENCY", freq.frequency); return 0; @@ -405,7 +410,7 @@ static int v4l2_do_set_pix_format(u32 pixformat, src_v4l2_t * vid_source, vid_source->dst_fmt.fmt.pix.pixelformat = pixformat; vid_source->dst_fmt.fmt.pix.field = V4L2_FIELD_ANY; - if (xioctl(vid_source->fd, VIDIOC_TRY_FMT, &vid_source->dst_fmt) != -1 && + if (xioctl(vid_source, VIDIOC_TRY_FMT, &vid_source->dst_fmt) != -1 && vid_source->dst_fmt.fmt.pix.pixelformat == pixformat) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Testing palette %c%c%c%c (%dx%d)", pixformat >> 0, pixformat >> 8, @@ -423,7 +428,7 @@ static int v4l2_do_set_pix_format(u32 pixformat, src_v4l2_t * vid_source, *height = vid_source->dst_fmt.fmt.pix.height; } - if (xioctl(vid_source->fd, VIDIOC_S_FMT, &vid_source->dst_fmt) == -1) { + if (xioctl(vid_source, VIDIOC_S_FMT, &vid_source->dst_fmt) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error setting pixel " "format.\nVIDIOC_S_FMT: "); return -1; @@ -502,7 +507,7 @@ static int v4l2_set_pix_format(struct context *cnt, src_v4l2_t * vid_source, MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Supported palettes:"); - while (xioctl(vid_source->fd, VIDIOC_ENUM_FMT, &fmtd) != -1) { + while (xioctl(vid_source, VIDIOC_ENUM_FMT, &fmtd) != -1) { int i; @@ -556,7 +561,7 @@ static void v4l2_set_fps(src_v4l2_t * vid_source) { setfpvid_source->parm.capture.timeperframe.numerator = 1; setfpvid_source->parm.capture.timeperframe.denominator = vid_source->fps; - if (xioctl(vid_source->fd, VIDIOC_S_PARM, setfps) == -1) + if (xioctl(vid_source, VIDIOC_S_PARM, setfps) == -1) MOTION_LOG(ERR, 1, "%s: v4l2_set_fps VIDIOC_S_PARM"); @@ -581,7 +586,7 @@ static int v4l2_set_mmap(src_v4l2_t * vid_source) vid_source->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_source->req.memory = V4L2_MEMORY_MMAP; - if (xioctl(vid_source->fd, VIDIOC_REQBUFS, &vid_source->req) == -1) { + if (xioctl(vid_source, VIDIOC_REQBUFS, &vid_source->req) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error requesting buffers" " %d for memory map. VIDIOC_REQBUFS", vid_source->req.count); @@ -613,7 +618,7 @@ static int v4l2_set_mmap(src_v4l2_t * vid_source) buf.memory = V4L2_MEMORY_MMAP; buf.index = buffer_index; - if (xioctl(vid_source->fd, VIDIOC_QUERYBUF, &buf) == -1) { + if (xioctl(vid_source, VIDIOC_QUERYBUF, &buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error querying buffer" " %i\nVIDIOC_QUERYBUF: ", buffer_index); free(vid_source->buffers); @@ -642,7 +647,7 @@ static int v4l2_set_mmap(src_v4l2_t * vid_source) vid_source->buf.memory = V4L2_MEMORY_MMAP; vid_source->buf.index = buffer_index; - if (xioctl(vid_source->fd, VIDIOC_QBUF, &vid_source->buf) == -1) { + if (xioctl(vid_source, VIDIOC_QBUF, &vid_source->buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_QBUF"); return -1; } @@ -650,7 +655,7 @@ static int v4l2_set_mmap(src_v4l2_t * vid_source) type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (xioctl(vid_source->fd, VIDIOC_STREAMON, &type) == -1) { + if (xioctl(vid_source, VIDIOC_STREAMON, &type) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error starting stream." " VIDIOC_STREAMON"); return -1; @@ -671,7 +676,7 @@ static int v4l2_scan_controls(src_v4l2_t * vid_source) for (i = 0, count = 0; queried_ctrls[i]; i++) { queryctrl.id = queried_ctrls[i]; - if (xioctl(vid_source->fd, VIDIOC_QUERYCTRL, &queryctrl)) + if (xioctl(vid_source, VIDIOC_QUERYCTRL, &queryctrl)) continue; count++; @@ -692,7 +697,7 @@ static int v4l2_scan_controls(src_v4l2_t * vid_source) struct v4l2_control control; queryctrl.id = queried_ctrls[i]; - if (xioctl(vid_source->fd, VIDIOC_QUERYCTRL, &queryctrl)) + if (xioctl(vid_source, VIDIOC_QUERYCTRL, &queryctrl)) continue; memcpy(ctrl, &queryctrl, sizeof(struct v4l2_queryctrl)); @@ -704,7 +709,7 @@ static int v4l2_scan_controls(src_v4l2_t * vid_source) memset(&control, 0, sizeof (control)); control.id = queried_ctrls[i]; - xioctl(vid_source->fd, VIDIOC_G_CTRL, &control); + xioctl(vid_source, VIDIOC_G_CTRL, &control); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: \t\"%s\", default %d, current %d", ctrl->name, ctrl->default_value, control.value); @@ -740,14 +745,21 @@ static int v4l2_set_control(src_v4l2_t * vid_source, u32 cid, int value) case V4L2_CTRL_TYPE_INTEGER: value = control.value = (value * (ctrl->maximum - ctrl->minimum) / 256) + ctrl->minimum; - ret = xioctl(vid_source->fd, VIDIOC_S_CTRL, &control); + ret = xioctl(vid_source, VIDIOC_S_CTRL, &control); break; case V4L2_CTRL_TYPE_BOOLEAN: value = control.value = value ? 1 : 0; - ret = xioctl(vid_source->fd, VIDIOC_S_CTRL, &control); + ret = xioctl(vid_source, VIDIOC_S_CTRL, &control); break; + case V4L2_CTRL_TYPE_MENU: + /* set as is, no adjustments */ + control.value = value; + ret = xioctl(vid_source, VIDIOC_S_CTRL, &control); + break; + + default: MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: control type not supported yet"); return -1; @@ -788,6 +800,14 @@ static void v4l2_picture_controls(struct context *cnt, struct video_dev *viddev) v4l2_set_control(vid_source, V4L2_CID_HUE, viddev->hue); } +#ifdef V4L2_CID_POWER_LINE_FREQUENCY + /* -1 is don't modify as 0 is an option to disable the power line filter */ + if (cnt->conf.power_line_frequency != -1 && cnt->conf.power_line_frequency != viddev->power_line_frequency) { + viddev->power_line_frequency = cnt->conf.power_line_frequency; + v4l2_set_control(vid_source, V4L2_CID_POWER_LINE_FREQUENCY, viddev->power_line_frequency); + } +#endif + if (cnt->conf.autobright) { if (vid_do_autobright(cnt, viddev)) { if (v4l2_set_control(vid_source, V4L2_CID_BRIGHTNESS, viddev->brightness)) @@ -822,6 +842,7 @@ unsigned char *v4l2_start(struct context *cnt, struct video_dev *viddev, int wid vid_source->fd = viddev->fd; vid_source->fps = cnt->conf.frame_limit; vid_source->pframe = -1; + vid_source->finish = &cnt->finish; struct config *conf = &cnt->conf; if (v4l2_get_capability(vid_source)) @@ -965,7 +986,7 @@ int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map, vid_source->pframe); if (vid_source->pframe >= 0) { - if (xioctl(vid_source->fd, VIDIOC_QBUF, &vid_source->buf) == -1) { + if (xioctl(vid_source, VIDIOC_QBUF, &vid_source->buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_QBUF"); pthread_sigmask(SIG_UNBLOCK, &old, NULL); return -1; @@ -977,7 +998,7 @@ int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map, vid_source->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_source->buf.memory = V4L2_MEMORY_MMAP; - if (xioctl(vid_source->fd, VIDIOC_DQBUF, &vid_source->buf) == -1) { + if (xioctl(vid_source, VIDIOC_DQBUF, &vid_source->buf) == -1) { int ret; /* * Some drivers return EIO when there is no signal, @@ -1082,7 +1103,7 @@ void v4l2_close(struct video_dev *viddev) enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - xioctl(vid_source->fd, VIDIOC_STREAMOFF, &type); + xioctl(vid_source, VIDIOC_STREAMOFF, &type); close(vid_source->fd); vid_source->fd = -1; } diff --git a/video_common.c b/video_common.c index 54569c6e..cabc5aa4 100644 --- a/video_common.c +++ b/video_common.c @@ -732,6 +732,8 @@ static int vid_v4lx_start(struct context *cnt) dev->contrast = 0; dev->saturation = 0; dev->hue = 0; + /* -1 is don't modify, (0 is a valid value) */ + dev->power_line_frequency = -1; dev->owner = -1; dev->v4l_fmt = VIDEO_PALETTE_YUV420P; dev->fps = 0; diff --git a/webhttpd.c b/webhttpd.c index 2b3a1333..3693ae8d 100644 --- a/webhttpd.c +++ b/webhttpd.c @@ -16,6 +16,9 @@ #include #include +/* Timeout in seconds, used for read and write */ +const int NONBLOCK_TIMEOUT = 1; + pthread_mutex_t httpd_mutex; // This is a dummy variable use to kill warnings when not checking sscanf and similar functions @@ -197,7 +200,7 @@ static ssize_t write_nonblock(int fd, const void *buf, size_t size) struct timeval tm; fd_set fds; - tm.tv_sec = 1; /* Timeout in seconds */ + tm.tv_sec = NONBLOCK_TIMEOUT; tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); @@ -223,7 +226,7 @@ static ssize_t read_nonblock(int fd ,void *buf, ssize_t size) struct timeval tm; fd_set fds; - tm.tv_sec = 1; /* Timeout in seconds */ + tm.tv_sec = NONBLOCK_TIMEOUT; /* Timeout in seconds */ tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); @@ -2509,10 +2512,10 @@ void httpd_run(struct context **cnt) while ((client_sent_quit_message) && (!closehttpd)) { - client_socket_fd = acceptnonblocking(sd, 1); + client_socket_fd = acceptnonblocking(sd, NONBLOCK_TIMEOUT); if (client_socket_fd < 0) { - if ((!cnt[0]) || (cnt[0]->finish)) { + if ((!cnt[0]) || (cnt[0]->webcontrol_finish)) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd - Finishing"); closehttpd = 1; } @@ -2542,6 +2545,17 @@ void *motion_web_control(void *arg) { struct context **cnt = arg; httpd_run(cnt); + + /* + * 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--; + cnt[0]->webcontrol_running = 0; + pthread_mutex_unlock(&global_lock); + MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd thread exit"); pthread_exit(NULL); }