Index: alg.c =================================================================== --- alg.c (revision 439) +++ alg.c (working copy) @@ -1242,7 +1242,7 @@ * */ /* Seconds */ -#define ACCEPT_STATIC_OBJECT_TIME 10 +#define ACCEPT_STATIC_OBJECT_TIME 0 #define EXCLUDE_LEVEL_PERCENT 20 void alg_update_reference_frame(struct context *cnt, int action) { Index: conf.c =================================================================== --- conf.c (revision 439) +++ conf.c (working copy) @@ -82,6 +82,7 @@ ffmpeg_output: 0, extpipe: NULL, useextpipe: 0, + smooth_video: 0, ffmpeg_output_debug: 0, ffmpeg_bps: DEF_FFMPEG_BPS, ffmpeg_vbr: DEF_FFMPEG_VBR, @@ -104,7 +105,7 @@ tuner_device: NULL, #endif video_device: VIDEO_DEVICE, - v4l2_palette: 11, + v4l2_palette: DEF_PALETTE, vidpipe: NULL, filepath: NULL, imagepath: DEF_IMAGEPATH, @@ -147,6 +148,7 @@ text_left: NULL, text_right: DEF_TIMESTAMP, text_event: DEF_EVENTSTAMP, + cameraname: "CAMERA NAME", text_double: 0, despeckle_filter: NULL, area_detect: NULL, @@ -317,6 +319,17 @@ print_int }, { + "force_framerate", + "# Force the number of frames per second set in framerate.\n" + "# This is useful for capture cards on busy system where motion\n" + "# often failed to detect the correct framerate.\n" + "# Valid range: 2-100. Default: 100 (almost no limit).", + 0, + CONF_OFFSET(force_framerate), + copy_bool, + print_bool + }, + { "minimum_frame_time", "# Minimum time in seconds between capturing picture frames from the camera.\n" "# Default: 0 = disabled - the capture rate is given by the camera framerate.\n" @@ -750,6 +763,19 @@ print_string }, { + "smooth_video", + "\n############################################################\n" + "# Normally, video is recorded as __precap+motion+postcap____\n" + "# This option changes the default behavior of the video \n" + "# capturing so that it is __precap+event-start-->event-end__ \n" + "############################################################\n" + "# Use smooth_video on to enable this feature. \n", + 0, + CONF_OFFSET(smooth_video), + copy_bool, + print_bool + }, + { "snapshot_interval", "\n############################################################\n" "# Snapshots (Traditional Periodic Webcam File Output)\n" @@ -826,6 +852,16 @@ print_bool }, { + "cameraname", + "# This option defines the value of the special event conversion specifier %e\n" + "# Default: Camera 1\n" + "# The idea is that %e can be used as part of the filename to identify a camera\n", + 0, + CONF_OFFSET(cameraname), + copy_string, + print_string + }, + { "text_event", "# This option defines the value of the special event conversion specifier %C\n" "# You can use any conversion specifier in this option except %C. Date and time\n" Index: conf.h =================================================================== --- conf.h (revision 439) +++ conf.h (working copy) @@ -37,7 +37,9 @@ int input; int norm; int frame_limit; + int force_framerate; int quiet; + int smooth_video; int useextpipe; /* ext_pipe on or off */ const char *extpipe; /* full command line for pipe -- must accept YUV420P images */ const char *picture_type; @@ -118,6 +120,7 @@ const char *text_left; const char *text_right; const char *text_event; + const char *cameraname; int text_double; const char *despeckle_filter; const char *area_detect; Index: configure =================================================================== --- configure (revision 439) +++ configure (working copy) @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.61 for motion trunk-r438. +# Generated by GNU Autoconf 2.61 for motion trunk-r439. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. @@ -572,8 +572,8 @@ # Identity of this package. PACKAGE_NAME='motion' PACKAGE_TARNAME='motion' -PACKAGE_VERSION='trunk-r438' -PACKAGE_STRING='motion trunk-r438' +PACKAGE_VERSION='trunk-r439' +PACKAGE_STRING='motion trunk-r439' PACKAGE_BUGREPORT='' ac_unique_file="motion.c" @@ -1177,7 +1177,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures motion trunk-r438 to adapt to many kinds of systems. +\`configure' configures motion trunk-r439 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1238,7 +1238,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of motion trunk-r438:";; + short | recursive ) echo "Configuration of motion trunk-r439:";; esac cat <<\_ACEOF @@ -1376,7 +1376,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -motion configure trunk-r438 +motion configure trunk-r439 generated by GNU Autoconf 2.61 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1390,7 +1390,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by motion $as_me trunk-r438, which was +It was created by motion $as_me trunk-r439, which was generated by GNU Autoconf 2.61. Invocation command line was $ $0 $@ @@ -8230,7 +8230,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by motion $as_me trunk-r438, which was +This file was extended by motion $as_me trunk-r439, which was generated by GNU Autoconf 2.61. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -8279,7 +8279,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -motion config.status trunk-r438 +motion config.status trunk-r439 configured by $0, generated by GNU Autoconf 2.61, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Index: event.c =================================================================== --- event.c (revision 439) +++ event.c (working copy) @@ -368,8 +368,10 @@ if (cnt->extpipe_open) { cnt->extpipe_open = 0; fflush(cnt->extpipe); - motion_log(LOG_INFO, 0, "%s: CLOSING: extpipe file desc %d, error state %d", + if (debug_level >= CAMERA_INFO) { + motion_log(LOG_INFO, 0, "%s: CLOSING: extpipe file desc %d, error state %d", __FUNCTION__, fileno(cnt->extpipe), ferror(cnt->extpipe)); + } motion_log(LOG_INFO, 0, "%s: pclose return: %d", __FUNCTION__, pclose(cnt->extpipe)); event(cnt, EVENT_FILECLOSE, NULL, cnt->extpipefilename, (void *)FTYPE_MPEG, NULL); } @@ -451,8 +453,10 @@ motion_log(LOG_ERR, 1, "%s: Error writting in pipe , state error %d", __FUNCTION__, ferror(cnt->extpipe)); } else { + if (debug_level >= CAMERA_DEBUG) { motion_log(LOG_ERR, 0, "%s: pipe %s not created or closed already ", - __FUNCTION__, cnt->extpipe); + __FUNCTION__, cnt->extpipe); + } } } } @@ -469,10 +473,14 @@ if (debug_level >= CAMERA_INFO) motion_log(LOG_DEBUG, 0, "%s FPS %d", __FUNCTION__, cnt->movie_fps); - if (cnt->movie_fps > 30) + if (cnt->conf.force_framerate) + cnt->movie_fps = cnt->conf.frame_limit; + else if (cnt->movie_fps > 30) cnt->movie_fps = 30; else if (cnt->movie_fps < 2) cnt->movie_fps = 2; + + cnt->usinterval = 1000000 / cnt->movie_fps; /* less calculations are good... */ } #ifdef HAVE_FFMPEG @@ -768,10 +776,14 @@ EVENT_IMAGE_SNAPSHOT, event_image_snapshot }, + { + EVENT_IMAGEM, + event_vid_putpipe + }, #ifndef WITHOUT_V4L #if (!defined(BSD)) { - EVENT_IMAGE | EVENT_IMAGEM, + EVENT_IMAGE, event_vid_putpipe }, #endif /* BSD */ Index: Makefile.in =================================================================== --- Makefile.in (revision 439) +++ Makefile.in (working copy) @@ -34,8 +34,8 @@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ VIDEO_OBJ = @VIDEO@ -OBJ = motion.o logger.o conf.o draw.o jpegutils.o $(VIDEO_OBJ) netcam.o \ - netcam_ftp.o netcam_jpeg.o netcam_wget.o track.o \ +OBJ = motion.o logger.o conf.o draw.o jpegutils.o vloopback_motion.o $(VIDEO_OBJ) \ + netcam.o netcam_ftp.o netcam_jpeg.o netcam_wget.o track.o \ alg.o event.o picture.o rotate.o webhttpd.o \ stream.o @FFMPEG_OBJ@ SRC = $(OBJ:.o=.c) @@ -128,6 +128,18 @@ dep depend fastdep: $(DEPEND_FILE) ################################################################################ +# CURRENT, BUILD current svn trunk. # +################################################################################ +current: distclean svn autotools all + +svn: + svn update + +autotools: + autoconf + ./configure --with-developer-flags + +################################################################################ # INSTALL installs all relevant files. # ################################################################################ install: Index: motion.c =================================================================== --- motion.c (revision 439) +++ motion.c (working copy) @@ -531,49 +531,48 @@ cnt->imgs.width, t, cnt->conf.text_double); } - /* Output the picture to jpegs and ffmpeg */ - event(cnt, EVENT_IMAGE_DETECTED, - cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, - &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); +//smoothvideo: payne 10/15/2008 +//located in two places + /* process this only if we're outputting to a movie */ + if (cnt->conf.smooth_video && (cnt->ffmpeg_output || cnt->conf.useextpipe)) { + if (cnt->prevtv.tv_sec > 0) { + time_t elapsedus = ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec - cnt->prevtv.tv_sec) * 1000000) + + (cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec - cnt->prevtv.tv_usec); + int myinsertnum = ((elapsedus + cnt->leftovers) / cnt->usinterval); + /* the larger loop we're in is only called to process + * motion -- there will be one frame processed later + * REGARDLESS of what we do here, so, account for it */ + cnt->leftovers += elapsedus - (cnt->usinterval*(myinsertnum+1)); -//#ifdef HAVE_FFMPEG - /* Check if we must add any "filler" frames into movie to keep up fps */ - if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) { - /* movie_last_shoot is -1 when file is created, - * we don't know how many frames there is in first sec */ - if (cnt->movie_last_shot >= 0) { - if (debug_level >= CAMERA_DEBUG) { - int frames = cnt->movie_fps - (cnt->movie_last_shot + 1); - if (frames > 0) { - char tmp[15]; - motion_log(LOG_DEBUG, 0, "%s: Added %d fillerframes into movie", - __FUNCTION__, frames); - sprintf(tmp, "Fillerframes %d", frames); - draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 40, - cnt->imgs.width, tmp, cnt->conf.text_double); + if (debug_level >= 2) + motion_log(LOG_INFO, 0, "current, prev, fps, us, interval, insertnum, leftovers: " + "%d.%06ld, %d.%06ld, %d, %d, %d, %d, %d", + cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec, + cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec, + cnt->prevtv.tv_sec, cnt->prevtv.tv_usec, cnt->movie_fps, elapsedus, + cnt->usinterval, myinsertnum, cnt->leftovers); + + + while (myinsertnum > 0) { + event(cnt, EVENT_FFMPEG_PUT, cnt->previmg, NULL, NULL, + &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); + myinsertnum--; + } } + cnt->prevtv = cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv; + memcpy(cnt->previmg, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, cnt->imgs.size); } - /* Check how many frames it was last sec */ - while ((cnt->movie_last_shot + 1) < cnt->movie_fps) { - /* Add a filler frame into encoder */ + /* Output the picture to jpegs (if motion), ffmpeg and extpipe (regardless)*/ + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION && + ! cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_PRECAP) { + event(cnt, EVENT_IMAGE_DETECTED, + cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, + &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); + } else { event(cnt, EVENT_FFMPEG_PUT, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); - - cnt->movie_last_shot++; - } } - cnt->movie_last_shot = 0; - } else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot != (cnt->movie_last_shot + 1)) { - /* We are out of sync! Properbly we got motion - no motion - motion */ - cnt->movie_last_shot = -1; - } - - /* Save last shot added to movie - * only when we not are within first sec */ - if (cnt->movie_last_shot >= 0) - cnt->movie_last_shot = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot; -//#endif } /* Mark the image as saved */ @@ -674,6 +673,10 @@ cnt->imgs.type = VIDEO_PALETTE_YUV420P; } + /* setup cnt->previmg (previous img for ffmpeg streaming) */ + cnt->previmg = mymalloc(cnt->imgs.size); + memset(cnt->previmg, 0x80, cnt->imgs.size); /* initialize to grey */ + image_ring_resize(cnt, 1); /* Create a initial precapture ring buffer with 1 frame */ cnt->imgs.ref = mymalloc(cnt->imgs.size); @@ -746,7 +749,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, "%s: Failed to open video loopback", __FUNCTION__); + motion_log(LOG_ERR, 0, "%s: Failed to open video loopback for normal pictures", __FUNCTION__); return -1; } } @@ -761,7 +764,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, "%s: Failed to open video loopback", __FUNCTION__); + motion_log(LOG_ERR, 0, "%s: Failed to open video loopback for motion pictures", __FUNCTION__); return -1; } } @@ -1013,6 +1016,12 @@ if (cnt->conf.sqlite3_db) sqlite3_close(cnt->database_sqlite3); #endif /* HAVE_SQLITE3 */ + + /* Cleanup the previmg buffer */ + if (cnt->previmg) { + free(cnt->previmg); + cnt->previmg = NULL; + } } /** @@ -1149,8 +1158,10 @@ image_ring_resize(cnt, frame_buffer_size); /* Get time for current frame */ - cnt->currenttime = time(NULL); + gettimeofday(&cnt->tv, NULL); + cnt->currenttime = cnt->tv.tv_sec; + /* localtime returns static data and is not threadsafe * so we use localtime_r which is reentrant and threadsafe */ @@ -1227,6 +1238,8 @@ /* Store time with pre_captured image */ cnt->current_image->timestamp = cnt->currenttime; + cnt->current_image->tv = cnt->tv; + localtime_r(&cnt->current_image->timestamp, &cnt->current_image->timestamp_tm); /* Store shot number with pre_captured image */ @@ -1426,7 +1439,7 @@ /* Lightswitch feature - has light intensity changed? * 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. + * 15 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 && !cnt->lost_connection) { @@ -1434,8 +1447,8 @@ if (debug_level >= CAMERA_DEBUG) motion_log(-1, 0, "%s: Lightswitch detected", __FUNCTION__); - if (cnt->moved < 5) - cnt->moved = 5; + if (cnt->moved < 15) + cnt->moved = 15; cnt->current_image->diffs = 0; alg_update_reference_frame(cnt, RESET_REF_FRAME); @@ -1656,6 +1669,7 @@ * If post_capture is enabled we also take care of this in the this * code section. */ + if (cnt->conf.emulate_motion && (cnt->startup_frames == 0)) { cnt->detecting_motion = 1; /* Setup the postcap counter */ @@ -1715,9 +1729,8 @@ } /* Update last frame saved time, so we can end event after gap time */ - if (cnt->current_image->flags & IMAGE_SAVE) + if (cnt->current_image->flags & IMAGE_SAVE) cnt->lasttime = cnt->current_image->timestamp; - /* Simple hack to recognize motion in a specific area */ /* Do we need a new coversion specifier as well?? */ @@ -1779,8 +1792,6 @@ __FUNCTION__, cnt->event_nr); cnt->makemovie = 0; - /* Reset post capture */ - cnt->postcap = 0; /* Finally we increase the event number */ cnt->event_nr++; @@ -1790,12 +1801,59 @@ * images get a timestamp from previous event. */ cnt->text_event_string[0] = '\0'; + + /* And, finally again, we reset the prevtv structure. */ + cnt->prevtv.tv_sec = 0; + cnt->prevtv.tv_usec = 0; + + /* Make sure postcap is fulfilled */ + if (cnt->postcap && !cnt->finish) { + cnt->detecting_motion = 1; + cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); + motion_detected(cnt, cnt->video_dev, cnt->current_image); + } else { + /* Reset post capture */ + cnt->postcap = 0; + } } } /* Save/send to movie some images */ process_image_ring(cnt, 2); + /* For movie realtime sync. code needed here so we don't run into issues + with high numbers of queued images, which would slow down processing */ + if (cnt->conf.smooth_video && (cnt->ffmpeg_output || (cnt->conf.useextpipe))) { + /* process this if we're outputting to a movie only.. */ + if (cnt->prevtv.tv_sec > 0) { /* only run through this block of code if we're in an event -- + minimal processing is KEY =) */ + time_t elapsedus = ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec - cnt->prevtv.tv_sec) * 1000000) + + (cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec - cnt->prevtv.tv_usec); + int myinsertnum = ((elapsedus + cnt->leftovers) / cnt->usinterval); + + if (myinsertnum > 0) { + cnt->leftovers += elapsedus - (cnt->usinterval*(myinsertnum)); + + if (debug_level >= 2) + motion_log(LOG_INFO, 0, "KEEPUP: current, prev, fps, us, interval, insertnum, leftovers: " + "%d.%06ld, %d.%06ld, %d, %d, %d, %d, %d", + cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_sec, + cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv.tv_usec, + cnt->prevtv.tv_sec, cnt->prevtv.tv_usec, cnt->movie_fps, elapsedus, + cnt->usinterval, myinsertnum, cnt->leftovers); + + while (myinsertnum > 0) { + event(cnt, EVENT_FFMPEG_PUT, cnt->previmg, NULL, NULL, + &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); + myinsertnum--; + } + + cnt->prevtv = cnt->imgs.image_ring[cnt->imgs.image_ring_out].tv; + memcpy(cnt->previmg, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, cnt->imgs.size); + } + } + } + /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/ /* If CAMERA_VERBOSE enabled output some numbers to console */ @@ -2861,6 +2919,13 @@ pthread_getspecific(tls_key_threadnr)); break; + case 'e': // cameraname + if (cnt->conf.cameraname && cnt->conf.cameraname[0]) + snprintf(tempstr, PATH_MAX, "%s", cnt->conf.cameraname); + else + ++pos_userformat; + break; + case 'C': // text_event if (cnt->text_event_string && cnt->text_event_string[0]) snprintf(tempstr, PATH_MAX, "%s", cnt->text_event_string); @@ -2888,6 +2953,18 @@ ++pos_userformat; break; + case 'u': // tv_usec -- really "us" for micro time, returns microsecond part + switch(*++pos_userformat) { + case 's': // %us, just return microseconds (6 digits padded) + sprintf(tempstr, "%06ld",cnt->current_image->tv.tv_usec); + break; + } + if (tempstr[0]) //if %ut or %us + break; + else + --pos_userformat; + + default: // Any other code is copied with the %-sign *format++ = '%'; *format++ = *pos_userformat; Index: motion-dist.conf.in =================================================================== --- motion-dist.conf.in (revision 439) +++ motion-dist.conf.in (working copy) @@ -300,7 +300,7 @@ ############################################################# # Bool to enable or disable extpipe (default: off) -use_extpipe off +useextpipe off # External program (full path and opts) to pipe raw video to # Generally, use '-' for STDIN... @@ -365,6 +365,13 @@ # a unique identifier for each event. text_event %Y%m%d%H%M%S +# This option defines the value of the special event conversion specifier %e +# You can use any conversion specifier in this option except %e. +# Default: Camera %t +# The idea is that %e can be used filenames and text_left/right for creating +# a unique identifier for each camera. +cameraname Camera %t + # Draw characters at twice normal size on images. (default: off) text_double off Index: motion.h =================================================================== --- motion.h (revision 439) +++ motion.h (working copy) @@ -105,6 +105,8 @@ #define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ #endif +#define DEF_PALETTE 11 + /* Default picture settings */ #define DEF_WIDTH 352 #define DEF_HEIGHT 288 @@ -212,6 +214,7 @@ struct image_data { unsigned char *image; int diffs; + struct timeval tv; time_t timestamp; /* Timestamp when image was captured */ struct tm timestamp_tm; int shot; /* Sub second timestamp count */ @@ -316,6 +319,7 @@ struct config conf; struct images imgs; + unsigned char *previmg; struct trackoptions track; struct netcam_context *netcam; struct image_data *current_image; /* Pointer to a structure where the image, diffs etc is stored */ @@ -355,6 +359,10 @@ struct tm *eventtime_tm; time_t currenttime; + struct timeval tv; /* store current time of image */ + struct timeval prevtv; /* previous pic tv.. needs to stick around */ + struct timeval prevmotiontv; /* previous pic detection tv.. needs to stick around */ + time_t leftovers; /* leftover microseconds */ time_t lasttime; time_t eventtime; time_t connectionlosttime; /* timestamp from connection lost */ @@ -393,6 +401,7 @@ #endif short int movie_fps; + time_t usinterval; /* microsecond interval, 1000000 / movie_fps */ char newfilename[PATH_MAX]; char extpipefilename[PATH_MAX]; short int movie_last_shot; Index: video2.c =================================================================== --- video2.c (revision 439) +++ video2.c (working copy) @@ -62,7 +62,6 @@ #ifdef MOTION_V4L2 #include "motion.h" -#include "netcam.h" #include "video.h" #ifdef MOTION_V4L2_OLD @@ -139,7 +138,7 @@ struct v4l2_requestbuffers req; struct v4l2_buffer buf; - netcam_buff *buffers; + video_buff *buffers; s32 pframe; @@ -273,7 +272,7 @@ if (xioctl(vid_source->fd, VIDIOC_S_STD, &std_id) == -1) motion_log(LOG_ERR, 1, "%s: Error selecting standard method %d VIDIOC_S_STD", - __FUNCTION__, std_id); + __FUNCTION__, (int)std_id); } @@ -375,7 +374,7 @@ vid_source->fmt.fmt.pix.field = V4L2_FIELD_ANY; if (xioctl(vid_source->fd, VIDIOC_TRY_FMT, &vid_source->fmt) != -1 && vid_source->fmt.fmt.pix.pixelformat == pixformat) { - motion_log(LOG_INFO, 0, "%s: index_format %d Test palette %c%c%c%c (%dx%d)", + motion_log(LOG_INFO, 0, "%s: index_format %d Testing palette %c%c%c%c (%dx%d)", __FUNCTION__, index_format, pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, *width, *height); @@ -465,7 +464,7 @@ if (xioctl(vid_source->fd, VIDIOC_REQBUFS, &vid_source->req) == -1) { motion_log(LOG_ERR, 1, "%s: Error requesting buffers %d for memory map. VIDIOC_REQBUFS", - vid_source->req.count ,__FUNCTION__); + __FUNCTION__, vid_source->req.count); return -1; } @@ -473,11 +472,11 @@ if (vid_source->req.count < MIN_MMAP_BUFFERS) { motion_log(LOG_ERR, 1, "%s: Insufficient buffer memory %d < MIN_MMAP_BUFFERS.", - vid_source->req.count, __FUNCTION__); + __FUNCTION__, vid_source->req.count); return -1; } - vid_source->buffers = calloc(vid_source->req.count, sizeof(netcam_buff)); + vid_source->buffers = calloc(vid_source->req.count, sizeof(video_buff)); if (!vid_source->buffers) { motion_log(LOG_ERR, 1, "%s: Out of memory.", __FUNCTION__); @@ -857,20 +856,20 @@ pthread_sigmask(SIG_UNBLOCK, &old, NULL); /*undo the signal blocking */ { - netcam_buff *the_buffer = &vid_source->buffers[vid_source->buf.index]; + video_buff *the_buffer = &vid_source->buffers[vid_source->buf.index]; switch (vid_source->fmt.fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: - conv_rgb24toyuv420p(map, (unsigned char *) the_buffer->ptr, width, height); + conv_rgb24toyuv420p(map, the_buffer->ptr, width, height); return 0; case V4L2_PIX_FMT_UYVY: - conv_uyvyto420p(map, (unsigned char *) the_buffer->ptr, (unsigned)width, (unsigned)height); + conv_uyvyto420p(map, the_buffer->ptr, (unsigned)width, (unsigned)height); return 0; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YUV422P: - conv_yuv422to420p(map, (unsigned char *) the_buffer->ptr, width, height); + conv_yuv422to420p(map, the_buffer->ptr, width, height); return 0; case V4L2_PIX_FMT_YUV420: @@ -880,23 +879,20 @@ case V4L2_PIX_FMT_PJPG: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: - return mjpegtoyuv420p(map, (unsigned char *) the_buffer->ptr, width, height, + return mjpegtoyuv420p(map, the_buffer->ptr, width, height, vid_source->buffers[vid_source->buf.index].content_length); -/* return 0; - case V4L2_PIX_FMT_JPEG: - return conv_jpeg2yuv420(cnt, map, the_buffer, width, height); -*/ + /* FIXME: quick hack to allow work all bayer formats */ case V4L2_PIX_FMT_SBGGR16: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SPCA561: case V4L2_PIX_FMT_SBGGR8: /* bayer */ - bayer2rgb24(cnt->imgs.common_buffer, (unsigned char *) the_buffer->ptr, width, height); + bayer2rgb24(cnt->imgs.common_buffer, the_buffer->ptr, width, height); conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); return 0; case V4L2_PIX_FMT_SN9C10X: - sonix_decompress(map, (unsigned char *) the_buffer->ptr, width, height); + sonix_decompress(map, the_buffer->ptr, width, height); bayer2rgb24(cnt->imgs.common_buffer, map, width, height); conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); return 0; Index: video.c =================================================================== --- video.c (revision 439) +++ video.c (working copy) @@ -13,11 +13,6 @@ #include "rotate.h" /* already includes motion.h */ #include "video.h" -/* for vloopback */ -#include -#include - - static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) { int dev = viddev->fd; @@ -413,217 +408,4 @@ v4l_picture_controls(cnt, viddev); } } - -static int v4l_open_vidpipe(void) -{ - int pipe_fd = -1; - char pipepath[255]; - char buffer[255]; - char *major; - char *minor; - struct utsname uts; - - if (uname(&uts) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to execute uname", __FUNCTION__); - return -1; - } - - major = strtok(uts.release, "."); - minor = strtok(NULL, "."); - - if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) { - motion_log(LOG_ERR, 1, "%s: Unable to decipher OS version", __FUNCTION__); - return -1; - } - - if (strcmp(minor, "5") < 0) { - FILE *vloopbacks; - char *loop; - char *input; - char *istatus; - char *output; - char *ostatus; - - vloopbacks = fopen("/proc/video/vloopback/vloopbacks", "r"); - - if (!vloopbacks) { - motion_log(LOG_ERR, 1, "%s: Failed to open '/proc/video/vloopback/vloopbacks'", - __FUNCTION__); - return -1; - } - - /* Read vloopback version*/ - if (!fgets(buffer, sizeof(buffer), vloopbacks)) { - motion_log(LOG_ERR, 1, "%s: Unable to read vloopback version", __FUNCTION__); - return -1; - } - - fprintf(stderr, "\t%s", buffer); - - /* Read explanation line */ - - if (!fgets(buffer, sizeof(buffer), vloopbacks)) { - motion_log(LOG_ERR, 1, "%s: Unable to read vloopback explanation line", - __FUNCTION__); - return -1; - } - - while (fgets(buffer, sizeof(buffer), vloopbacks)) { - if (strlen(buffer) > 1) { - buffer[strlen(buffer)-1] = 0; - loop = strtok(buffer, "\t"); - input = strtok(NULL, "\t"); - istatus = strtok(NULL, "\t"); - output = strtok(NULL, "\t"); - ostatus = strtok(NULL, "\t"); - - if (istatus[0] == '-') { - snprintf(pipepath, sizeof(pipepath), "/dev/%s", input); - pipe_fd = open(pipepath, O_RDWR); - - if (pipe_fd >= 0) { - motion_log(-1, 0, "%s: \tInput: /dev/%s \tOutput: /dev/%s", - __FUNCTION__, input, output); - break; - } - } - } - } - - fclose(vloopbacks); - } else { - DIR *dir; - struct dirent *dirp; - const char prefix[] = "/sys/class/video4linux/"; - char *ptr, *io; - int fd; - int low = 9999; - int tfd; - int tnum; - - if ((dir = opendir(prefix)) == NULL) { - motion_log(LOG_ERR, 1, "%s: Failed to open '%s'", __FUNCTION__, prefix); - return -1; - } - - while ((dirp = readdir(dir)) != NULL) { - if (!strncmp(dirp->d_name, "video", 5)) { - strncpy(buffer, prefix, sizeof(buffer)); - strncat(buffer, dirp->d_name, sizeof(buffer) - strlen(buffer)); - strncat(buffer, "/name", sizeof(buffer) - strlen(buffer)); - - if ((fd = open(buffer, O_RDONLY)) >= 0) { - if ((read(fd, buffer, sizeof(buffer)-1)) < 0) { - close(fd); - continue; - } - - ptr = strtok(buffer, " "); - - if (strcmp(ptr, "Video")) { - close(fd); - continue; - } - - major = strtok(NULL, " "); - minor = strtok(NULL, " "); - io = strtok(NULL, " \n"); - - if (strcmp(major, "loopback") || strcmp(io, "input")) { - close(fd); - continue; - } - - if ((ptr = strtok(buffer, " ")) == NULL) { - close(fd); - continue; - } - - tnum = atoi(minor); - - if (tnum < low) { - strcpy(buffer, "/dev/"); - strncat(buffer, dirp->d_name, sizeof(buffer) - strlen(buffer)); - if ((tfd = open(buffer, O_RDWR)) >= 0) { - strncpy(pipepath, buffer, sizeof(pipepath)); - - if (pipe_fd >= 0) - close(pipe_fd); - - pipe_fd = tfd; - low = tnum; - } - } - close(fd); - } - } - } - - closedir(dir); - - if (pipe_fd >= 0) - motion_log(-1, 0, "%s: Opened input of %s", __FUNCTION__, pipepath); - } - - return pipe_fd; -} - -static int v4l_startpipe(const char *dev_name, int width, int height, int type) -{ - int dev; - struct video_picture vid_pic; - struct video_window vid_win; - - if (!strcmp(dev_name, "-")) { - dev = v4l_open_vidpipe(); - } else { - dev = open(dev_name, O_RDWR); - } - - if (dev < 0) - return -1; - - if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - return -1; - } - - vid_pic.palette = type; - - if (ioctl(dev, VIDIOCSPICT, &vid_pic) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSPICT)", __FUNCTION__); - return -1; - } - - if (ioctl(dev, VIDIOCGWIN, &vid_win) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGWIN)", __FUNCTION__); - return -1; - } - - vid_win.height = height; - vid_win.width = width; - - if (ioctl(dev, VIDIOCSWIN, &vid_win) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSWIN)", __FUNCTION__); - return -1; - } - - return dev; -} - -static int v4l_putpipe (int dev, unsigned char *image, int size) -{ - return write(dev, image, size); -} - - -int vid_startpipe(const char *dev_name, int width, int height, int type) -{ - return v4l_startpipe(dev_name, width, height, type); -} - -int vid_putpipe (int dev, unsigned char *image, int size) -{ - return v4l_putpipe(dev, image, size); -} #endif /*WITHOUT_V4L*/ Index: video_common.c =================================================================== --- video_common.c (revision 439) +++ video_common.c (working copy) @@ -3,7 +3,7 @@ * Video stream functions for motion. * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) * 2006 by Krzysztof Blaszkowski (kb@sysmikro.com.pl) - * 2007 by Angel Carpinteo (ack@telefonica.net) + * 2007 by Angel Carpintero (ack@telefonica.net) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * @@ -363,35 +363,6 @@ } } -int conv_jpeg2yuv420(struct context *cnt, unsigned char *dst, netcam_buff *buff, int width, int height) -{ - netcam_context netcam; - - if (!buff || !dst) - return 3; - - if (!buff->ptr) - return 2; /* Error decoding MJPEG frame */ - - memset(&netcam, 0, sizeof(netcam)); - netcam.imgcnt_last = 1; - netcam.latest = buff; - netcam.width = width; - netcam.height = height; - netcam.cnt = cnt; - - pthread_mutex_init(&netcam.mutex, NULL); - pthread_cond_init(&netcam.cap_cond, NULL); - pthread_cond_init(&netcam.pic_ready, NULL); - pthread_cond_init(&netcam.exiting, NULL); - - if (setjmp(netcam.setjmp_buffer)) - return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; - - return netcam_proc_jpeg(&netcam, dst); -} - - /* * mjpegtoyuv420p * Index: video.h =================================================================== --- video.h (revision 439) +++ video.h (working copy) @@ -14,6 +14,7 @@ #ifndef WITHOUT_V4L #include #include +#include "vloopback_motion.h" #include "pwc-ioctl.h" #endif @@ -36,6 +37,15 @@ #define VIDEO_DEVICE "/dev/video0" +typedef struct video_image_buff { + unsigned char *ptr; + int content_length; + size_t size; /* total allocated size */ + size_t used; /* bytes already used */ + struct timeval image_time; /* time this image was received */ +} video_buff; + + struct video_dev { struct video_dev *next; int usage_count; @@ -81,7 +91,6 @@ void conv_yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height); void conv_uyvyto420p(unsigned char *map, unsigned char *cap_map, unsigned int width, unsigned int height); void conv_rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height); -int conv_jpeg2yuv420(struct context *cnt, unsigned char *dst, netcam_buff * buff, int width, int height); int sonix_decompress(unsigned char *outp, unsigned char *inp, int width, int height); void bayer2rgb24(unsigned char *dst, unsigned char *src, long int width, long int height); int vid_do_autobright(struct context *cnt, struct video_dev *viddev); @@ -89,8 +98,6 @@ #ifndef WITHOUT_V4L /* video functions, video.c */ -int vid_startpipe(const char *dev_name, int width, int height, int); -int vid_putpipe(int dev, unsigned char *image, int); unsigned char *v4l_start(struct video_dev *viddev, int width, int height, int input, int norm, unsigned long freq, int tuner_number); void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, int input,