diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..4456bf5 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,1839 @@ +3.2.5 Development Released + +snap1 - Fixed misc problems in FreeBSD. (Angel Carpintero) + - Update README.FreeBSD + - Fix problems with tuner_device and frequency, now by default is not + defined to allow use any input without problem. + - Replace strndup() by memcpy() in netcam.c + - Merged configure.in.freebsd with configure.in (configure.in.freebsd + deleted) + - Remove a warning when used --without-bktr + - Remove cpu optimization (is broken) + - Fixed memory leak in ffmpeg code. (Andrew Hamilton) + - Fixed http control of pan and tilt (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x12x22x122649 + - Fixed netcamera bug related to seperating frames in an mjpeg stream. + (Peter Holik). From mailing list 23 Dec 2005. + - Fix related to connecting to the netcam (William Black) + From mailing list 23 Dec 2005. + + +3.2.4 Formal Release. Summary of changes + +Features + - New text option text_event and new conversion specifier %C. Option + text_event defines the value %C which then can be used in filenames and + text_right/text_left. The text_event/%C uses the time stamp for the first + image detected in a new event. Default value is %Y%m%d%H%M%S. %C is an empty + string when no event is in progress (gap period expired). Pre_captured + and minimum_motion_frames images are time stamped before the event happens + so %C in text_left/right does not have any effect on those images. + - Added new option track_auto which is a boolean option (on or off) + with default value off. This enables people to start Motion with auto + tracking enabled. Changing the config value for track_auto and + enabling the auto tracking via the httpd track/auto has the exact same + effect. + - Added 3 new tracking options: track_step_angle_x, track_step_angle_y, + and track_move_wait. The options track_step_angle control the movement + during auto tracking and are currently only active for the pwc type + tracking. The idea is that they can later also be used for the generic + tracking as it evolves. The track_move_wait controls the number of frames + after the camera has moved (auto or manual) during which motion detection + is disabled. This option should be set so low that the motion detection + is re-enabled the minute the camera is standing still again. + - Added new sql_query option. This in combination with convertion + specifiers incl the two new %f and %n enables the user to use any database + structure they please. Adding fields is now a simple matter of modifying + the sql query. + - Added the %t conversion specifier which is the thread (camera) number. + - Added two new conversion specifiers: %f which is filename (full path) + and %n which is filetype (sqltype) valid in on_picture_save, on_movie_start, + on_movie_end and sql_query. This also means that filename is no longer + appended at the end of the 3 on_xxxx commands. + - http control had a number of small improvements. + - Added the debian sub directory so that people can build the deb package. + - Enhanced netcam compatibility with Lumenera and Pixord Cameras. + - Netcam feature now supports both http and ftp. + - Added an infinite retry scheme for netcams that are not available + when Motion is started. Instead of just dying, Motion now retries every + 10 seconds until the netcam is available. Until the netcam is available + Motion enters a mode showing a grey image with a text information which + is fed to webcam, timelapse, snapshots, vloopback etc. If the actual + height and width of the netcam does not match the dimentions in the + config file Motion will perform a quick restart. + - Added a better error handling of a netcam that changes dimensions + while Motion is running. Instead of just writing error messages Motion + restarts quickly to recover from this change. + - FreeBSD Code improvements including set/get hue, saturation, contrast + and brightness, support large resolutions. + - RPM specs file changed as suggested for use in the Livna repository. + - Changed the sequence of events connected with creating files. Data is + now written to the databases (if used) before an external commens is + on (on_xxxx options) allowing the external program to use the new data + in the database. + - Motion is now also works on MaxOSX with similar feature set as FreeBSD. + +Bugfixes + - netcam code now waits for the next frame to arrive for a limited period + in order to avoid too many duplicate images. + - Motion loop resets its frame timer when the image received is from a netcam. + This lowers the actual framerate of Motion to the rate the netcam can actually + keep up with. + - Removed all warnings when running ./configure --with-developer-flags. + - Fixed error message with unknown config option. + - Fixed small mistake in allocating memory for cnt->imgs.common_buffer. + - Implemented a speed-up patch of the draw text feature. + - Introduced check for device image size being a multiple of 16. + - Switchfilter feature repaired. + - Fixed small bug where motion was detected when using a tracking camera and + the camera moved to center position when gap period expires. + - Implemented fix to configure so that LDFLAGS from the environment are used + when making the Makefile. + - Changed configure so that --with-jpeg-mmx is default off as a reaction to + known problems seen when using the jpeg-mmx library. + - The lightswitch and switchfilter features have changed to ensure that both + algoritms work on raw unfiltered motion pixels which they both were designed + for. + - Fixed bug related to init of mutex in netcam code. + - Fixed small bug where the displayed time in the grey error image + shown during start with unavailable netcam could show a garbage value + under rare circumstances. + - Restored the function sigchild_handler so it contains the same code + as before motion-3.2.1_snap9. They is done in an attempt to fix an old + problem with zombie child processes that has shown up again. + - Added a work-around so people in FreeBSD that uses a capture card + where input 1 is not tuner can use motion if frequency is set -1 in + motion.conf or thread#.conf + + +3.2.4 Detailed changes for 3.2.4 +snap1 - Removed all warnings originating from the motion sources when running + ./configure --with-developer-flags. + The modifications were done by the following people: Peter Holik, Bill Brack, + Angel Carpintero and Kenneth Lavrsen. + We now encourage developers to ensure that new code is checked with + --with-developer-flags and code made so that no new warnings shows originating + from the motion sources. + http://www.lavrsen.dk/twiki/bin/view/Motion/ReduceWarningsPatch + - Fixed error message with unknown config option (Bill Brack) + - Fixed small mistake in allocating memory for cnt->imgs.common_buffer + (Angel Carpintero). + - Implemented a speed-up patch of the draw text feature (Peter Holik). + http://www.lavrsen.dk/twiki/bin/view/Motion/DrawTextspeedup + - http control updated: (null) messages replaced by "disabled", last parameter + in conf/list are displayed correctly and only in Main thread. When motion runs + with only one thread, it displays "No threads". (Angel Carpintero) + - Enhanced compatibility with Lumenera (Bill Brack) + - http control: selectbox instead of a textfield for changing boolean configs + (Peter Holik and Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/WebhttpEnhancements. + - Introduced check for device image size being a multiple of 16 (Peter Holik). + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamModulo16Patch + - Added the debian sub directory so that people can build the deb package + (Angel Carpintero). + - Sync configure.in.freebsd (adding support for jpeg-mmx, developer-flags and + some cosmetic changes ) (Angel Carpintero) + - Implemented --with-developer-flags fixes in FreeBSD code (Angel Carpintero). + - Implemented Threadnr in TLS (thread-local storage)patch. It puts the thread + number into TLS and modifies motion_log() so that we do not have to drag the + cnt struct around just to be able to print the thread number in the log and + on the console. (Per Jönsson with additional removal of unuseded cnt by + Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/ThreadNrTlsPatch + - Moved the motion_loop initialization into a new function motion_init + (Bill Brack). + - Removed old unused code related to read mode (not mmap) from V4L devices + (Kenneth Lavrsen). + - In v4l_start change map from unsinged char * to void * to be ANSI C compliant + with mmap (Angel Carpintero) + - http control: Changed disabled to (not defined) when displaying option list + (Angel Carpintero) + - netcam code now waits for the next frame to arrive for a limited period + in order to avoid too many duplicate images (Bill Brack). + - Motion loop resets its frame timer when the image received is from a netcam. + This lowers the actual framerate of Motion to the rate the netcam can actually + keep up with. (Kenneth Lavrsen) + - Last --with-developer-flags warnings eliminated simply by swapping the + order of the #include statements in the sources (Bill Brack and Kenneth Lavrsen). + - FreeBSD Code improvements by Angel Carpintero + - Implemented set/get hue , saturation , contrast and brightness. + - Better support to capture with big resolution ( 640x480 , 768x576 ). + - Update Readme adding information about "how to configure a capture + card and settings" , update packages dependencies . + - Remove support for libjpeg-mmx , motion sefgault ( future fix ). + - Cosmetics changes in configure.in.freebsd ( replace --without-v4l by + without-bktr ). + - Cleanup code and fix warnings. + +snap2 - Simplified rotation code based on the fact that images must have dimentions + that are a multiple of 16 (Per Jönsson) + http://www.lavrsen.dk/twiki/bin/view/Motion/RotateSimplificationPatch + - Switchfilter feature repaired. It was called inside motion_detected() + after overlays on cnt->img.out were added which meant that the feature also + detected all the overlays, smartmasks, fixed mask and text. It is now moved + to the motion_loop right after the lightswitch feature and before any + overlays are added (Kenneth Lavrsen). + - Fixed small bug where motion was detected when using a tracking camera and + the camera moved to center position when gap period expires. The fix includes + gathering the updating of reference frame in one place only in the motion_loop + (Kenneth Lavrsen). + - Implemented the new text option text_event and new conversion specifier %C. + Option text_event defines the value %C which then can be used in filenames + and text_right/text_left. The text_event/%C uses the time stamp for the first + image detected in a new event. Default value is %Y%m%d%H%M%S. %C is an empty + string when no event is in progress (gap period expired). Pre_captured + and minimum_motion_frames images are time stamped before the event happens + so %C in text_left/right does not have any effect on those images (Kenneth + Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/EventConvertionSpecifierDiscussion + - Renamed some variables related to time to be better descriptive of function + and type (Kenneth Lavrsen). + - Added new option 'sql_user_text'. This can be defined with the same + conversion specifiers as text_xxx, on_xxxx and filenames. The SQL field + text_left has been removed and replaced by a field user_text which is + used for storing the interpreted value of sql_user_text (Kenneth Lavrsen) + - Added new SQL field event_time_stamp of the type TIMESTAMP + (Kenneth Lavrsen). + +snap3 - Enhancement to Netcam Code for Connection to Pixord Cameras (Bill Brack). + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamFixPixordBug + - Implemented fix to configure so that LDFLAGS from the environment are used + when making the Makefile (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x09x15x185558 + - Changed configure so that --with-jpeg-mmx is default off as a reaction to + known problems seen when using the jpeg-mmx library (Angel Carpintero). + - RPM specs file changed as suggested for use in the Livna repository. + (Kenneth Lavrsen) + - The lightswitch and switchfilter features have been moved up before the + despeckle features are run. This should ensure that both algoritms work on + raw unfiltered motion pixels which they both were designed for. (Kenneth + Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x10x05x212444 + +snap4 - Integrated NetcamWithFtp patch. To use ftp simply use a URL starting with + ftp:// (Bill Brack). Code was additionally cleaned up by Kenneth Lavrsen. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamWithFTP + - Changed error handling in vid_start so that failing to open the video + device no longer causes an exit but a return with error code -1. (Kenneth + Lavrsen) + - Added the %t conversion specifier to show the thread number. (Angel + Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/ThreadConversionSpecifierPatch + - Added help texts in conf.c and motion-dist.conf describing the %t + specifier. Added a good example of use in motion-dist.conf. (Kenneth + Lavrsen). + - Fixed bug related to init of mutex in netcam code (Angel Carpintero). + - Improved fix for netcam mutex init (Bill Brack). + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamFixPthreadInit + - Netcam_ftp code fixes (Angel Carpintero and Asbjørn Pettersen) + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamWithFtpEnhancements + - Enhanced ffmpeg detection (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/BetterFFmpegDetection + - Added two new conversion specifiers: %f which is filename (full path) + and %n which is filetype (sqltype) valid in on_picture_save, on_movie_start, + on_movie_end and sql_query. This also means that filename is no longer + appended at the end of the 3 on_xxxx commands. (Kenneth Lavrsen) + - Removed the sql_user_text option that was added in snap 2 (Kenneth + Lavrsen) + - Added new sql_query option. This in combination with convertion + specifiers incl the two new %f and %n enables the user to use any database + structure they please. Added fields is now a simple matter of modifying + the sql query. The default is the same as the default in snap1. + (Kenneth Lavrsen). + - Changed the sequence of events connected with creating files. Data is + now written to the databases (if used) before an external commens is + on (on_xxxx options) allowing the external program to use the new data + in the database (Kenneth Lavrsen). + - Added an infinite retry scheme for netcams that are not available + when Motion is started. Instead of just dying, Motion now retries every + 10 seconds until the netcam is available. Until the netcam is available + Motion enters the normal flow with the same grey image with a text + information being fed to webcam, timelapse, snapshots, vloopback etc. + Motion uses the width and height from the config file for this. It is + a good idea to setup width and height so it is the same as the netcam. + If the dimentions are the same Motion will switch over to the netcam + seemlessly. If the dimensions are different Motion will perform a quick + restart so all the many internal buffers can be initialized properly + (Kenneth Lavrsen). + - Added a better error handling of a netcam that changes dimensions + while Motion is running. Instead of just writing error messages Motion + restarts quickly to recover from this change. Note the now more well + defined error coding for vid_next for both netcams and V4L cams. + (Kenneth Lavrsen) + +snap5 - Fixed small bug where the displayed time in the grey error image + shown during start with unavailable netcam could show a garbage value + under rare circumstances. (Kenneth Lavrsen). + - Restored the function sigchild_handler so it contains the same code + as before motion-3.2.1_snap9. They is done in an attempt to fix an old + problem with zombie child processes that has shown up again. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x11x13x115016 + (Kenneth Lavrsen). + - Move the declaration of sig_handler_action and sigchild_action from + the setup_signals function where they are local and will be destroyed + and out in main just before setup_signals is called. Changed the + function setup_signals so the two structs are passed as pointers. + (Kenneth Lavrsen) + +Release - Added new option track_auto which is a boolean option (on or off) + with default value off. This enable people to start Motion with auto + tracking enabled. Changing the config value for track_auto and + enabling the auto tracking via the httpd track/auto has the exact same + effect. (Kenneth Lavrsen) + - Added 3 new tracking options: track_step_angle_x, track_step_angle_y, + and track_move_wait. The options track_step_angle control the movement + during auto tracking and are currently only active for the pwc type + tracking. The idea is that they can later also be used for the generic + tracking as it evolves. The track_move_wait controls the number of frames + after the camera has moved (auto or manual) during which motion detection + is disabled. This option should be set so low that the motion detection + is re-enabled the minute the camera is standing still again. Feature + originally made by Moshe Van Der Sterre. Kenneth Lavrsen extended it to + be more generic. + http://www.lavrsen.dk/twiki/bin/view/Motion/PwcConfiguration + - New Feature: Motion is now also supported on MaxOSX with similar + feature set as for Free BSD. See README.MacOSX for details how to install + it. (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/MacOSXPatch + - Added a work-around so people in FreeBSD that uses a capture card + where input 1 is not tuner can use motion if frequency is set -1 in + motion.conf or thread#.conf (Angel Carpintero). + + +3.2.3 Detailed changes for 3.2.3 + Bugfix release only. No new features. + - Fixed a bug in the http control code that failed to accept a client + connecting in some systems (Peter Holik). + - Fixed a series of bugs where several feature were using the image buffer + after text was added for noise tuning, auto_brightness, reference frame + update when tracking etc. When a netcam failed to produce an image the + text added to the previous image became motion detected also. The code is + not changed so that the ring buffer is used for timestamped images + and the image used for detection is in a buffer cnt->imgs.image_virgin. + (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x08x15x140701 + - Auto brightness used the first image in ring buffer instead of the + latest image and it used an image with time stamping. It now uses the new + cnt->imgs.image_virgin buffer. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x08x15x160208 + - Cleaned out unused code from httpd control (Angel Carpintero). + - Option switch_filter used print_int instead of print_bool when motion.conf + was saved (Kenneth Lavrsen). + + +3.2.2 Formal Release. Summary of changes + +Features + - New completely rewritten netcam code. + - Proxy servers are again supported by netcam feature + - New conversion specifier %o for threshold + - New convertion specifier %Q for number of labels + - Drawing of mask and smartmask in setup mode improved + - Compilation of motion on 64 bit machines improved + - RPMs can now be built by non-root user + - Improved the labelling algoritm so that locate feature and tracking features + includes all labelled areas above threshold + - Motion now supports the mjpeg webcam stream while saving PPM images. + - New improved webcam feature. When you set webcam_motion on Motion will now + stream at 1 fps instead of none. When motion is detected the webcam stream + increases to the limit set in the config file. This change makes the + webcam_motion much more interesting. The previous function always ended up + with clients timing out. + - Implemented the libjpeg-mmx patch. Installing the MMX version of libjpeg + can increase performance. Especially for machines with very little CPU power. + It only modifies the configure script. If you do not have the libjpeg-mmx + the configure script with ignore this and use the standard libjpeg. + Note that RPMS will be built without this + - Improved descriptions in motion.conf + - Many small code speed optimizations. + - Added new feature: Double size text. A new config option 'text_double' can + be set 'on' and this scales the text to double size. Default is off. + - Improved error handling of missing picture frames from camera. Especially + network cameras will often not be able to provide a picture frame from time + to time. Motion would retry before and eventually and rather quickly exit + the camera thread and maybe completely exit. The improved handling now + makes a copy of the previous frame for 30 seconds (longer if cpu_low is + activated because the implementation is 30 X framerate frames) and then + show a grey image with a message saying the connection is lost and an ISO + format time stamp of first poor connection. + - Added a configure option --with-developer-flags which enables many compiler + warnings that can be used by developers to make code more robust. Not + for normal users building Motion. + - Included a CODE_STANDARD text file to help new developers make patches + that are easier to integrate without too much manual editing. + +Bug Fixes + - Fixed a bug in the autobrightness algoritm + - Fixed bug reporting errors when creating symlink to last snap + - Improved code so that Motion no longer uses the tmpfile() function for + buffering the frames of the mjpeg stream. + - Implemented a fix/work around to a bug related to building and installing + RPMs on Suse. + - Improved signal handler + - Code commented much more. + - Many bugfixes to get Motion more stable (less seg faults) + - Code improved to remove all warnings when compiled with gcc 4.0 + - Better FreeBSD support + - Replace functions not threadsafe with threadsafe functions. + - Implemented a much easier to use motion_log function which replaces the + calls to prinf and syslog. This code change as no impact to the user. + - Fixed a bug in video.c so that VIDEO_PALETTE_GREY cameras now actually work. + - Updated the ffmpeg.c code so that Motion can now be built with ffmpeg CVS + release from the June/July 2005 timeframe. + + + +3.2.2 Detailed changes for 3.2.2 +snap1 - Pthread deadlock in motion 3.2.1 fixed (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x26x125712 + - http lockup bugfixes and ConvertSignalToSigaction only for webhttpd + (Angel Carpintero) + - alg_draw_location: Use temporary variables to store the values used in + for() loops instead of compute them in each loop (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/ImproveAlgDrawLocation + - Small speed boost to the function draw_textn (Andrew Hamilton and + Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/DrawTextnImprovement + - Added two new convertion specifiers: %o for threshold and %Q for number + of labels. (Kenneth Lavrsen) + - Improved the config file description for pre_capture to get people to + use small values (Kenneth Lavrsen). + +snap2 - Avoid Cleanup Segfault. Avoid Cleanup Segfault. Allocates filepath using + strdup to avoid segfault is target_dir parameter is not supplied in + motion.conf. Moves out from signal handler the cleanup for pipe and mpipe. + (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/AvoidCleanupSegfault + - Major code cleanup concerning signedness of chars all over the code to + allow compilation with gcc4.0 (like in Fedora Core 4) without any + errors or warnings. This will probably require that some of the not yet + included patches will have to be fixed because it it code all over the + place that has been changed. (Kenneth Lavrsen) + +snap3 - Changed the order of drawing the red mask in setup mode so that the + smartmask is drawn after the fixed mask (Joerg Weber). + - Changed the configure script so that /usr/lib64 is also searched for + the presense of ffmpeg (should fix the problem with 64 bit machines) + (Kenneth Lavrsen). + - Changed the configure script so that rpms can be made by normal non-root + users (Angel Carpintero, Kenneth Lavrsen). + +snap4 - Fixed the ffmpeg code so that Motion also compiles against libavcodec + build 4754 or later. (Per Jönsson) + - Above change in configure script for 64 bit ffmpeg support also implemented + in the freeBSD configure (Angel Carpintero) + - Webhttp control interface fixed so it also works in FreeBSD (Angel + Carpintero) + - Improved the display of fixed mask. It is now shown as grey instead of + red. This makes it easier to see the smart mask working when you also have + a fixed mask (Joerg Weber). + - Netcam First Header patch. If an error with jpeg decompression occured at + connecting to a mjpeg streaming webcam, this patch skips this jpeg and tries + to decompress next jpeg up to MAX_HEADER_RETRIES (20) (Peter Holik). + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamFirstHeader + +snap5 - Small improvement in framerate accuracy (Peter Holik). + http://www.lavrsen.dk/twiki/bin/view/Motion/FramerateAdjust + - Fixed a bug in the autobrightness algoritm (Per Johnsson) + - Fixed a bug in the webhttpd code related to pan/tilt. Bug was introduced in + snap4 (Angel Carpintero, Kenneth Lavrsen). + - Improved the labelling algoritm so that locate feature and tracking features + includes all labelled areas above threshold (Joerg Weber). + http://www.lavrsen.dk/twiki/bin/view/Motion/ImprovedLabellingPatch + - Fixed bug reporting errors when creating symlink to last snap (Bill Maidment) + - Changed all use of localtime to localtime_r which is threadsafe + (Kenneth Lavrsen). + - Implemented a modified version of the WebcamCompressInMemory so that Motion + no longer uses the tmpfile() function for buffering the frames of the mjpeg + stream (Peter Holik). + http://www.lavrsen.dk/twiki/bin/view/Motion/WebcamCompressInMemory + - Modified the WebcamCompressInMemory patch so that Motion now supports the + mjpeg webcam stream while being setup for saving PPM images (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/WebcamCompressInMemory + - Major clean-up of code in picture.c and webcam.c so that function names and + variable names are less confusing. Also added many comments in picture.c. + (Kenneth Lavrsen). + +snap6 - Webcam code commented more (Kenneth Lavrsen) + - New improved webcam feature. When you set webcam_motion on Motion will now + stream at 1 fps instead of none. When motion is detected the webcam stream + increases to the limit set in the config file. This change makes the + webcam_motion much more interesting. The previous function always ended up + with clients timing out. (Kenneth Lavrsen). + +snap7 - Implemented WebcamShortWriteHandling patch (Bill Brack) + http://www.lavrsen.dk/twiki/bin/view/Motion/WebcamShortWriteHandlingPatch + - Implemented the libjpeg-mmx patch. Installing the MMX version of libjpeg + can increase performance. Especially for machines with very little CPU power. + It only modifies the configure script. If you do not have the libjpeg-mmx + the configure script with ignore this and use the standard libjpeg. + Note that RPMS will be built without this (Peter Holik and Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/LibJpegMmx + +snap8 - Small code cleanup in webcam.c and picture.c and .h for the webcam code + (Peter Holik and Kenneth Lavrsen) + - Small code cleanup in motion.c for the variable holding the number of + microseconds since epoch. The old code worked fine but relied on an integer + overflow every 71 minutes. (Bill Brack and Kenneth Lavrsen) + - Implemented a fix/work around to a bug related to building and installing + RPMs on Suse. (Paul Beltrani) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x07x14x212356 + - Small speed optimization in the creation of reference frame (Peter Holik). + - Complete rewrite of the Netcam code. Should fix many of the reported and + still open netcam bugs. This is first release in a snapshot. Expect to find + bugs. Testing is important. If you have a netcam please test this and report + bugs. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamCodeRewritePatch + +snap9 - Fixed bug related to disabled webcam or duplicate webcam port. Error log + accept(): Socket operation on non-socket continuously written to syslog. + (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x08x01x150922 + - Fixed memory leak in webhttpd related to use of strdup (Angel Carpintero). + - Improved the error reporting in the Netcam code and did a few minor + corrections and code cleansups (Bill Brack). + - Implemented a much easier to use motion_log function which replaces the + calls to prinf and syslog. The implementation to actually use this has been + implemented in video.c and the Netcam code files. Rest will be in next snap. + This code change as no impact to the user (Bill Brack). + http://www.lavrsen.dk/twiki/bin/view/Motion/ErrorLoggingEnhancementPatch + - Fixed a bug in video.c so that VIDEO_PALETTE_GREY cameras now actually work + (Bill Brack). + - Implemented the conversion of signal to sigaction which should be more + thread safe. Hopefully this still keeps Motion from making Zombies. + (Christophe Grenier). + http://www.lavrsen.dk/twiki/bin/view/Motion/ConvertSignalToSigaction + - Added new feature: Double size text. A new config option 'text_double' can + be set 'on' and this scales the text to double size. Default is off. + (Andrew Hamilton). + http://www.lavrsen.dk/twiki/bin/view/Motion/TextScalingPatch + +snap10 - Error Logging Enhancement Patch v 1.3 (Angel Carpintero) including: + - Populate the motion_log to the whole motion source code. + - Fixed FreeBSD copilation. + - Added the posiblity to pass NULL as struct context * + - Removed unused errno variables. + - Fixed errno in rotate.c , set to 0. + - Fixed some errno flags in webhttpd.c and motion.c + - Fixed a bug when not motion.conf is found + - Removed printf from all files + - Fixed the conf_list[] index in motion.c + http://www.lavrsen.dk/twiki/bin/view/Motion/ErrorLoggingEnhancementPatch + - RotateBswapFix Patch v 2 (Per Jönsson) including: + - cleanup in code comments + - fix for __bswap_32 macro collision + - fixed bug where initialization would be incomplete for invalid degrees + of rotation + - now uses motion_log for error reporting + http://www.lavrsen.dk/twiki/bin/view/Motion/RotateBswapFix + - Re-mplementation of optional Proxy Server for Network Cameras (Bill Brack). + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamProxyServerPatch + - Included a CODE_STANDARD text file to help new developers make patches + that are easier to integrate without too much manual editing. (Kenneth + Lavrsen) + - Added the missing rotate feature in the new netcam code (Bill Brack) + +snap11 - Updated the ffmpeg.c code so that Motion can now be built with ffmpeg CVS + release from the June/July 2005 timeframe (Per Jönsson). + http://www.lavrsen.dk/twiki/bin/view/Motion/FfmpegCodecPatch + - Improved error handling of missing picture frames from camera. Especially + network cameras will often not be able to provide a picture frame from time + to time. Motion would retry before and eventually and rather quickly exit + the camera thread and maybe completely exit. The improved handling now + makes a copy of the previous frame for 5 seconds (longer if cpu_low is + activated because the implementation is 5 X framerate frames) and then + show a grey image with a message saying the connection is lost and an ISO + format time stamp of first poor connection. (Kenneth Lavrsen). + - Implemented version 2 of the NetcamErrorImprovementPatch which should + work with the improved error handler. Changes include: + - Changes handling of non-streaming camera to include a separate thread. + - Changes the value returned from netcam_next to the motion main loop to + indicate the status of the image returned. + - Many changes to the comments, and some enhancement to the logic, to begin + the implementation of points agreed on the NetcamRetryErrorDiscussion page. + - Implements the triple-buffering scheme proposed by PeterHolik (but not the + "Without Locking" portion of his proposal). + Version 2 however does not seems to recover when an mjpeg stream resumes + after a period of not being available. (Bill Brack) + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamErrorImprovementPatch + - Note: Snap11 release as a developer sync release. Bug reports welcome. + FreeBSD code changes not tested yet. + +Release - Netcam error handling improvements and cleanup from Walgrind analysis + (Bill Brack). + - Added a configure option --with-developer-flags which enables many compiler + warnings that can be used by developers to make code more robust. Not + for normal users building Motion (Bill Brack) + - http-control: Fixed segfault when motion is restarted from command line + ( kill -s 1 pid_motion ). Improved control code so Motion can Restart and + Finish 'smoothly'. (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Changed the 5 second missed camera signal timeout to 30 seconds. (Kenneth + Lavrsen) + - Fixed bug where an extra jpeg is saved if you have output_normal=best + and you stop motion after an event has ended. (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x08x05x173526 + + + +3.2.1 Detailed changes for 3.2.1 since 3.1.19_snap3 +snap1 - Major new feature. XMLRPC is replaced by a simpler http remote control + interface (implemented by Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + +snap2 - Fixed netcam->userpass problem (Angel Carpintero) + - Added support in configure for athlon64 from + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x30x190907 + (Angel Carpintero and William M Brack) + - Fixed some gcc warnings (William M Brack) + - Code cleanup from a valgrind analysis (William M. Brack). + +snap3 - Added Best Preview Patch (Joerg Weber) + http://www.lavrsen.dk/twiki/bin/view/Motion/BestPreviewShot + +snap4 - Fix for tracking control with http control (Angel Carpintero) + - Added the new feature Setup Mode (Joerg Weber). This also enables + much more error messages given to the console when in non-daemon mode + while still preserving the messages in syslog which are important + for daemon mode debugging. + The patch is still being worked on and is not finished. + Changes in the FreeBSD code are not yet tested. + http://www.lavrsen.dk/twiki/bin/view/Motion/SetupModePatch + Remove most command line options and replace them by an option to specify + location to motion.conf and a few options related to setting up motion. + (Joerg Weber). This is also included in SetupModePatch. + - Small improvement of the http control interface (link to setting itself + on the html response when setting parameter) (Kenneth Lavrsen) + +snap5 - Fixed a bug in noise tune which was most visible at very low light. + (Joerg Weber and Kenneth Lavrsen) + - Further improvement in the setup mode. Messages are now prefixed by + the thread number in [brackets]. Moved 2 funtions from motion.c to + picture.c. The setup mode patch is now considered finished. (Joerg Weber) + +snap6 Netcam fixes and debug code by Christopher Price + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + snap5_post1_video.c and snap5-post1 patches + - Fixed netcam startup race condition. + - Refactored image handling back to single unified function + - Refactored reconnection algorithm + - Jpeg only based connections should now use less cpu time + - Temporarily removed support for devices that do not support content-length + (in progress) + - Synced syslog/printf style to new motion standard + - Added developer debug trace defines/code + - Defines now used for many constants + +snap7 - Improved console output in setup mode. Now also outputs threshold. + (Joerg Weber) + - Added some additional text to the motion http messages to the terminal + so that you know where the messages come from. (Kenneth Lavrsen) + Netcam Stability Patch version snap6-post1 (Christopher Price) + - Added support for netcams without content-length header (streaming only) + - Remove memmem from netcam_wget.[c|h] (no longer used) + - Several miscellaneous code cosmetic changes + - TODO: remove tests for memmem from configure + +snap8 - Added support for non-streaming (image based) netcams without content-length + header. (3.2.1-snap7-post1 version of the Netcam Stabilty Patch by Christopher + Price). + - Improvement in the noise-tune algoritm (Joerg Weber) + - Re-arranged many of the const char declarations so that they are always + before any statements within a block { }. This is to avoid compiler errors + with older but still used gcc versions such as 2.95. (Kenneth Lavrsen) + - Changed the use of %zd to %llu in printf statements of size_t types. + This is done to avoid compiler errors with older but still used gcc versions + such as 2.95. (Kenneth Lavrsen) + +snap9 - Fixed even more gcc 2.95 compiler errors (declarations not at beginning + of block) (Kenneth Lavrsen). + - Removed a gcc 2.95 compiler warning (netcam.c:1036: warning: variable `pic' + might be clobbered by `longjmp' or `vfork') (Kenneth Lavrsen) + - The values for cnt->locate and cnt->new_img are now #defines in motion.h + to enhance code readability (Kenneth Lavrsen). + - The setting of sql_mask is now only done once per second to save CPU power + (Kenneth Lavrsen) + - Adding checking for conflict between control port and webcam port. Webcam + port for a thread is disabled if it is set to the same value as the control + port (Kenneth Lavrsen). + - Fixed some file descriptor leaks in webcam.c and netcam.c (Christophe + Grenier) + - Added "motion-http:" prefix to error messages from the http control thread. + (Kenneth Lavrsen) + - Added additional error information when connection to MySQL fails (Kenneth + Lavrsen) + - Initiate cnt->event_nr to 1 to avoid code related to end of events and long + mpeg films to be run during startup of Motion. (Kenneth Lavrsen) + - Added new function in event.c close_anything_open() which is called from + send_sms, send_mail and exec_command in order to prevent file descriptor and + open sockets to be inherited by the shell causing freezing and instability. + Code contributed by Christophe Grenier, Christopher Price and Kenneth Lavrsen. + - Added new context global cnt_list.control_socket_server set by the httpd + thread so that the above mentioned close_anything_open() can close open + control sockets (Kenneth Lavrsen). + - Renamed the top level global context structure to cnt_list so it can be + reached from child threads and by above mentioned close_anything_open() + (Christophe Grenier). + +snap10 - Fixed a problem when compiling with --without-v4l configuration. + (Philip Marien) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x03x27x112843 + - Threw away the file descripter leak fix from snap 9 because it caused + more trouble than it fixed. Removed the close_anything_open() and the + cnt_list.control_socket_server field. Replaced it all with a simple + piece of code that all server daemons call when started: setsid() followed + by for (i=getdtablesize(); i>2; --i) close(i). Dirty and simple. + (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x03x21x070534 + - Fixed a bug where rate of fetching picture frames was disturned by + the signal SIG_CHLD from exec_command programs terminating. The symptom + was that the number of post_capture frames became inaccurate and motion + in mpegs did not have constant time between frames. (Kenneth Lavrsen) + - Fixed a bug where motion did not work with gap=1 (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x30x073616 + - Added the feature gap=0 which now also works. It disables gap completely + so that one single mpeg file is created. You can end the event from the + remote control interface make movie feature using for example cron. + This makes Motion close the mpeg and make a new with event number increased + by one. (Kenneth Lavrsen) + - Improved the http remote control action features so that makemovie + and snapshot for thread 0 (all) works on all threads instead of being + ignored (Kenneth Lavrsen). + - Moved some code in the beginning of the motion_loop to a position later + to improve the accuracy of time calculations for the framerate (Kenneth + Lavrsen) + - Updated code so Motion again runs on FreeBSD (Angel Carpintero). + - Removed chech for memmem from configure (Angel Carpintero). + - Updated http control interface so that an additional check is done + before saving config files (Angel Carpintero). + - Fixed a problem with URLs http://192.168.1.3:8080/0 which did not + work without a trailing space (Angel Carpintero). + +snap11 - Implemented new Generic onxxxx features. + Function --- Old Option --- New Option + Start of event (first motion) --- execute --- on_event_start + End of event (no motion for gap seconds) --- New! --- on_event_end + Picture saved (jpg or ppm) --- onsave --- on_picture_save + Movie starts (mpeg file opened) --- onmpeg --- on_movie_start + Movie ends (mpeg file closed) --- onffmpegclose --- on_movie_end + Motion detected --- New! --- on_motion_detected + http://www.lavrsen.dk/twiki/bin/view/Motion/OnXxxCommandsPatch and + http://www.lavrsen.dk/twiki/bin/view/Motion/OnXxxxFeatureDiscussion + (Joerg Weber) + - More Netcam Stability Fixes (snap10-post1-6) (Christopher Price) + - Destroy mutexes in netcam_cleanup(). + - Add reconnection for netcam_start() - this may block other cameras + from starting up!. + - Added additional defines for reconnect retries. + - Change reconnection timeouts to 60 seconds. + - Reworked close(sock) in netcam_connect, to insure future changes + won't forget to close the socket. + - Reworked reconnection for netcam_start() - disabled by default, see + source for INIT_RECONNECT_RETRIES. + - Break some long lines in code. + - Replaced sleep with nanosleep per suggestion by Kenneth Lavrsen. + - Added additional header validation check. + - Changed a couple fd references to use RBUF_FD. + - Added error message if jpeglib error occurs. + - Removed additional header validation check. + - Limited times headers will be checked. + - Removed mutex lock around netcam_start() in video.c, hopefully race + conditions are fixed. + - Added additional headers in http request. + - Added back header validation (should fix netcam_read_header lockups). + - Detect when there is no data on socket in netcam_read_ functions + (should fix netcam_read_image_contentlength() and + netcam_read_image_no_contentlength() lockups). + - Rearranged timeout assignments for pthread_cond_timedwait() calls. + - Adjusted TIMEOUT_COND_WHICH to 4 seconds. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + - Improvements of motion.conf help comments including improvements in new + onxxxx options. (Kenneth Lavrsen) + +snap12 - Fixed a bug in the rgb2yuv420p function. (Daniel Ladd) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x03x30x011107 + - Fixed a bug of locate feature for movement images combined with the + new output_normal best feature (Joerg Weber) + - More Netcam Stability Fixes (snap11-post1-4) (Christopher Price) + - Reworked thread signal/wait conditions, should fix some race conditions. + - Use gettimeofday() to determine thread timeouts, results in better accuracy. + - Adjusted condition timeouts to smaller values due to usage of gettimeofday() + and rework of thread signal/wait conditions. + - Adjusted reconnection retries to 60 (every minute for an hour). + - Fix bug where motion will not quit if requested when reconnecting. + - Cruft, feature creep and redudant code removed. + - Consolated reconnection capability to unified netcam_reconnect function. + - Rework netcam_start logic, minimize startup variables. + - Rework netcam_stream_read and netcam_single_read logic. + - Minor changes to netcam_next logic. + - Fix bug in streaming camera without content-length, recent mod broke. + - Fix bug in startup of single image reads without content-length. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + - Motion Guide refactored completely for 3.2.1 with better web navigation and + auto generation of pages. Makefile updated so that the Motion TWiki topic + MotionGuideOneLargeDocument is fetched when updating the guide and making + releases. (Kenneth Lavrsen). + +snap13 - Removed the debug_parameter option which had no use. Programmers can still + use it because the code is only commented out. This change required a small + update in the code that rewrites motion.conf so that a remote control command + to write the config files still adds a text header for the thread section at + the end of motion.conf (Kenneth Lavrsen). + - Changed the default values for a few options: quiet on, webcam_maxrate 1, + threshold_tune off, webcam_quality 50 (Kenneth Lavrsen). + - Changed some cosmetics in the way motion.conf is written (space after #) + (Kenneth Lavrsen). + - Updated the motion-dist.conf to use default values unless there is a reason + not to (Kenneth Lavrsen). + - Fix the compile issue with official ffmpeg packages from debian (Angel + Carpintero). + - More Netcam Stability Fixes (snap12-post1) (Christopher Price) + - Newrote url parser, better syntax checking and error handling of urls. + - Userpass now allowed in url (http://user:pass@example.com/). + Netcam_userpass has precedence, it will override a userpass embedded in + the url. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + +snap14 - Added basic authentication to the http control interface introducing new + config option control_authentication. (Angel Carpintero) + - Fixed memory leak when restarting Motion from http control (Angel + Carpintero). + - Small improvement in configure script for Debian (Angel Carpintero) + - Added the ability to clear an option to off (bool), 0 (int) or undefined + (string) by submitting blank entry field in the http control interface. + (Angel Carpintero). + +snap15 - Added new feature which shows the fixed mask (in addition to the smart mask) + in bright red on the Motion type images (Joerg Weber). + http://www.lavrsen.dk/twiki/bin/view/Motion/FixedMaskFileOnMotionImagesPatch + - Added new feature. When you specify a mask file in the config file and start + Motion, and the mask file does not exist, Motion will create a new clear + (white) mask file for you in the right size. Then it is easy to simply + open the file in your favorite paint program and add the masking in black + (Joerg Weber). + http://www.lavrsen.dk/twiki/bin/view/Motion/FixedMaskFileOnMotionImagesPatch + - Fixed a bug in the low_cpu feature where cpu load increased instead of + decreasing because the framerate calculations were completely wrong. This was + an old bug introduced in 3.0.1 (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x04x24x205933 + - Improved the auto-brightness algoritm. When auto-brightness is enabled + the brightness option becomes a target value for the brightness level. + This should also close a bug report (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x02x26x195358 + - http interface small fixes (motion-3.2.1_snap14-small-fixes 1.1) incl + Add 'back' link to response_client errors (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Started adding tuner_number as option. This is not fully implemented. First + code is added and rest will be done in next snap. (Kenneth Lavrsen) + +snap16 - Made the http control interface more RFC compliant (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x02x180550 + - Made the http control HTML responses nicer to look at as sources and + therefore easier to debug errors (Kenneth Lavrsen). + - Code style cleanup of webhttpd.c (Kenneth Lavrsen). + - Fixed compatibility problem with Palantir. Fixed by making output more + compatible with RFC (\r\n). Original fixes by Roberto Spadim and Angel + Carpintero. However this fix made Firefox flicker even more than it normally + does. Final fix which works in both Palantir client, Firefox and Cambozola + was made by Kenneth Lavrsen. This closes the following bugs: + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x02x205307, + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x07x042849 + +snap17 - Fixed small bug when pre_capture buffer is resized during operation. + (Joerg Weber). + - In httpd control code: Fixed RAW syntax following API specs. (Angel + Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Added new conversion specifiers: %D (diffs), (noise) %K (motion center x), + %L (motion center y), %i (locate width x) and %J (locate width y). These + changes also required a refactoring of the alg_locate code. This change + is part of the implementation of a generic tracking feature and it enables + implementing external programs that can perform sinple prediction features. + (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/ExtendReplaceConversionSpecifiersDiscussion + http://www.lavrsen.dk/twiki/bin/view/Motion/GenericTrackingPatch + - Fixed a bug in switchfilter which caused motion detection to not work + when the feature was enabled (Kenneth Lavrsen). + +Release - Change the working directory to / in daemon mode. This way you don't have + to kill motion to umount the partition from where you start it. (Christophe + Grenier) + http://www.lavrsen.dk/twiki/bin/view/Motion/ChdirNetCamWgetPatch + - In netcam-wget header_get() didn't always in add a \0 string terminator. + This was fixed by Christophe Grenier + http://www.lavrsen.dk/twiki/bin/view/Motion/ChdirNetCamWgetPatch + - Fix for Unknown content type with lumenera cameras (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x06x174416 + - MotionHttpControl Patch motion-3.2.1_snap18-pre1 v,1.0 19 May 2005. + Fixed some HTTP response codes and added header copyrights. (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Implemented pthread fix by Christophe Grenier. + http://www.lavrsen.dk/twiki/bin/view/Motion/PthreadFixPatch + - Fixed problem compiling "ffmpeg reports only YUV420 is supported" when + ffmpeg is a recent CVS version. (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x22x213229 + - Man page updated. It is now semi-autogenerated in the Motion TWiki + (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionOptionsAlphabeticalManpage + - Bug fix in netcam code: Sometimes motion try to free an invalid memory area + (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x21x105335 + - Small configure fix related to --without-v4l (Angel Carpintero) + - Fixes for http control HTML code (Angel Carpintero) + - Added init script to RPM (Angel Carpintero) + + +3.1.19 Detailed changes for 3.1.19 snapshot releases since 3.1.18 +snap1 - Fixed bug which caused Motion 3.1.18 fail to save timelapse mpegs when + setting ffmpeg_timelapse = 1 (fixed by Michael Reuschling) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x31x211756 + - Fixed several bugs in new netcam code introduced in 3.1.18 + (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x16x030209 + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x02x01x071546 + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x02x03x035918 + - Added patch that enables Motion to work with vloopback version 0.94 + and kernel 2.6.10+. (patch by William M Brack). + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionAndVloopbackVideoDotCPatch + +snap2 Following bugfixes all by Angel Carpintero + - Netcam code: Change printf() to fprintf(). + - Netcam code: Cleanup memory netcam (netcam.c , motion.c ). + - Netcam code: Redesign of reconnection feature. + - Configure: Added debug , conditional compile of xmlrpc-c + - Fix a non allocated pointer to be freed. + - Added fix to BugReport2005x02x11x170019 + - Added fix to BugReport2005x02x11x150802 + +snap3 Bugfixes by Angel Carpintero + - fix motion.spec, motion.spec.in + - typo in configure.in , configure.in.freebsd + - fix version number + + +3.1.18 Formal Release - Summary of changes since 3.1.17. + - Removed the Berkeley mpeg feature + - New brightness, contrast, hue and saturation options. + - Makefile with automatic check of dependencies and nicer user output. + - Improvement of the rotate feature. + - Added the new smart mask feature. + - Added a new config option --without-optimizecpu which disables CPU + specific compiler optimizations. + - Configure help texts improved. + - Added the pwc-10.0.5 version of pwc-ioctl.h. Also good for pwc 10.0.6. + - Changing rotate, height and width via xmlrpc no longer affects the + running program. This change is done because many internal data structures + and memory allocations cannot handle change of image dimensions/size. + - Enabled use of leading spaces when changing text_left and text_right via + xmlrpc by using quotation marks if the value starts with a leading space. + - Speed optimizations for dilate and labelling code. + - Significant speed improvement in the motion detection algoritm. + - Motion images are now gray scale instead of green. Smartmask is shown + in red. + - Implemented FreeBSD auto-detection CPU/ARCH fix. + - Removed the never finished prediction feature. + - Implemented a major improvement of noise_tune. + - Implemented ffmpeg-0.4.9 support. + - Default for option 'ffmpeg_video_codec' is now mpeg4. mpeg1 is now only + supported with the old ffmpeg-0.4.8. + - Option 'output_normal' value set to 'first' makes Motion only save a + jpeg from the first motion detected picture frame in an event. + - Implemented Streaming Netcam Without Curl which enables connecting to + network cameras both with single jpeg frame mode and streaming mjpeg + mode. This enables much higher framerates with Netcams. + - Corrected a small error in the usage help text + - Improved the help and doc texts for config option night_compensate. + - Improved the signal handling of ctrl-C and kill. + - Implemented a POSIX compliant SIGCHLD signal handler to avoid floods of + warnings and script zombies in some RedHat versions. + - Reporting of the changes of noise detection level is now only displayed + in the console (daemon off) when the always_changes option is enabled. + - Made the code in xmlrpc more correct and robust (handling of select()). + - Fixed several bugs in the timelapse feature. + + + Detailed changes for all 3.1.18 snapshot releases since 3.1.17 +snap1 - Removed the Berkeley mpeg feature (code commented out) + - Implemented a bugfixed version of + http://www.lavrsen.dk/twiki/bin/view/Motion/BrightnessContrastPatch + Released as snapshot for developers to merge other patches. + The snap1 is not recommended for normal use. +snap2 - Improved the Makefile with automatic check of dependencies and + nicer output for the user. + http://www.lavrsen.dk/twiki/bin/view/Motion/MakefileWithAutoDependencies + - Implenmented first phase of the rotate patch. Need to fix the storage + method for image height and width + http://www.lavrsen.dk/twiki/bin/view/Motion/RotatePatch +snap3 - Implemented phase 2 of the rotate patch + - Added brightness patch options to motion-dist.conf +snap4 - Added the new smart mask feature. It is working but it is still under + development. It currently outputs an extra smart mask timelapse movie + when the normal timelapse is enabled. This will be removed in the final + version. http://www.lavrsen.dk/twiki/bin/view/Motion/PatchSmartMask + - Added a new config option --without-optimizecpu which disables the + CPU specific compiler optimizations introduced with the rotate phase 2 + patch. The purpose of the new option is to enable a packager to build + an RPM or deb package which is not tied to a specific CPU type. + - Man page updated with the new brightness and smart mask options. + - Configure help texts improved. + - Added the pwc-10.0.5 version of pwc-ioctl.h. + - Changing rotate, height and width via xmlrpc no longer affects the + running program. The user can change the options and write them to the + config files and then restart motion. This change is done because many + internal data structures and memory allocations cannot handle change + of image dimentions/size. + - Fixed the problem with leading spaces of text_left and text_right + getting lost when saving with xmlrpc. For text_left and text_right + Motion now puts the string in quotation marks if the value starts with + a leading space. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2004x10x24x135840 +snap5 -Implemented the November 10 update for smartmask + -Started resolving some of the signed vs unsigned char problems. There + is still much to do here. snap5 is released to make the developers up2date. +snap6 -Merged in the DilateNineSpeedPatch + http://www.lavrsen.dk/twiki/bin/view/Motion/DilateNineSpeedPatch + -Changed a few image char definitions to unsigned char. Still many to fix. +snap7 -Implemented the 15-Nov-2004 Smartmask patch which removed the smartmask + debugging timelapse code and instead adds the smartmask info to the + motion images and jpegs as red areas. Normal motion is shown in black + and white (greytones). This concludes Joerg Webers smartmask feature. + The patch is now in ReleasedScheduled state for 3.1.18. + -Implemented Angel Carpintero's FreeBSD auto-detection CPU/ARCH fix. + -Merged in Per Johnsson's DilateFiveSpeedPatch + http://www.lavrsen.dk/twiki/bin/view/Motion/DilateFiveSpeedPatch + -Removed the prediction feature from the code (commented out for now). + - Included fix by Jan X. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2004x11x13x202132 +snap8 -Implemented an improvement of Smartmask so that the mask is cleared when + the smart_mask_speed is set from a non-zero to zero (by Joerg Weber) + -Implemented an improvement of noise_tune with smart mask (and probably + also in general) (by Joerg Weber) + -Improved the picture control function so that cameras are only probed + when needed to avoid USB errors. (Kenneth Lavrsen) + -Implemented new ffmpeg patch (Per Jönsson) + http://www.lavrsen.dk/twiki/bin/view/Motion/FfmpegPatch049 + -Implemented new preview patch (Joerg Weber) + http://www.lavrsen.dk/twiki/bin/view/Motion/PreviewShotsPatch + -Removed commented code from obsolete Berkeley and Prediction features + - Implemented labelling speed patch (Per Jönsson) + http://www.lavrsen.dk/twiki/bin/view/Motion/LabelingSpeedPatch +snap9 -Implemented Streaming Netcam Without Curl which enables connecting to + network cameras both with single jpeg frame mode and streaming mjpeg + mode. This enables much higher framerates with Netcams. (by Christopher + Price and Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/StreamingNetcamWithoutCurl + -Implemented a significant speed improvement in the motion detection + algoritm (by Per Jönsson). + http://www.lavrsen.dk/twiki/bin/view/Motion/AlgDiffStandardMmxPatch + -Fixed a small bug which caused in jumpy mpeg1 videos with ffmpeg 0.4.8. +snap10 -Corrected a small error in the usage help text + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x05x174139 + -Improved the help text for config option night_compensate in docs, + conf.c, motion man pages and config file. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x06x103939 + -Improved the Netcam patch (Angel Carpintero) + http://www.lavrsen.dk/twiki/pub/Motion/StreamingNetcamWithoutCurl/ + (pre2 patch fixes problem with not detecting Content-length and segfaults + in netcam) + -Improved the signal handling of ctrl-C as suggested by Per Jonsson + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x06x181426 + -Implemented a POSIX compliant SIGCHLD signal handler as replacement for + the traditional signal(SIGCHLD, SIG_IGN) which can cause floods of + warnings in some RedHat versions. (Angel Carpintero and Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2004x10x26x134906 + -Changed the reporting of the changes of noise detection level so that + it is only displayed in the console (daemon off) when the always_changes + option is enabled. (Kenneth Lavrsen) + -Changed the ffmpeg>0.4.8 = no mpeg1 gcc warning message so that it is + clear to people that it is information and not an error message. +snap11 -Changed allocation of despeckle buffer to avoid a seg fault when using + a netcam where the image is wider than defined in motion.conf width. + -The noise tune value displayed in the upper left corner along with + number of changed pixels is no longer displayed (was there for debugging). + -Improved the Netcam patch (Angel Carpintero) + http://www.lavrsen.dk/twiki/pub/Motion/StreamingNetcamWithoutCurl/ + (pre3 reconnection feature added) + -Changed the SIGCHLD handler introduced in snap10 so that it is a shorter + and faster function. Disabled this handler in the xmlrpc thread as this + caused unnecessary loops of cpu cycles. Additionally made the code in + xmlrpc more correct and robust (handling of select()) (Kenneth Lavrsen) + -Fixed a bug in the timelapse feature. Both the rollover events of the + timelapse video and timelapse shots could be missed if the CPU load was + very high or the time was changes by ntp. Motion will now catch up a few + seconds later if this happens. Also fixed the code for monthly rollover + (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x23x133554 +Release - Fixed a bug in the timelapse feature. Both the rollover events of the + timelapse video and timelapse shots could be missed if the CPU load was + very high or the time was changes by ntp. Motion will now catch up a few + seconds later if this happens. Also fixed the code for monthly rollover + (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x23x133554 + - Small improvement in timelapse feature so that an image is added when + the new mpeg is created and not waiting till the following timelapse + (Kenneth Lavrsen). + - Small improvement so that the timelapse rollover happens on the hour + and not one timelapse past the hour (Kenneth Lavrsen). + +3.1.17 Bugfix release +snap1 - Removed annoying debug syslog message (input: #) + - Implemented Peter Ilin's patch for handling vloopback pipes better + when Motion receives SIGTERM or SIGHUB. + - Implemented fix for compiling errors when building the FreeBSD version + without bktr support. + (http://www.lavrsen.dk/twiki/bin/view/Motion/FreeBSDFixCompile) + - Commented out many unnecessary syslog debug only messages. The commented + out code was first patched by Mike Lees patch related to syslog causing + instability and hanging motion processes. + (http://www.lavrsen.dk/twiki/bin/view/Motion/SyslogEventPatch). + - Included Kalle Andersson's patch that ensures that Motion detaches from + the stdin and stout devices so that a secure shell that was used to start + Motion in daemon mode does not hang when you exit the shell. + (http://www.lavrsen.dk/twiki/bin/view/Motion/DaemonDetachFromSTDIO) +snap2 - Implemented a new lightswitch feature so that is now triggers lightswitch + detected based on the percentage of pixels set by the lightswitch option which + is now an integer instead of a boolean. When lightswitch is detected motion + skips 5 frames to allow camera to settle. + - Fixed a bug in the autobrightness function. + - Fixed a bug in netcam_start() - wrong imgs.size calculation +Release - Swapped width and height parameters in some functions. This has no influence + on program execution. Just a cosmetic change. + + +3.1.16 Bugfix release +snap1 Fixed a configure error related to xmlrpc. + Fixed a bug in the SQL code related to file type. +Release Fixed a seg fault problem in alg.c related to the locate feature. + Made motion more robust to whitespace in its config files. It now + accepts CR LF (DOS/Windows) and whitespace only lines no longer + gives warning messages in syslog. Also cleaned up the conf.c + function structure a little bit. + + +3.1.15 Summary of changes from 3.1.14 to 3.1.15. + New features: + - ffmpeg now supports mpeg4 and msmpeg4 in addition to mpeg1. Timelapse + mpegs are always made with mpeg1 because this allows appending to + existing mpeg when motion or timelapse is restarted. This also meant + a change of the configure option --with-libavcodec to --with-ffmpeg + which now needs to point to the parent directory that holds libraries. + - configure has been significantly improved so that most people can + simply run ./configure, make and make install with no parameters and + all libraries should be detected automatically if they are installed + like in most distributions. + - rotation feature added which allows the camera to be mounted upside + down or in portrait. + - SQL (Postgres and MySQL) table format has been changed. The time + related fields such as minute, hour, day, month and year has been + replaced by a timestamp field called 'time_stamp'. Additionally a + field called 'frame' has been added so that each file can be correctly + sorted based on time_stamp and frame (picture frame number within one + second). An additional field called 'text_left' stores the displayed + text given by config option 'text_left'. And a field 'camera' has been + added which stores the thread number. + - The %v (event) prefix has been removed from the default filename + for timelapse mpegs. It makes little sense to have the current event + number as part of the timelapse filename as default. + - A new feature called 'labeling' was added which is a great enhancement + to the motion detection algoritm. It ensures that only the largest + area of movement is included in the detection and it prevents noise + and wind from makeing false detection. It should also make tracking + work better. + - Angel Carpintero ported motion and it's main features to freeBSD. + The freeBSD is still very much beta and because of limited access to + hardware we need more people to test the various features. + - Motion is now also released as an RPM with both mysql, postgres, + libcurl and ffmpeg support. + + Bugfixes: + - Fixed the problem with default strings being written to thread + config files when using the XMLRPC command motion.conf.write. + - Implemented improvement of vid_putpipe so that Motion does not spend + time writing to a vloopback device which is not there. + - Introduced reporting to syslog of writing to vloopback fails. + - Memory clean-up improvements when Motion exits normally. + - Fixed a small bug related to the filename given for onffmpegclose. + - Provided more improvements of error handling. + - Added additional error reporting to console. + +3.1.15 Snapshot releases +snap1 Fixed the problem with default strings being written to thread + config files when using the XMLRPC command motion.conf.write. +snap2 Implemented Ryan Ayers improvement of vid_putpipe so that Motion + does not spend time writing to a vloopback device which is not there. + Also introduced reporting to syslog of writing to vloopback fails. + Ryan Ayers improved configure's ability to find custom installations + of ffmpeg. + Ryan Ayers provided misc. cosmetic changes in the code. + Ryan Ayers rewrote the ffmpeg functions completely added support for + new codecs/formats mpeg4 and msmpeg4. mpeg1 was improved also. +snap3 Implemented Ryan Ayers simplified ffmpeg patch where 10 ffmpeg + functions are reduced to 5. The memory leak related to an unused + filehandle has been resolved. + Kenneth Lavrsen solved the timelapse related .mpeg.mpeg filename + error. + Kenneth Lavrsen removed the memory leak in ffmpeg.c related to the + use of strdup without a free. +snap4 Implemented Ryan Ayers snap3 based ffmpeg patch that cleans up the + code significantly, forces timelapse. The list of improvements: + 1. Forces timelapse to mpeg1 for the time being. + 2. Removes FILE* f declaration in ffmpeg.h since we no longer need it. + 3. Removes all picture_buf references. This was carried over from + output_example.c in ffmpeg-0.4.8. However, we don't need it. That is + allocating space for picture->data, but motion already takes care of + this for us with the newimg variable. + 4. Removed some old commented code ... tmpfilename, picture_buf stuff. + Included Angel Carpintero's memory clean-up improvements when Motion + exits normally. +snap5 Added Per Jönsson's rotate feature. When set to non-zero some extra + CPU time is used for the rotation. + Kenneth Lavrsen changed the ffmpeg code so that mpeg1 files are + created using the libavcodec method and mpeg4 and msmpeg4 are created + using the new libavframe method in ffmpeg. + Kenneth Lavrsen added seconds and frame fields to the database feature. + This means that users must add these two fields to the table "security" + in existing databases when upgrading to 3.1.15. + Kenneth Lavrsen removed the %v (event) prefix from the default filename + for timelapse mpegs. It makes little sense to have the current event + number as part of the timelapse filename as default. +snap6 Kenneth Lavrsen fixed a small bug related to the filename given for + onffmpegclose. + Joerg Webers added the new labeling motion detection feature. + Angel Carpintero provided more improvements of error handling. +snap7 Kenneth Lavrsen changed the configure option --with-libavcodec to + --with-ffmpeg and updated Guide and man pages and text in code and + config file to match the new shared library way of using ffmpeg + Angel Carpintero ported motion and it's main features to freeBSD. + The freeBSD is still very much beta and because of limited access to + hardware we need more people to test the various features. + Angel Carpintaro created an updated specs file that enables Kenneth + to build RPMs of Motion. +snap8 Kenneth Lavrsen added the new fields to the SQL security table + camera (thread number), text (text_left) and time (timestamp). + Angel Carpintero improved error handling for the webcam functions. +Release Kenneth Lavrsen simplified the sql functions (1 instead of 3) + and ensured that the text field is not assigned when text_left is + an empty string. This allows for the field to be auto defaulted. + Added additional error reporting to console. + + + +3.1.14 2004 May 31 + Included Ian McConnell's fix for snapshots when using the + "lastsnap" filename. + Fixed the bug in advanced filename and text feature when event + numbers go higher than 99. + Included Angel Carpintero's improvement of configure reporting of + missing shared libraries (Kenneth Lavrsen improved it a little + further). + Included Ian McConnell's timelapse close mpeg file when set to zero + patch. + Changed motion-control to make a proper output from motion.conf.list. + Renamed ffmpeg_timelaps to ffmpeg_timelapse (we change now or never) + Corrected man page (\n). + Added setting access rights to 644 (755 for configure) when doing + make dist. + Small improvement on xmlrpc-api.html document. + Included Angel Carpintero's fix for building motion without ffmpeg + (missing #ifdef round newly added ffmpeg timelapse code) + Fixed missing init of viddev.frequency causing VIDIOCGCHAN errors. + Included Angel Carpintero's snap2 based patch for improving configure. + XML-RPC changes of threshold and noise_level are now being used as + long as threshold_tune and noise_level are not enabled. + Enabled the round robin feature to also work by changing frequency on + the same device and same input. + Fixed the pre_capture feature so that it also stores the jpegs properly. + Fixed the ffmpeg_timelapse feature so that the calculated time is + correct and the current image is used instead of an old image from + position 0 in the pre_capture ring buffer. + Fixed ffmpeg routines so that also graytone images can be + pre_captured and used with ffmpeg_timelapse. + Fixed the position of the incrementing of shots in the motion_loop + so that it is correct before any functions use it. + Added quite many comments to the code to make it easier to maintain + (more comments will be added). + Removed some old debugging printf's that were displayed in non-quiet + mode. + Changed to snapshot feature from being alarm driven to being timer + driven. This means that each thread can have its own interval + value. The XML-RPC motion.action.snapshot still works. The SIGALRM + method has been changed so that all thread that have the + snapshot_interval non zero will take a snapshot when being signaled + with SIGALRM. A negative value for snapshot_interval will activate + the SIGALRM trigger but not the timing interval. + Implemented ffmpeg_timelapse_mode feature by James A. Russo. + Implemented RH (sysV) and Debian type control scripts for /etc/init.d + by Angel Carpintero. + Implemented enhanced SQL features by James A. Russo. This adds logging + of mpeg and prediction events to MySQL/PostgreSQL. + James patch also replaced the mime file types by a more refined + filetype scheme that allows more refined control for SQL and other + future control. + Kenneth Lavrsen changed the enhanced SQL config from single sql_mask + option to 5 sql_log_ options for more user friendly control. + Implemented Daniel Sterlings minimum_motion_frames feature. + Plugged a memory leak in the pre_capture feature. + Changed the behaviour of onsave back to original mode where also + snapshots causes onsave command to be run. + Fixed a bug in frequency setting of V4L device. + Renamed snapshots_interval and snapshots_filename to snapshot_interval + and snapshot_filename. + Changed the webcam_port value to 0 in motion-dist.conf to avoid that + people get segmentation faults when having 2 or more cameras and + webcam_port not set in the thread config files. + Implemented Daniel Sterlings improved handling of config strings. This + plugs the memory leak when changing string type options via XML-RPC. + It also makes the memory handling more elegant/optimal and finally + it now allows strings to be as long as allowed by the environment + variable PATH_MAX. + Kenneth and Daniel added many comments to motion.c and conf.c. + Daniel Sterling made the XMLRPC able to handle errors without crashing. + Daniel Sterling and Kenneth Lavrsen added a feature that checks for + two threads having the same webcam_port. If this is the case the last + thread gets its webcam disabled and a warning message is written + to console and syslog. + Implemented Daniel Sterling's fix for a small calculation error in + alg_diff_fast(). + Small improvements in messages sent to console and syslog during + startup of Motion. + +3.1.13 Included Ian McConnell's despeckle feature (including extra + improvement of the original patch). + Changed the name of motion.conf to motion-dist.conf to avoid + make install overwrites your perfectly OK working motion.conf + file when you re-install. + Updated the motion.spec.in. Not tested yet. + Included Matthias Kilian configure patch which enables configure + to find and use a dynamic library of ffmpegs libavcodec.so . + Included Steffen Haas improved on screen display patch + (plus some extra characters including a real space). + Changed the parsing of the motion.conf and thread.conf files so + that spaces are now allowed. This also enables using a space in + the user text. For XML-RPC you put text in "" if you need spaces. + Changed the XML-RPC function motion.conf.write so that undefined + config options gets written into the main motion.conf file with + the help line and the option prefixed by a '#'. Example + # netcam_url value. + Added Mike Lees onffmpegclose feature which enabled a command to + be executed each time a file generated by ffmpeg is closed. + Added Daniel Sterlings night compensate fix. + Added Angel Carpintero's improved configure process which + automatically detects presence of xmlrpc-c and ffmpeg and makes + the Makefile accordingly. + Included Ian McConnell's flexible on screen display feature and + flexible strftime based path names. + Changed conf.c so that xml-rpc command motion.conf.write creates + a much more user friendly motion.conf file. + Modified Ian's on screen display putting back the config parameter + (draw)text_changes. If enabled the number of changed pixes are shown + in the upper right corner of the image. + Removed the snap_override feature and reduced the oldlayout to an + Berkeley mpeg_encode feature only renaming it to + berkeley_single_directory. Instead the flexible filename + feature now has oldlayout as default and the "new" directory layout + specified in the motion.conf file. + Motion.conf sequency re-arranged so the important things comes first. + Changed names of many options to be more user friendly. + Renamed the options for displayed text to text_right, text_left and + text_changes. + Change the parsing of config files so that the argument can be in + quotation marks (" or ') allowing leading spaces for the text_left + and text_right options. This means that you can place the text + anywhere on the picture by using spaces and new lines \n. + + +3.1.12 Removed vid_keepalive + Added reentrant warning to codestyle answer in FAQ. + Rewrite of PWC tracking code. + Tracking is inactive by default. + Motion-control action.quit fix. + Netcam with mask fixed. + Added tracking options to xmlrpc interface. + Ignore SIGPIPE (crashes webcam code). + Changed fast algorithm to imgs.size/10000 steps. + Renamed prerecord to pre_capture to be more consistent with + post_capture. + Redone pre_capture completly. + +3.1.12 +rc1 added pre-record. with configparameter n_prerecord one can set + the number of frames that should be recorded *before* the + motion starts. for this feature to work, you need to set + post capture to at the value given for n_prerecord! also: + n_prerecord must be at least 1 + +3.1.11 found 2 memory-leaks (two 'FILE *' were not closed) in the + webcam-interface + started working on support for logitech sphere/orbit tracking + fixed bug in tracking routines (would sometimes use garbage + coordinates) + added max-number-of-frames-limit to the webcaminterface (patch + by Jeroen Vreeken) + +3.1.10 added Kenneth's fixes for ffmpeg instability and the problem + with the "ioctl(VIDIOCGCHAN): Invalid argument" error + small optimalisation of rgb24toyuv420p (about 6% faster) so + for certain video-devices things might be a little faster + optimized alg_diff_fast: about 50% faster + made it compile again with 2.6.0-test9 + noise tuning is now only done when there's no motion! + noise is resetted to the median of the tuned value and the + configured value as soon as no motion is detected + fixed includes for mysql/psql (thanks Felix Finch!) + +3.1.9 motion now logs to syslog instead of stderr, that way it is + still possible to see what is going wrong when motion runs + as a daemon process. + motion will now exit nicely when memory allocation fails + instead of segfaulting + low_cpu now takes the number of frames per second to process + when no motion is detected instead of on/off + "quick motion detection" is now only performed when motion + is in "idle" mode. + added Kenneth's patch: motion with ffmpeg-0.4.8 now compiles + again! + small optimalisation: if a file is created, the path is now + only recreated when it did not already exist. + + +3.1.8 is the last version release by Jeroen Vreeken. +New maintainers are: Kenneth Lavrsen (http://www.lavrsen.dk/) and Folkert +van Heusden (http://www.vanheusden.com/). + + +3.1.8 Froze 3.1.8 + Can't use the same variable name twice in ppm code... + +3.1.7 Froze 3.1.7 + Added codingstyle answer to faq + yuv to rgb conversion for ppm images. + Webcam close and denial of service fixes. + Renamed roundrobing to roundrobin. + Don't try to compile in xml-rpc support when not defined. + +3.1.6 Froze 3.1.6 + Added output_all option for continuous file saving. + Fixed picture saving on rgb files. + Fixed off-by-one error in framerate calculation. + +3.1.5 Froze 3.1.5 + Added motion.action.quit to xml-rpc api. + Changed v4l code to convert rgb to yuv420p and + removed support for rgb24 from all other files. + Removed read() support from v4l code. + Changed netcam code to output yuv420p images. + Fixed libavcodec.h include in ffmpeg.h + +3.1.4 Froze 3.1.4 + Fixed ffmpeg segfault. + Removed motion.conf* from install section in Makefile + Fixed mail option. + Fixed conf list hang. + +3.1.3 Froze 3.1.3 + Made timelaps interval variable. + Added motion.conf.write to xmlrpc API + Motion can write its own config file. + Changed xmlrpc API to use strings for all option types. + Added comment fields for config options. + Added additional settings for ffmpeg. + +3.1.2 Froze 3.1.2 + Use SO_REUSEADDR on http listen sockets. + Added control_localhost and webcam_localhost options for + binding servers to the loopback interface (default=on). + xmlrpc-httpd now uses nonblocking io allowing for multiple + connections to be handled at the same time. + Set default for 'threshold_tune' and 'noise_tune' to yes. + Added threshold_tune config option. + +3.1.1 Froze 3.1.1 + Sync with 3.0.4 + Added xmlrpc motion-control. + Changed description output to match input format. + Added Server and Connection fields to the webcam code. + Added threshold_tune????? for autotuning the max_changes level. + Added noise_tune option for autotuning the noise level. + Sync with 3.0.3 + +3.1.0 Froze 3.1.0 + Added predict evaluation. + Added predict_description config option. + Started with predict functions. + Forked from 3.0.2 + +3.0.2 Froze 3.0.2 + Changed strtok() call for argument in conf.c arguments with + '=' in them are now allowed. + +3.0.1 Froze 3.0.1 + Added 'dist' and 'updateguide' options to Makefile. + Added motion_guide.html to documentation. + Fixed mpeg names when using oldlayout. + Updated manpage. + Added check to low_cpu frame_delay calculation for a maximum + of 1 second. + +3.0.0 Froze 3.0.0 + Added string.h to ffmpeg.c + +2.9.12 Froze 2.9.12 + Added ffmpeg_bps option. + Fixed devpipe instead of devmpipe in motion.c + +2.9.11 Froze 2.9.11 + Added all config files to examples in Makefile + Fixed YUV422 converter for real :) + Use 2 as minimum fps for ffmpeg (less produces a floating point + exeption) + +2.9.10 Froze 2.9.10 + Fixed YUV422 to YUV420 converter. + Fixed oldlayout for ffmpeg files. + Added -lz for mysql. + Removed TODO from makefile. + +2.9.9 Froze 2.9.9 + Fixed leaks in webcam.c + Fixed mask image use (imgs.motionsize instead of imgs.size) + Don't try to detect motion with threshold set to 0. + +2.9.8 Froze 2.9.8 + Always try to remove snapshot link. + Cleande up config file. + Cleaned up ppm code, now loads pgm greyscale files. + Fixed max_mpeg_time segfault in ffmpeg close event. + Added -lm for libavcodec. + Updated FAQ. + Added timelaps option. + Fixed SIGHUP handling. + Added /usr/local/mysql/include and /usr/local/mysql/lib to + configure script and fixed bogus error message. + Fixed snapshot location (missing filepath) + +2.9.7 Froze 2.9.7 + Fixed some more #endif statements + Use cnt->lastrate for ffmpeg framerate. + Fixed ffmpeg code for RGB and GREY images. + Added YUV422 support (converted to YUV420P) + Added roundrobing on frequency. + +2.9.6 Froze 2.9.6 + Tweaked autobrightness mode. + Added webcam_maxrate option. + Reversed image and boundary in webcam code. + Added framerate control. + Added webcam_motion config option. + New frame_limit code. + Fixed include files for ffmpeg.h + Code cleanup. + Removed SIGHUP handler. + Removed draw on motion images. + Added speed and stepsize options to tracking code. + Reversed the night compensation. + Removed ffmpeg error in max mpegtime code. + Fixed #endif statements in header files. + Changed 'deamon' to 'daemon' + +2.9.5 Froze 2.9.5 + Added missing time.h define in webcam.c + Added 'frequency' option for v4l tuners. + Fixed max mpeg time with ffmpeg encoding + Added path for mpeg_encode (problems with PATH variable) + Fixed deamon mode. + Changed overlay to white on black (more readable). + +2.9.4 Froze 2.9.4 + Added webcam option. + Moved reference image update before draw() + Added fclose() to put_picture() + +2.9.3 Froze 2.9.3 + Added GNU license to all file headers. + New netcam code + Fixed snapshot filenames + Use correct image sizes (no more width*height*3) + +2.9.2 Froze 2.9.2 + Added ffmpeg code (from monitor) for realtime mpeg encoding. + Moved image creation to event.c + Moved put_picture functions to picture.c + Added support for Y plane images (YUV420P and GREYSCALE) + Set default jpeg quality from 50 to 75 + PostgreSQL support + Moved event handlers from motion.c to event.c + Introduced new event() functions. + Updated xml-rpc rmon code. + +2.9.1 Froze 2.9.1 + Fixed lastsnap symlink for new layout. + switchfilter added + post_capture option added + Round robbing_skip added + Added roundrobing_frames option. + +2.9.0 Froze 2.9.0 + Shuffled everything around and added 'struct context', this + should make motion ready for some serious threading. + Added xml-rpc remote monitoring. + Higher quality settings for mpeg. + Added missing w to getopt in conf.c + Removed c++ style '//' comments, they're EVIL. + Removed some old outcommented code. + +2.6.3 froze 2.6.3 + Fixed removal of directories after mpeg creation. + Added new netcam code. + +2.6.2 froze 2.6.2 + Added support for YUV420P palette to video.c, this should fix + problems with the Philips webcams. + +2.6.1 froze 2.6.1 + Changed system() calls into remove() and symlink(). + oldlayout option now also affects snapshots. + Fixed snapshot link name (ppm vs jpg). + Added new snapshot_overwrite option. + Changed directory permissions to 0755 instead of 0750. + Made timestamp overlay better readable, it is now black + or white depending on the back color. + +2.6.0 froze 2.6.0 + Updated manpage. + Added iomojo Smilecam support. + Added uninstall option to Makefile + Added config file location to the FAQ. + Made timestamp overlay inverse of original pixel instead of + white. + Created 'struct images' to minimize the passing of arguments + between functions. + Move contents of various header files around to make things + more sane. + Made locate box inverse off original pixel instead of white. + Created alg.c and alg.h, they contain all functions that + have something to do with the motion detection algoritmes. + Renamed video.c functions to vid_xxxxx. + Created video.h + Improved stepper tracking code. + Tracking code cleanup, multiple tracking interfaces are now + possible. + Added diff_hybrid, a combination of diff_fast and diff_standard + Introduced some pointer magic in the locate functions. + Added experimental diff_fast. + Fixed SIGHUP handling and blocking. + Added auto_bright option. + +2.5.0 froze 2.5.0 + Updated documentation, config files, man page. + Freeing 'line' in decompress_jpeg, this fixes a memory leak + when using Axis cameras. + Implemented SIGHUP handler for reloading config files. + Added realconfig option for starting multiple motion processes + or loading alternative config files. + Added 'jpg_cleanup' to the manpage. + Made 'low_cpu' lower on cpu. + Fixed mpeg creation for old layout. + Moved mpeg movies into year/month/day dir. + Remove empty directories when doing jpg_cleanup. + +2.4.2 froze 2.4.2 + Fixed low_cpu option to be actually low on the cpu. + Fixed missing case for '-w' in conf.c. + +2.4.1 froze 2.4.1 + New version numbering: ala linux kernel + Fixed includes for track.c + +2.4 froze 2.4 + Complete rewrite of the tracking stuff... it now uses a serial + stepper motor interface (and actually works!) + Fixed division by zero in ajdust_rate code + Added 'low_cpu' option for minimizing the cpu load while not + detecting any motion. + Added 'oldlayout' option for using the old style filenames. + Added automatic location of vloopback inputs by using + /proc/video/vloopback/vloopbacks. + Fixed '-l' option + Uploaded motion to the sourceforge CVS + Alphabetized CREDITS file. + Added 'pal-nc' norm. + +2.3 froze 2.3 + Started faq. + Includec fix for bad 'strtok' in glibc with RH 7.0 + Added \t as a delimiter for config files. + Made mpeg creation checks more sane. + +2.2 froze 2.2 + Fixed segfault bug with filebase creation. + Added creation of mpegs when killed or when getting SIGUSR1 + or when max_mpeg_time has been reached. + +2.1 froze 2.1 + Only create directories if they are going to be used. + Changed SYNC ioctl argument to int in video.c + Added realmotion option + Default config changed: + -gap is now 60 instead of 300 seconds. + -locate is on by default (-l function now works reversed) + -night_compensate is on by default + Added mpeg framerate adjustment. + Added night_compenstate for dark pictures. + +2.0 froze 2.0 + Integrated motion tracking. + Improved lightswitch detection. + Added clipcount in video.c, quickcam should work good now. + +1.99 froze 1.99 + Fixed memory leak in mpeg creation. + Created put_picture for saving images. + Moved lightswitch code out of main loop. + Merged exec_externcommand, exec_onsavecommand and + exec_onmpegcommand into exec_command + Added onmpeg command. + +1.81 froze 1.81 + Fixed segfault bug in 'mysql_password' option. + +1.8 froze 1.8 + Added video loopback for motion pictures. + Location box is now only enlarged for heads, not for feet. + Merged in Axis 2100 camera support. + conf.c now also looks for '~/.motion/motion.conf'. + Moved filebase creation to mkfilebase + Minor improvements. + Loopback feed during lightswitch. + +1.7 froze 1.7 + Little bit of code cleanup + Splitted motion.c in motion.c and video.c + Changed config.in to correctly detect the mysql libraries and + include files. + +1.6 froze 1.6 + Motion now has its own mailinglist: motion@frogtown.com + Added MySQL support + Added creation of symbolic link to snapshots + Changed file names of snapshots + +1.5 froze 1.5 + Added video loopback support for realtime viewing. + Fixed division by zero bug. + Added install option to Makefile. + Fixed bug that prevented external commands, mail and sms from + being called at the first event. + +1.4 froze 1.4 + Added lightswitch filter + Updated manpage + +1.3 froze 1.3 + Added minimum gap option. + Changed /007 to /a, not a real change but more sane. + Added mask option. + Added get_ppm for reading ppm files + Optimized greyscale blowup code. + Added 'FORCE_ENCODE_LAST_FRAME' to mpeg params file. + Experimental tracking routines for mini ssc library + +1.2 froze 1.2 + Fixed some exit(-1) to exit(1) + Updated manpage with the new options. + Added adjustable noise level. + Snapshots can be made in ppm format to. + Fixed signal blocking code (this time right?) + +1.1 froze 1.1 + Motion now has it own logos!!!! + Added timestamp to picture. + Added ppm output format. + Rewrote locate function, small things (like fish :) don't + disturb the locate option anymore. + Added break for -B option + +1.0 froze 1.0 + Went back to alarm for automated snapshots + (signals should work with bttv now) + Moved usage to conf.c + Finished manpage + Added check for existing target dir. + Moved diff calculation and image_out generation out of main + loop, adding other methods is easier this way. + Removed sanity check for output formats since someone might + want no pictures at all but only warning messages. + Make snapshots seperately from other pictures. + New snapshot names: YYYYMMDDHHMMSS-snapshot + Motion images are encoded to mpeg movies to. + Made movie file names and counter sane. + +0.99-2 froze 0.99-2 + Added signal blocking during ioctls, bttv should now continue + to work when receiving signals. + Snapshots with no movement don't go to the movies ;) + Also flush buffers when making snapshots. + Fixed for segfault when there is no config file. + (Second time, remember to fix the current version next time) + +0.99 Last beta before 1.0 + mpeg movies get timestamp in filename + mpeg_encode is now called from within motion + added chdir, filename generation is now much easier + Added check for complete frames with read + +0.8 froze 0.8 + Added script for mpeg_encode + Moved getopt stuff to conf.c + Added conf.h and conf.c for config file parsing + Added -f option for frame rate limit + Fixed frame nr count (starts at 0 again instead of 1 in v0.7) + +0.7 froze 0.7 + Added event nr to the filename, settable with -g (gap). + Added genhtml.sh (for creating a static version of show.cgi) + New show.cgi + Option for saving both motion and normal images. + Moved image_ref update into diff for loop. + -a option does not use alarm anymore, bttv card users can use + it to (they just miss the SIGALRM option...) + Added picture size settings for read. + Moved read back in, after mmap failure motion we fall back + to normal reads. + Greyscale camera fix + Added check for capture failure + +0.6 froze 0.6 + Fixed stupid typo for execute option (forgot the shift) + Added -l option for locating and marking movement + Added buffer flush to keep log files up to date + +0.5 froze 0.5 + Added contrib dir with infra red script + Option to always output changes between images + Output of motion images + configure script + +0.4 froze 0.4 + Gave the help text a new look. + Overall cleanup (moving defines to motion.h) + Added -E option for executing external commands + +0.3 froze 0.3 + New homepage: http://motion.technolust.cx + Fixed -t option (target path) + Changed from read to mmap for bttv cards + Added input and norm selection + Added alarm signal handler and snapshot (-a) option + (does not work with bttv....) + +0.2 froze 0.2 + Updated README and TODO + Added SMS and mail alert messages + Changed file name format to YYYYMMDDHHMMSS-fn.jpg + (fn=framenumber) + Added deamonize option + Cleaned up includes + Decaying reference picture added + Renamed image1 and image2 to image_ref and image_new + +0.1 Initial release diff --git a/CODE_STANDARD b/CODE_STANDARD new file mode 100644 index 0000000..d80ed37 --- /dev/null +++ b/CODE_STANDARD @@ -0,0 +1,301 @@ +Formatting rules for Motion code. + +Motion is coded in accordance with the K&R formatting style. Indentation is +TAB based but done so that formatting never depends upon how a text editor or +text viewer represents a TAB. + +Some people assume a tab is always at multiples of 8 positions, but many +others choose to use 4 or 6. If the source file does not take this into +consideration, the text alignment looks awful when viewed with a tab setting +which differs from the original. + +We want to ensure that no matter which motion source file you look at, the +style looks the same. In order to do that, the Motion project enforces some +additional rules. + +Here are the basic rules. + +Note: To understand them you must view this document with spaces and tabs +visible. + +-------------------- +RULE 1 +Code is generally indented using TABS + +Example1 + +/* allocate some memory and check if that succeeded or not. If it failed + * do some error logging and bail out + */ +void * mymalloc(size_t nbytes) +{ + void *dummy = malloc(nbytes); + if (!dummy) { + printf("Could not allocate %llu bytes of memory!\n", (unsigned long long) nbytes); + syslog(LOG_EMERG, "Could not allocate %llu bytes of memory!", (unsigned long long) nbytes); + exit(1); + } + + return dummy; +} + +-------------------- +RULE 2 +If a line or statement is broken into two lines you will normally want the text +in the 2nd line to align with text in the first line. This alignment must be +done by following these rules: + 1. On the continuation line, first you put tabs to reach the same + indentation level as the line above. + 2. Then you align with SPACES until the text in the 2nd line is aligned + with the desired text above. + +Example 2 +/* allocate some memory and check if that succeeded or not. If it failed + * do some error logging and bail out + */ +void * mymalloc(size_t nbytes) +{ + void *dummy = malloc(nbytes); + if (!dummy) { + printf("Could not allocate %llu bytes of memory!\n", + (unsigned long long) nbytes); + syslog(LOG_EMERG, "Could not allocate %llu bytes of memory!", + (unsigned long long) nbytes); + exit(1); + } + + return dummy; +} + +Never do this: +WRONG EXAMPLE + printf("Could not allocate %llu bytes of memory!\n", + (unsigned long long) nbytes); + +The reason is that the 3rd tab will be shown with whatever width is given by +the editor or viewer. The result is that you never know where the text ends. +The alignment of the text is very important for the readability of the code. + + +GOOD EXAMPLE + cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + + cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + + cnt->conf.sql_log_mpeg * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; + +BAD EXAMPLE + cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + + cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + + cnt->conf.sql_log_mpeg * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; + + +GOOD EXAMPLE + char msg[] = "This is a very long message which we would like to break" + "into two lines or more because otherwise the line gets" + "too long to read. We align them below each other for readability" + +BAD EXAMPLE + char msg[] = "This is a very long message which we would like to break" + "into two lines or more because otherwise the line gets" + "too long to read. We align them below each other for readability" + +Again, a different tab setting destroys alignment. + +-------------------- +RULE 3 +Never use TABS to align anything other than the start of line indentation. + +WRONG EXAMPLE + * + * cnt Pointer to the motion context structure + * level logging level for the 'syslog' function + * (-1 implies no syslog message should be produced) + * errno_flag if set, the log message should be followed by the + * error message. + * fmt the format string for producing the message + * ap variable-length argument list + +THE CORRECT WAY + * + * cnt Pointer to the motion context structure + * level logging level for the 'syslog' function + * (-1 implies no syslog message should be produced) + * errno_flag if set, the log message should be followed by the + * error message. + * fmt the format string for producing the message + * ap variable-length argument list + +Again the reason is that the aligment of the text when using tabs is +depending on the tab settings in editor or viewer. + +BAD EXAMPLE + +void function_a(void someparam) +{ + int myvar1 /* explanation */ + char hellomyvar2 /* explanation */ + +In this bad example the variable names will not align if the tab setting is +not 8 positions. At 4 positions, for example, the variable names (as well as +the comments) are no longer aligned. + +GOOD EXAMPLE +void function_a(void someparam) +{ + int myvar1 /* explanation */ + char hellomyvar2 /* explanation */ + +Don't try and align variables. It does not become very readable when one type +is int and another is unsigned long long int. There is too much white space +between a short type name and the variable name. Comments after the variable +name look good, provided that you use spaces to align them. + +-------------------- +RULE 4 +Functions should be written with this syntax. + +GOOD EXAMPLE +/* Comment block + * A comment block should be at least one line saying what the function does. + * It is better to make several lines explaining what it does, what it takes + * for arguments and what it returns. It is a bad idea to try to use tabs to + * align text in the comment block + */ +type function_name(parameters) +{ + declarations + declarations + + statements + statements +} + +Do not split the function declaration into two lines. +Do not put the '{' after the function declaration. Put it on an empty line +right after. Note that this rule is only for functions. + +BAD EXAMPLE + +type +function_name(parameters) { + declarations + declarations + + statements + statements +} + +-------------------- +RULE 5 +Blocks follow K&R. +Kenneth Lavrsen actually hates the K&R syntax, but it is the most generally +accepted way, it was the way Motion was coded when Kenneth took over the +project and it is now the way in which we will continue. + +GOOD EXAMPLE + +if ((picture=fopen(cnt->conf.mask_file, "r"))) { + cnt->imgs.mask=get_pgm(cnt, picture, cnt->imgs.width, cnt->imgs.height); + fclose(picture); +} else { + put_fixed_mask(cnt, cnt->conf.mask_file); + printf("Hello world\n"); +} + + +BAD EXAMPLE (even though Kenneth loves this one personally) +if ((picture=fopen(cnt->conf.mask_file, "r"))) +{ + cnt->imgs.mask=get_pgm(cnt, picture, cnt->imgs.width, cnt->imgs.height); + fclose(picture); +} +else +{ + put_fixed_mask(cnt, cnt->conf.mask_file); + printf("Hello world\n"); +} + +-------------------- +RULE 6 +Whitespace. +To ensure that Motion code looks homogeneous and to enhance readability: + 1. Do not use a space before a comma + 2. Always leave at least one space after a comma + 3. Use one space between a block start statement and a '{' + 4. Do not use a space between a function name and the '(' + 5. Use spaces to enhance readability (a non objective rule but at least + think about it) + 6. The '*' for pointers should be just before the variable name with no + space. + +BAD EXAMPLES +int function_name (int * par1 , int par2,int par3){ +if (var1==2||var2==3){ + +GOOD EXAMPLES +int function_name(int *par1, int par2, int par3) { +if (var1==2 || var2==3) { + +-------------------- +RULE 7 +Comment your code +That's worth repeating - PLEASE, PLEASE comment your code. +We receive far too much code which is completely uncommented and where +variable names are short and say nothing about their function. +Use /* This style of comment for permament comments */ or +/* + * This style of comment for comments which + * require more that one line + */ +Use // this style comments for something you add temporarily while testing and +FIXME type comments. It is much easier to spot the temporary comments this way. + +-------------------- +RULE 8 +Use variable names that say what the variable is used for. +Avoid x,t,vt type variable names. +Use names like image, image_buffer, image_height, output_buffer +Short names like i and j for loop index variable are a known good practice. +Variable and function names are in lower case. Use '_' to separate words. +MACROS are in uppercase. +camelCase (mix of upper and lower case) is not allowed because it creates too +many typos for many two finger typers. + + +-------------------- + +BEST PRACTICES +Not rules, but these suggestions make code easier to read. + +Use lots of white space and empty lines to group code. +For example, large if statements are easier to read when there is an empty +line before and after them. + +Use an empty line before a comment which describes the code lines below. + +Always use spaces in statements like +thisvar->thismember>thisvar->thisothermember (bad) +thisvar->thismember > thisvar->thisothermember (good) + +if (cnt->event_nr==cnt->prev_event||cnt->makemovie) (bad) +if (cnt->event_nr == cnt->prev_event || cnt->makemovie) (good) + +frame_delay=(1000000L/cnt->conf.low_cpu)-frame_delay-elapsedtime; (bad) +frame_delay = (1000000L/cnt->conf.low_cpu) - frame_delay - elapsedtime; (good) + + +-------------------- + +This document can probably be enhanced more as time goes by. +Hope it helps developers to understand the ideas. + +What happens if I do not follow the rules? +Your code will probably be accepted, but Kenneth will have to spend a lot of +time rewriting the code to follow the standard. If this happens, he may make +a less-than-complimentary remark. Please help Kenneth by at least trying to +follow the spirit of this document. We all have our coding preferences, but +if Motion is coded in 40 different styles, readability (and at the end +quality) will become bad. + + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..3702407 --- /dev/null +++ b/CREDITS @@ -0,0 +1,1108 @@ +Thanks go to: + +Jeroen Vreeken + *** Original writer of this great program! *** + From 3.1.12 these contributions: + - Rewrite of PWC tracking code. + - Motion-control action.quit fix. + - Added tracking options to xmlrpc interface. + - Ignore SIGPIPE (crashes webcam code). + - Changed fast algorithm to imgs.size/10000 steps. + - Renamed prerecord to pre_capture to be more consistent with + post_capture. + - Redone pre_capture completly. + +Kalle Andersson + - Created a patch that ensures that Motion detaches from + the stdin and stout devices so that a secure shell that was used to + start Motion in daemon mode does not hang when you exit the shell. + +Ryan Ayers + - Implemented improvement of vid_putpipe so that Motion does not + spend time writing to a vloopback device which is not there. + Also introduced reporting to syslog of writing to vloopback + fails. + - Improved configure's ability to find custom installations of + ffmpeg. + - Misc. cosmetic changes in the code. + - Rewrote the ffmpeg functions completely added support for + new codecs/formats mpeg4 and msmpeg4. mpeg1 was improved also. + - Misc. improvement of the mpeg4 feature and new ffmpeg code. + +Paul Beltrani + - Implemented a fix/work around to a bug related to building and installing + RPMs on Suse. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x07x14x212356 + +Michael Newlyn Blake + - For setting up the motion mailinglist and the onsave command. + +Mathias Bogaert + - Lots of good ideas and the motion logos + +William M Brack + - Added patch that enables Motion to work with vloopback version 0.94 + and kernel 2.6.10+. + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionAndVloopbackVideoDotCPatch + - Added support in configure for athlon64 from + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x30x190907 + (Angel Carpintero and William M Brack) + - Fixed some gcc warnings + - Code cleanup from a valgrind analysis. + - Implemented WebcamShortWriteHandling patch + http://www.lavrsen.dk/twiki/bin/view/Motion/WebcamShortWriteHandlingPatch + - Small code cleanup in motion.c for the variable holding the number of + microseconds since epoch. The old code worked fine but relied on an integer + overflow every 71 minutes. (Bill Brack and Kenneth Lavrsen) + - Complete rewrite of the Netcam code. Should fix many of the reported and + still open netcam bugs. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamCodeRewritePatch + - Improved the error reporting in the Netcam code and did a few minor + corrections and code cleansups. + - Implemented a much easier to use motion_log function which replaces the + calls to prinf and syslog. The implementation to actually use this has been + implemented in video.c and the Netcam code files. Rest will be in next snap. + This code change as no impact to the user. + http://www.lavrsen.dk/twiki/bin/view/Motion/ErrorLoggingEnhancementPatch + - Fixed a buf in video.c so that VIDEO_PALETTE_GREY cameras now actually work. + - Re-mplementation of optional Proxy Server for Network Cameras. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamProxyServerPatch + - Added the missing rotate feature in the new netcam code (Billl Brack) + - Netcam error handling improvements and cleanup from Walgrind analysis. + - Added a configure option --with-developer-flags which enables many compiler + warnings that can be used by developers to make code more robust. Not + for normal users building Motion. + - Removed all warnings originating from the motion sources when running + ./configure --with-developer-flags. + The modifications were done by the following people: Peter Holik, Bill Brack, + Angel Carpintero and Kenneth Lavrsen. + http://www.lavrsen.dk/twiki/bin/view/Motion/ReduceWarningsPatch + - Fixed error message with unknown config option. + - Enhanced compatibility with Lumenera. + - Moved the motion_loop initialization into a new function motion_init. + - netcam code now waits for the next frame to arrive for a limited period + in order to avoid too many duplicate images. + - Last --with-developer-flags warnings eliminated simply by swapping the + order of the #include statements in the sources (Bill Brack and Kenneth Lavrsen). + - Enhancement to Netcam Code for Connection to Pixord Cameras. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamFixPixordBug + - Fix related to connecting to the netcam. From mailing list 23 Dec 2005. + +Ashley Cambrell + - PostgreSQL support, put_jpeg_grey(), webcam bugfixes. + +Angel Carpintero + - Improved configure process which automatically detects presence + of xmlrpc-c and ffmpeg and makes the Makefile accordingly. + - Improvement of configure reporting of missing shared libraries. + - Fix for building motion without ffmpeg (missing #ifdef round newly + added ffmpeg timelapse code) + - Implemented RH (sysV) and Debian type control scripts for /etc/init.d + - Memory clean-up improvements when Motion exits normally. + - Provided several improvements of error handling. + - Ported Motion to FreeBSD. + - Created the spec file so that Kenneth can build RPMS when releasing + Motion. + - Improved error handling for the netcam functions. + - Implemented fix for compiling errors when building the FreeBSD version + without bktr support. + - Added a new config option --without-optimizecpu which disables the + CPU specific compiler optimizations introduced with the rotate phase 2 + patch. + - Implemented Streaming Netcam Without Curl which enables connecting to + network cameras both with single jpeg frame mode and streaming mjpeg + mode. This enables much higher framerates with Netcams. (with + Christopher Price). + - Improved the Netcam patch (Angel Carpintero) + http://www.lavrsen.dk/twiki/pub/Motion/StreamingNetcamWithoutCurl/ + (pre2 patch fixes problem with not detecting Content-length and segfaults + in netcam) + - Implemented a POSIX compliant SIGCHLD signal handler as replacement for + the traditional signal(SIGCHLD, SIG_IGN) which can cause floods of + warnings in some RedHat versions. (with Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2004x10x26x134906 + - Improved the Netcam patch (Angel Carpintero) + http://www.lavrsen.dk/twiki/pub/Motion/StreamingNetcamWithoutCurl/ + (pre3 reconnection feature added) + - Fixed several bugs in new netcam code introduced in 3.1.18 + (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x16x030209 + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x02x01x071546 + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x02x03x035918 + - Netcam code: Change printf() to fprintf(). + - Netcam code: Cleanup memory netcam (netcam.c , motion.c ). + - Netcam code: Redesign of reconnection feature. + - Configure: Added debug , conditional compile of xmlrpc-c + - Fix a non allocated pointer to be freed. + - Added fix to BugReport2005x02x11x170019 + - Added fix to BugReport2005x02x11x150802 + - Major new feature. XMLRPC is replaced by a simpler http remote control + interface (implemented by Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Fixed netcam->userpass problem + - Added support in configure for athlon64 from + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x30x190907 + (Angel Carpintero and William M Brack) + - Updated code so Motion again runs on FreeBSD. + - Removed check for memmem from configure. + - Updated http control interface so that an additional check is done + before saving config files. + - Fixed a problem with URLs http://192.168.1.3:8080/0 which did not + work without a trailing space. + - Fix the compile issue with official ffmpeg packages from debian. + - Added basic authentication to the http control interface introducing new + config option control_authentication. + - Fixed memory leak when restarting Motion from http control. + - Small improvement in configure script for Debian. + - Added the ability to clear an option to off (bool), 0 (int) or undefined + (string) by submitting blank entry field in the http control interface. + - http interface small fixes (motion-3.2.1_snap14-small-fixes 1.1) incl + Add 'back' link to response_client errors. + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Made the http control interface more RFC compliant. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x02x180550 + - Fixed compatibility problem with Palantir. Fixed by making output more + compatible with RFC (\r\n). Original fixes by Roberto Spadim and Angel + Carpintero. However this fix made Firefox flicker even more than it normally + does. Final fix which works in both Palantir client, Firefox and Cambozola + was made by Kenneth Lavrsen. This closes the following bugs: + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x02x205307, + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x07x042849 + - In httpd control code: Fixed RAW syntax following API specs. + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - MotionHttpControl Patch motion-3.2.1_snap18-pre1 v,1.0 19 May 2005. + Fixed some HTTP response codes and added header copyrights. + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl + - Fixed problem compiling "ffmpeg reports only YUV420 is supported" when + ffmpeg is a recent CVS version. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x22x213229 + - Bug fix in netcam code: Sometimes motion try to free an invalid memory area + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x21x105335 + - Small configure fix related to --without-v4l. + - Fixes for http control HTML code. + - Added init script to RPM. + - Pthread deadlock in motion 3.2.1 fixed. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x26x125712 + - http lockup bugfixes and ConvertSignalToSigaction only for webhttpd + - alg_draw_location: Use temporary variables to store the values used in + for() loops instead of compute them in each loop + http://www.lavrsen.dk/twiki/bin/view/Motion/ImproveAlgDrawLocation. + - Small speed boost to the function draw_textn (Andrew Hamilton and + Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/DrawTextnImprovement + - Avoid Cleanup Segfault. Avoid Cleanup Segfault. Allocates filepath using + strdup to avoid segfault is target_dir parameter is not supplied in + motion.conf. Moves out from signal handler the cleanup for pipe and mpipe. + http://www.lavrsen.dk/twiki/bin/view/Motion/AvoidCleanupSegfault + - Changed the configure script so that rpms can be made by normal non-root + users. + - Above change in configure script for 64 bit ffmpeg support also implemented + in the freeBSD configure. + - Webhttp control interface fixed so it also works in FreeBSD. + - Fixed a bug in the webhttpd code related to pan/tilt. Bug was introduced in + snap4 (Angel Carpintero, Kenneth Lavrsen). + - Implemented the libjpeg-mmx patch. Installing the MMX version of libjpeg + can increase performance. Especially for machines with very little CPU power. + It only modifies the configure script. If you do not have the libjpeg-mmx + the configure script with ignore this and use the standard libjpeg. + Note that RPMS will be built without this (Peter Holik and Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/LibJpegMmx + - Fixed memory leak in webhttpd related to use of strdup. + - Error Logging Enhancement Patch v 1.3 (Angel Carpintero) including: + - Populate the motion_log to the whole motion source code. + - Fixed FreeBSD copilation. + - Added the posiblity to pass NULL as struct context * + - Removed unused errno variables. + - Fixed errno in rotate.c , set to 0. + - Fixed some errno flags in webhttpd.c and motion.c + - Fixed a bug when not motion.conf is found + - Removed printf from all files + - Fixed the conf_list[] index in motion.c + http://www.lavrsen.dk/twiki/bin/view/Motion/ErrorLoggingEnhancementPatch + - http-control: Fixed segfault when motion is restarted from command line + ( kill -s 1 pid_motion ). Improved control code so Motion can Restart and + Finish 'smoothly'. (Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpControl. + - Fixed a bug in the http control code that failed to accept a client + connecting in some systems + - Removed all warnings originating from the motion sources when running + ./configure --with-developer-flags. + The modifications were done by the following people: Peter Holik, Bill Brack, + Angel Carpintero and Kenneth Lavrsen. + http://www.lavrsen.dk/twiki/bin/view/Motion/ReduceWarningsPatch + - Fixed small mistake in allocating memory for cnt->imgs.common_buffer. + - http control updated: (null) messages replaced by "disabled", last parameter + in conf/list are displayed correctly and only in Main thread. When motion runs + with only one thread, it displays "No threads". + - http control: selectbox instead of a textfield for changing boolean configs + (Peter Holik and Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/WebhttpEnhancements. + - Added the debian sub directory so that people can build the deb package. + - Sync configure.in.freebsd (adding support for jpeg-mmx, developer-flags and + some cosmetic changes ). + - Implemented --with-developer-flags fixes in FreeBSD code. + - In v4l_start change map from unsinged char * to void * to be ANSI C compliant + with mmap + - http control: Changed disabled to (not defined) when displaying option list. + - FreeBSD Code improvements by Angel Carpintero + - Implemented set/get hue , saturation , contrast and brightness. + - Better support to capture with big resolution ( 640x480 , 768x576 ). + - Update Readme adding information about "how to configure a capture + card and settings" , update packages dependencies . + - Remove support for libjpeg-mmx , motion sefgault ( future fix ). + - Cosmetics changes in configure.in.freebsd ( replace --without-v4l by + without-bktr ). + - Cleanup code and fix warnings. + - Implemented fix to configure so that LDFLAGS from the environment are used + when making the Makefile. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x09x15x185558 + - Changed configure so that --with-jpeg-mmx is default off as a reaction to + known problems seen when using the jpeg-mmx library. + - Added the %t conversion specifier to show the thread number. + http://www.lavrsen.dk/twiki/bin/view/Motion/ThreadConversionSpecifierPatch + - Fixed bug related to init of mutex in netcam code. + - Netcam_ftp code fixes (Angel Carpintero and Asbjørn Pettersen) + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamWithFtpEnhancements + - Enhanced ffmpeg detection. + http://www.lavrsen.dk/twiki/bin/view/Motion/BetterFFmpegDetection + - New Feature: Motion is now also supported on MaxOSX with similar + feature set as for Free BSD. See README.MacOSX for details how to install + it. (Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/MacOSXPatch + - Added a work-around so people in FreeBSD that uses a capture card + where input 1 is not tuner can use motion if frequency is set -1 in + motion.conf or thread#.conf. + - Fixed misc problems in FreeBSD. + - Update README.FreeBSD + - Fix problems with tuner_device and frequency, now by default is not + defined to allow use any input without problem. + - Replace strndup() by memcpy() in netcam.c + - Merged configure.in.freebsd with configure.in (configure.in.freebsd + deleted) + - Remove a warning when used --without-bktr + - Remove cpu optimization (is broken) + - Fixed http control of pan and tilt. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x12x22x122649 + +John Edwards + - Added the 'pal-nc' norm. + +Stephen Farrugia + - Fixing the division by zero problem. + This makes motion a lot more stable. + +Miguel Freitas + - Came up with the round robing idea. + +Aaron Gage + - Pointed me to the vid_mmap/int problem when calling SYNC in + video.c + +Christophe Grenier + - Fixed some file descriptor leaks in webcam.c and netcam.c. + - Renamed the top level global context structure to cnt_list so it can be + reached from child threads and by above mentioned close_anything_open() + - Contributed with most of the code for new function in event.c + close_anything_open() which is called from send_sms, send_mail and + exec_command in order to prevent file descriptor and open sockets to be + inherited by the shell causing freezing and instability. + Code contributed by Christophe Grenier, Christopher Price and + Kenneth Lavrsen. + - Change the working directory to / in daemon mode. This way you don't have + to kill motion to umount the partition from where you start it. + http://www.lavrsen.dk/twiki/bin/view/Motion/ChdirNetCamWgetPatch + - In netcam-wget header_get() didn't always in add a \0 string terminator. + This was fixed. + http://www.lavrsen.dk/twiki/bin/view/Motion/ChdirNetCamWgetPatch + - Made a pthread fix. + http://www.lavrsen.dk/twiki/bin/view/Motion/PthreadFixPatch + - Implemented the conversion of signal to sigaction which should be more + thread safe. Hopefully this still keeps Motion from making Zombies. + http://www.lavrsen.dk/twiki/bin/view/Motion/ConvertSignalToSigaction + +Mihnea-Costin Grigore + - Fixed the oldlayout behaviour of snapshots. + - Fixed snapshot link extension. + - Added the snapshot_overwrite option. + - Fix for correct mpeg names when using mpeg_encode. + +Jan Gyselinck + - Original time/date-stamp code. + - ppm support + - Good ideas + +Colling H - New frame_limit. + +Steffen Haas + - Improved on screen display by adding more symbols and lower + case letters. + +Andrew Hamilton + - Small speed boost to the function draw_textn (Andrew Hamilton and + Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/DrawTextnImprovement + - Added new feature: Double size text. A new config option 'text_double' can + be set 'on' and this scales the text to double size. Default is off. + http://www.lavrsen.dk/twiki/bin/view/Motion/TextScalingPatch + - Fixed memory leak in ffmpeg code. + +Peter Holik + - Netcam First Header patch. If an error with jpeg decompression occured at + connecting to a mjpeg streaming webcam, this patch skips this jpeg and tries + to decompress next jpeg up to MAX_HEADER_RETRIES (20). + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamFirstHeader + - Small improvement in framerate accuracy. + http://www.lavrsen.dk/twiki/bin/view/Motion/FramerateAdjust + - Implemented a modified version of the WebcamCompressInMemory so that Motion + no longer uses the tmpfile() function for buffering the frames of the mjpeg + stream. + http://www.lavrsen.dk/twiki/bin/view/Motion/WebcamCompressInMemory + - Implemented the libjpeg-mmx patch. Installing the MMX version of libjpeg + can increase performance. Especially for machines with very little CPU power. + It only modifies the configure script. If you do not have the libjpeg-mmx + the configure script with ignore this and use the standard libjpeg. + Note that RPMS will be built without this (Peter Holik and Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/LibJpegMmx + - Small code cleanup in webcam.c and picture.c and .h for the webcam code + (Peter Holik and Kenneth Lavrsen). + - Small speed optimization in the creation of reference frame. + - Removed all warnings originating from the motion sources when running + ./configure --with-developer-flags. + The modifications were done by the following people: Peter Holik, Bill Brack, + Angel Carpintero and Kenneth Lavrsen. + http://www.lavrsen.dk/twiki/bin/view/Motion/ReduceWarningsPatch + - Implemented a speed-up patch of the draw text feature. + http://www.lavrsen.dk/twiki/bin/view/Motion/DrawTextspeedup + - http control: selectbox instead of a textfield for changing boolean configs + (Peter Holik and Angel Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/WebhttpEnhancements. + - Introduced check for device image size being a multiple of 16. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamModulo16Patch + - Fixed netcamera bug related to seperating frames in an mjpeg stream. + From mailing list 23 Dec 2005. + +Wesley Hosking + - For pointing me to the absence of a frame length check using + read for capturing + +Peter Ilin - Patch for handling vloopback pipes better when Motion receives + SIGTERM or SIGHUB + +Per Jönsson + - Added the rotate feature. + - Improved the Makefile with automatic check of dependencies and + nicer output for the user. + http://www.lavrsen.dk/twiki/bin/view/Motion/MakefileWithAutoDependencies + - Improved rotate feature (speed) + http://www.lavrsen.dk/twiki/bin/view/Motion/RotatePatch + - Implemented new ffmpeg patch + http://www.lavrsen.dk/twiki/bin/view/Motion/FfmpegPatch049 + - Implemented labelling speed patch + http://www.lavrsen.dk/twiki/bin/view/Motion/LabelingSpeedPatch + - Improved the signal handling of ctrl-C + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x06x181426 + - Fixed the ffmpeg code so that Motion also compiles against libavcodec + build 4754 or later. + - Fixed a bug in the autobrightness algoritm. + - RotateBswapFix Patch v 2 including: + - cleanup in code comments + - fix for __bswap_32 macro collision + - fixed bug where initialization would be incomplete for invalid degrees + of rotation + - now uses motion_log for error reporting + http://www.lavrsen.dk/twiki/bin/view/Motion/RotateBswapFix + - Implemented Threadnr in TLS (thread-local storage)patch. It puts the thread + number into TLS and modifies motion_log() so that we do not have to drag the + cnt struct around just to be able to print the thread number in the log and + on the console. (Per Jönsson with additional removal of unuseded cnt by + Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/ThreadNrTlsPatch + - Simplified rotation code based on the fact that images must have dimentions + that are a multiple of 16. + http://www.lavrsen.dk/twiki/bin/view/Motion/RotateSimplificationPatch + +Matthias Kilian + - Configure patch which enables configure to find and use a + dynamic library of ffmpegs libavcodec.so + +Daniel Ladd + - Fixed a bug in the rgb2yuv420p function. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x03x30x011107 + +Kenneth Lavrsen (Currently project managing Motion) + - Wrote the excellent Motion Guide. (Jeroen wrote this :-) ) + - Fixed low_cpu to check for a 1sec. maximum wait. + - Updated manpage for 3.0.0 + - New usertext additions to draw.c + - Fixed ffmpeg compatibility for ffmpeg 0.4.8 + - Fixed ffmpeg instability problem + - Fixed "ioctl(VIDIOCGCHAN): Invalid argument" error + - Changed motion.conf to motion-dist.conf. + - Changed the parsing of the motion.conf and thread.conf files so + that spaces are now allowed. + - Changed the parsing of the user defined on screen display text + so that you can enclose a string in "" both in config file + and using the XML-RPC interface. Ie. you can use spaces in the + text. + - Changed conf.c so that xml-rpc command motion.conf.write creates + a much more user friendly motion.conf file. + - Modified Ian's on screen display putting back the config parameter + drawtext_changes. If enabled the number of changed pixes are shown + in the upper right corner of the image. + - Removed the snap_override feature and reduced the oldlayout to an + Berkeley mpeg_encode feature only renaming it to + berkeley_single_directory. Instead the flexible filename + feature now has oldlayout as default and the "new" directory layout + specified in the motion.conf file. + - Motion.conf sequency re-arranged so the important things comes first. + - Changed names of many options to be more user friendly. + - Renamed the options for displayed text to text_right, text_left and + text_changes. + - Change the parsing of config files so that the argument can be in + quotation marks (" or ') allowing leading spaces for the text_left + and text_right options. This means that you can place the text + anywhere on the picture by using spaces and new lines \n. + - Fixed problem with strftime based names with event number %v when + event numbers were higher than 99. + - Changed motion-control to make a proper output from motion.conf.list. + - Renamed ffmpeg_timelaps to ffmpeg_timelapse (we change now or never) + - Corrected man page (\n) (thanks Daniel). + - Added setting access rights to 644 (755 for configure) when doing + make dist. + - Small improvement on xmlrpc-api.html document. + - Fixed missing init of viddev.frequency causing VIDIOCGCHAN errors. + XML-RPC changes of threshold and noise_level are now being used as + long as threshold_tune and noise_level are not enabled. + - Enabled the round robin feature to also work by changing frequency on + the same device and same input. + - Fixed the pre_capture feature so that it also stores the jpegs properly. + - Fixed the ffmpeg_timelapse feature so that the calculated time is + correct and the current image is used instead of an old image from + position 0 in the pre_capture ring buffer. + - Fixed ffmpeg routines so that also graytone images can be + pre_captured and used with ffmpeg_timelapse. + - Fixed the position of the incrementing of shots in the motion_loop + so that it is correct before any functions use it. + - Added quite many comments to the code to make it easier to maintain + (more comments will be added). + - Removed some old debugging printf's that were displayed in non-quiet + mode. + - Changed to snapshot feature from being alarm driven to being timer + driven. This means that each thread can have its own interval + value. The XML-RPC motion.action.snapshot still works. The SIGALRM + method has been changed so that all thread that have the snapshot_interval + non zero will take a snapshot when being signaled with SIGALRM. A + negative value for snapshot_interval will activate the SIGALRM trigger + but not the timing interval. + - Kenneth Lavrsen changed the enhanced SQL config from single sql_mask + option to 5 sel_log_ options for more user friendly control. + - Changed the behaviour of onsave back to original mode where also + snapshots causes onsave command to be run. + - Fixed a bug in frequency setting of V4L device. + - A few lines of code for Dan's improved handling of config strings. + - Daniel Sterling and Kenneth Lavrsen added a feature that checks for + two threads having the same webcam_port. If this is the case the last + thread gets its webcam disabled and a warning message is written + to console and syslog. + - Small improvements in messages sent to console and syslog during + startup of Motion. + - Fixed the problem with default strings being written to thread + config files when using the XMLRPC command motion.conf.write. + - Fixed memory leaks in new ffmpeg code. + - Changed the ffmpeg code so that mpeg1 files are created using the + libavcodec method and mpeg4 and msmpeg4 are created using the new + libavframe method in ffmpeg. + - Added seconds and frame fields to the database feature. + - Fixed a small bug related to the filename given for onffmpegclose. + changed the configure option --with-libavcodec to --with-ffmpeg + and updated Guide and man pages and text in code and config file to + match the new shared library way of using ffmpeg. + - Added the new fields to the SQL security table camera (thread + number), text (text_left) and time (timestamp). + - Simplified the sql functions (1 instead of 3) and ensured that the + text field is not assigned when text_left is an empty string. This + allows for the field to be auto defaulted. + - Added additional error reporting to console. + - Implemented a new lightswitch feature so that is now triggers lightswitch + detected based on the percentage of pixels set by the lightswitch option which + is now an integer instead of a boolean. When lightswitch is detected motion + skips 5 frames to allow camera to settle. + - Fixed a bug in the autobrightness function. + - Fixed a bug in netcam_start() - wrong imgs.size calculation. + - Removed the obsolete Berkeley mpeg feature. + - Corrected a small error in the usage help text + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x05x174139 + - Improved the help text for config option night_compensate in docs, + conf.c, motion man pages and config file. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x06x103939 + - Implemented a POSIX compliant SIGCHLD signal handler as replacement for + the traditional signal(SIGCHLD, SIG_IGN) which can cause floods of + warnings in some RedHat versions. (with Angel Carpintero) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2004x10x26x134906 + - Changed the reporting of the changes of noise detection level so that + it is only displayed in the console (daemon off) when the always_changes + option is enabled. (Kenneth Lavrsen) + - Changed the ffmpeg>0.4.8 = no mpeg1 gcc warning message so that it is + clear to people that it is information and not an error message. + - Changed allocation of despeckle buffer to avoid a seg fault when using + a netcam where the image is wider than defined in motion.conf width. + - The noise tune value displayed in the upper left corner along with + number of changed pixels is no longer displayed (was there for debugging). + - Changed the SIGCHLD handler introduced in snap10 so that it is a shorter + and faster function. Disabled this handler in the xmlrpc thread as this + caused unnecessary loops of cpu cycles. Additionally made the code in + xmlrpc more correct and robust (handling of select()) (Kenneth Lavrsen) + - Fixed a bug in the timelapse feature. Both the rollover events of the + timelapse video and timelapse shots could be missed if the CPU load was + very high or the time was changes by ntp. Motion will now catch up a few + seconds later if this happens. Also fixed the code for monthly rollover. + (Kenneth Lavrsen) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x23x133554 + - Small improvement in timelapse feature so that an image is added when + the new mpeg is created and not waiting till the following timelapse + (Kenneth Lavrsen). + - Small improvement so that the rollover happens on the hour and not one + timelapse past the hour (Kenneth Lavrsen). + - Fixed a bug in noise tune which was most visible at very low light. + - Re-arranged many of the const char declarations so that they are always + before any statements within a block { }. This is to avoid compiler errors + with older but still used gcc versions such as 2.9.5. + - Changed the use of %zd to %llu in printf statements of size_t types. + This is done to avoid compiler errors with older but still used gcc versions + such as 2.95. + - Fixed even more gcc 2.95 compiler errors (declarations not at beginning + of block). + - Removed a gcc 2.95 compiler warning (netcam.c:1036: warning: variable `pic' + might be clobbered by `longjmp' or `vfork'). + - The values for cnt->locate and cnt->new_img are now #defines in motion.h + to enhance code readability. + - The setting of sql_mask is now only done once per second to save CPU power. + - Adding checking for conflict between control port and webcam port. Webcam + port for a thread is disabled if it is set to the same value as the control + port. + - Added "motion-http:" prefix to error messages from the http control thread. + (Kenneth Lavrsen) + - Added additional error information when connection to MySQL fails. + - Initiate cnt->event_nr to 1 to avoid code related to end of events and long + mpeg films to be run during startup of Motion. + - Added new function in event.c close_anything_open() which is called from + send_sms, send_mail and exec_command in order to prevent file descriptor and + open sockets to be inherited by the shell causing freezing and instability. + Code contributed by Christophe Grenier, Christopher Price and Kenneth Lavrsen. + - Added new context global cnt_list.control_socket_server set by the httpd + thread so that the above mentioned close_anything_open() can close open + control sockets. + - Threw away the file descripter leak fix from snap 9 because it caused + more trouble than it fixed. Removed the close_anything_open() and the + cnt_list.control_socket_server field. Replaced it all with a simple + piece of code that all server daemons call when started: setsid() followed + by for (i=getdtablesize(); i>2; --i) close(i). Dirty and simple. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x03x21x070534 + - Fixed a bug where rate of fetching picture frames was disturned by + the signal SIG_CHLD from exec_command programs terminating. The symptom + was that the number of post_capture frames became inaccurate and motion + in mpegs did not have constant time between frames. + - Fixed a bug where motion did not work with gap=1. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x30x073616 + - Added the feature gap=0 which now also works. It disables gap completely + so that one single mpeg file is created. You can end the event from the + remote control interface make movie feature using for example cron. + This makes Motion close the mpeg and make a new with event number increased + by one. + - Improved the http remote control action features so that makemovie + and snapshot for thread 0 (all) works on all threads instead of being + ignored. + - Moved some code in the beginning of the motion_loop to a position later + to improve the accuracy of time calculations for the framerate. + - Improvements of motion.conf help comments including improvements in new + onxxxx options. + - Motion Guide refactored completely for 3.2.1 with better web navigation and + auto generation of pages. Makefile updated so that the Motion TWiki topic + MotionGuideOneLargeDocument is fetched when updating the guide and making + releases. + - Removed the debug_parameter option which had no use. Programmers can still + use it because the code is only commented out. This change required a small + update in the code that rewrites motion.conf so that a remote control command + to write the config files still adds a text header for the thread section at + the end of motion.conf. + - Changed the default values for a few options: quiet on, webcam_maxrate 1, + threshold_tune off, webcam_quality 50. + - Changed some cosmetics in the way motion.conf is written (space after #). + - Updated the motion-dist.conf to use default values unless there is a reason + not to. + - Fixed a bug in the low_cpu feature where cpu load increased instead of + decreasing because the framerate calculations were completely wrong. This was + an old bug introduced in 3.0.1. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x04x24x205933 + - Improved the auto-brightness algoritm. When auto-brightness is enabled + the brightness option becomes a target value for the brightness level. + This should also close a bug report. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x02x26x195358. + - Made the http control HTML responses nicer to look at as sources and + therefore easier to debug errors. + - Code style cleanup of webhttpd.c. + - Fixed compatibility problem with Palantir. Fixed by making output more + compatible with RFC (\r\n). Original fixes by Roberto Spadim and Angel + Carpintero. However this fix made Firefox flicker even more than it normally + does. Final fix which works in both Palantir client, Firefox and Cambozola + was made by Kenneth Lavrsen. This closes the following bugs: + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x02x205307, + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x07x042849 + - Added new conversion specifiers: %D (diffs), (noise) %K (motion center x), + %L (motion center y), %i (locate width x) and %J (locate width y). These + changes also required a refactoring of the alg_locate code. This change + is part of the implementation of a generic tracking feature and it enables + implementing external programs that can perform sinple prediction features. + http://www.lavrsen.dk/twiki/bin/view/Motion/ExtendReplaceConversionSpecifiersDiscussion + http://www.lavrsen.dk/twiki/bin/view/Motion/GenericTrackingPatch + - Fixed a bug in switchfilter which caused motion detection to not work + when the feature was enabled. + - Fix for Unknown content type with lumenera cameras + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x06x174416 + - Man page updated. It is now semi-autogenerated in the Motion TWiki + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionOptionsAlphabeticalManpage + - Added two new convertion specifiers: %o for threshold and %Q for number + of labels. + - Improved the config file description for pre_capture to get people to + use small values. + - Major code cleanup concerning signedness of chars all over the code to + allow compilation with gcc4.0 (like in Fedora Core 4) without any + errors or warnings. This will probably require that some of the not yet + included patches will have to be fixed because it it code all over the + place that has been changed. + - Changed the configure script so that /usr/lib64 is also searched for + the presense of ffmpeg (should fix the problem with 64 bit machines). + - Changed the configure script so that rpms can be made by normal non-root + users. + - Fixed a bug in the webhttpd code related to pan/tilt. Bug was introduced in + snap4 (Angel Carpintero, Kenneth Lavrsen). + - Changed all use of localtime to localtime_r which is threadsafe. + - Modified the WebcamCompressInMemory patch so that Motion now supports the + mjpeg webcam stream while being setup for saving PPM images. + http://www.lavrsen.dk/twiki/bin/view/Motion/WebcamCompressInMemory + - Major clean-up of code in picture.c and webcam.c so that function names and + variable names are less confusing. Also added many comments in picture.c. + - Webcam code commented more. + - New improved webcam feature. When you set webcam_motion on Motion will now + stream at 1 fps instead of none. When motion is detected the webcam stream + increases to the limit set in the config file. This change makes the + webcam_motion much more interesting. The previous function always ended up + with clients timing out. + - Small code cleanup in webcam.c and picture.c and .h for the webcam code + (Peter Holik and Kenneth Lavrsen) + - Small code cleanup in motion.c for the variable holding the number of + microseconds since epoch. The old code worked fine but relied on an integer + overflow every 71 minutes. (Bill Brack and Kenneth Lavrsen) + - Fixed bug related to disabled webcam or duplicate webcam port. Error log + accept(): Socket operation on non-socket continuously written to syslog. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x08x01x150922 + - Included a CODE_STANDARD text file to help new developers make patches + that are easier to integrate without too much manual editing. + - Changed the 5 second missed camera signal timeout to 30 seconds. + - Fixed bug where an extra jpeg is saved if you have output_normal=best + and you stop motion after an event has ended. (Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x08x05x173526 + - Option switch filter used print_int instead of print_bool when motion.conf + was saved. + - Removed all warnings originating from the motion sources when running + ./configure --with-developer-flags. + The modifications were done by the following people: Peter Holik, Bill Brack, + Angel Carpintero and Kenneth Lavrsen. + http://www.lavrsen.dk/twiki/bin/view/Motion/ReduceWarningsPatch + - Implemented Threadnr in TLS (thread-local storage)patch. It puts the thread + number into TLS and modifies motion_log() so that we do not have to drag the + cnt struct around just to be able to print the thread number in the log and + on the console. (Per Jönsson with additional removal of unuseded cnt by + Kenneth Lavrsen). + http://www.lavrsen.dk/twiki/bin/view/Motion/ThreadNrTlsPatch + - Removed old unused code related to read mode (not mmap) from V4L devices. + - Motion loop resets its frame timer when the image received is from a netcam. + This lowers the actual framerate of Motion to the rate the netcam can actually + keep up with. + - Last --with-developer-flags warnings eliminated simply by swapping the + order of the #include statements in the sources (Bill Brack and Kenneth Lavrsen). + - Switchfilter feature repaired. It was called inside motion_detected() + after overlays on cnt->img.out were added which meant that the feature also + detected all the overlays, smartmasks, fixed mask and text. It is now moved + to the motion_loop right after the lightswitch feature and before any + overlays are added. + - Fixed small bug where motion was detected when using a tracking camera and + the camera moved to center position when gap period expires. The fix includes + gathering the updating of reference frame in one place only in the motion_loop. + - Implemented the new text option text_event and new conversion specifier %C. + Option text_event defines the value %C which then can be used in filenames + and text_right/text_left. The text_event/%C uses the time stamp for the first + image detected in a new event. Default value is %Y%m%d%H%M%S. %C is an empty + string when no event is in progress (gap period expired). Pre_captured + and minimum_motion_frames images are time stamped before the event happens + so %C in text_left/right does not have any effect on those images. + http://www.lavrsen.dk/twiki/bin/view/Motion/EventConvertionSpecifierDiscussion + - Renamed some variables related to time to be better descriptive of function + and type. + - Added new option 'sql_user_text'. This can be defined with the same + conversion specifiers as text_xxx, on_xxxx and filenames. The SQL field + text_left has been removed and replaced by a field user_text which is + used for storing the interpreted value of sql_user_text. + - Added new SQL field event_time_stamp of the type TIMESTAMP. + - RPM specs file changed as suggested for use in the Livna repository. + - The lightswitch and switchfilter features have been moved up before the + despeckle features are run. This should ensure that both algoritms work on + raw unfiltered motion pixels which they both were designed for. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x10x05x212444 + - Added help texts in conf.c and motion-dist.conf describing the %t + specifier. Added a good example of use in motion-dist.conf. + - Added two new conversion specifiers: %f which is filename (full path) + and %n which is filetype (sqltype) valid in on_picture_save, on_movie_start, + on_movie_end and sql_query. This also means that filename is no longer + appended at the end of the 3 on_xxxx commands. (Kenneth Lavrsen) + - Removed the sql_user_text option that was added in snap 2 (Kenneth + Lavrsen) + - Added new sql_query option. This in combination with convertion + specifiers incl the two new %f and %n enables the user to use any database + structure they please. Added fields is now a simple matter of modifying + the sql query. The default is the same as the default in snap1. + (Kenneth Lavrsen). + - Change the sequence of events connected with creating files. Data is + now written to the databases (if used) before an external commens is + on (on_xxxx options) allowing the external program to use the new data + in the database (Kenneth Lavrsen) + - Added an infinite retry scheme for netcams that are not available + when Motion is started. Instead of just dying, Motion now retries every + 10 seconds until the netcam is available. Until the netcam is available + Motion enters the normal flow with the same grey image with a text + information being fed to webcam, timelapse, snapshots, vloopback etc. + Motion uses the width and height from the config file for this. It is + a good idea to setup width and height so it is the same as the netcam. + If the dimentions are the same Motion will switch over to the netcam + seemlessly. If the dimensions are different Motion will perform a quick + restart so all the many internal buffers can be initialized properly. + - Added a better error handling of a netcam that changes dimensions + while Motion is running. Instead of just writing error messages Motion + restarts quickly to recover from this change. Note the now more well + defined error coding for vid_next for both netcams and V4L cams. + - Fixed small bug where the displayed time in the grey error image + shown during start with unavailable netcam could show a garbage value + under rare circumstances. + - Restored the function sigchild_handler so it contains the same code + as before motion-3.2.1_snap9. They is done in an attempt to fix an old + problem with zombie child processes that has shown up again. + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x11x13x115016 + - Move the declaration of sig_handler_action and sigchild_action from + the setup_signals function where they are local and will be destroyed + and out in main just before setup_signals is called. Changed the + function setup_signals so the two structs are passed as pointers. + - Added new option track_auto which is a boolean option (on or off) + with default value off. This enable people to start Motion with auto + tracking enabled. Changing the config value for track_auto and + enabling the auto tracking via the httpd track/auto has the exact same + effect. + - Added 3 new tracking options: track_step_angle_x, track_step_angle_y, + and track_move_wait. The options track_step_angle control the movement + during auto tracking and are currently only active for the pwc type + tracking. The idea is that they can later also be used for the generic + tracking as it evolves. The track_move_wait controls the number of frames + after the camera has moved (auto or manual) during which motion detection + is disabled. This option should be set so low that the motion detection + is re-enabled the minute the camera is standing still again. Feature + originally made by Moshe Van Der Sterre. Kenneth Lavrsen extended it to + be more generic. + http://www.lavrsen.dk/twiki/bin/view/Motion/PwcConfiguration + +Mike Lees + - Added the onffmpegclose feature. + - Fixed a serious stability issue related to syslog not being a fully + re-entrant function. + - Implemented the new brightness, contrast, hue, saturation features + http://www.lavrsen.dk/twiki/bin/view/Motion/BrightnessContrastPatch + +Bill Maidment + - Fixed bug reporting errors when creating symlink to last snap. + +Philip Marien + - Fixed a problem when compiling with --without-v4l configuration. + +Lionnel Maugis + - ffmpeg code + +Andrew McCarthy + - Added the netcam functionality to the original axis code. + +Ian McConnell + - Fixed the problem with Netcams and mask files. + - New despeckle feature. + - Flexible on screen display feature based on strftime. + - Flexible strftime based path names. + - Fixed problem with snapshot names when name is lastsnap. + - Fix for snapshots when using the "lastsnap" filename. + - Provided "timelapse closes mpeg file when set to zero" feature. + +Randy McEoin + - For adding the onmpeg command. + +Marcel J.E. Mol + - new show.cgi and genhtml.sh, -a without alarm, both motion + and normal images, various improvements and ideas + +Sean Murphy + - Executing external commands + +nemosoft + - For his differential view in the camstream program. + It inspired me (Jeroen) to make this. And for a great program to + test my video loopback support. (www.smcc.demon.nl/camstream/) + +nullset? + - For the ir script to turn motion and lights on and of + +Mikko Paananen + - Changed netcam open to use pipes and fixed authentication. + +Asbjørn Pettersen + - Netcam_ftp code fixes (Angel Carpintero and Asbjørn Pettersen) + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamWithFtpEnhancements + +Pawel Pierscionek + - Signal blocking during ioctls. + - Greyscale blowup optimization + +Philippe Possemiers + - For fixing the bug that prevented external commands, mail + and sms from being called at the first event. + - And for writing the send_jpg.py script. + +Alan Post + - Pointed me to the exit(-1) instead of exit(1) calls. + +Christopher Price + - Implemented Streaming Netcam Without Curl which enables connecting to + network cameras both with single jpeg frame mode and streaming mjpeg + mode. This enables much higher framerates with Netcams. (with Angel + Carpintero). + http://www.lavrsen.dk/twiki/bin/view/Motion/StreamingNetcamWithoutCurl + - Netcam fixes and debug code by Christopher Price + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + snap5_post1_video.c and snap5-post1 patches + - Fixed netcam startup race condition. + - Refactored image handling back to single unified function + - Refactored reconnection algorithm + - Jpeg only based connections should now use less cpu time + - Temporarily removed support for devices that do not support + content-length (in progress) + - Synced syslog/printf style to new motion standard + - Added developer debug trace defines/code + - Defines now used for many constants + Netcam Stability Patch version snap6-post1 + - Added support for netcams without content-length header (streaming only) + - Remove memmem from netcam_wget.[c|h] (no longer used) + - Several miscellaneous code cosmetic changes + Netcam Stabilty Patch version 3.2.1-snap7-post1 + - Added support for non-streaming (image based) netcams without + content-lengthheader. + - Contributed code for new function in event.c close_anything_open() + which is called from send_sms, send_mail and exec_command in order to + prevent file descriptor and open sockets to be inherited by the shell + causing freezing and instability. Code contributed by Christophe Grenier, + Christopher Price and Kenneth Lavrsen. + - More Netcam Stability Fixes (snap10-post1-6) + - Destroy mutexes in netcam_cleanup(). + - Add reconnection for netcam_start() - this may block other cameras + from starting up!. + - Added additional defines for reconnect retries. + - Change reconnection timeouts to 60 seconds. + - Reworked close(sock) in netcam_connect, to insure future changes + won't forget to close the socket. + - Reworked reconnection for netcam_start() - disabled by default, see + source for INIT_RECONNECT_RETRIES. + - Break some long lines in code. + - Replaced sleep with nanosleep per suggestion by Kenneth Lavrsen. + - Added additional header validation check. + - Changed a couple fd references to use RBUF_FD. + - Added error message if jpeglib error occurs. + - Removed additional header validation check. + - Limited times headers will be checked. + - Removed mutex lock around netcam_start() in video.c, hopefully race + conditions are fixed. + - Added additional headers in http request. + - Added back header validation (should fix netcam_read_header lockups). + - Detect when there is no data on socket in netcam_read_ functions + (should fix netcam_read_image_contentlength() and + netcam_read_image_no_contentlength() lockups). + - Rearranged timeout assignments for pthread_cond_timedwait() calls. + - Adjusted TIMEOUT_COND_WHICH to 4 seconds. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + - More Netcam Stability Fixes (snap11-post1-4) (Christopher Price) + - Reworked thread signal/wait conditions, should fix some race conditions. + - Use gettimeofday() to determine thread timeouts, results in better accuracy. + - Adjusted condition timeouts to smaller values due to usage of gettimeofday() + and rework of thread signal/wait conditions. + - Adjusted reconnection retries to 60 (every minute for an hour). + - Fix bug where motion will not quit if requested when reconnecting. + - Cruft, feature creep and redudant code removed. + - Consolated reconnection capability to unified netcam_reconnect function. + - Rework netcam_start logic, minimize startup variables. + - Rework netcam_stream_read and netcam_single_read logic. + - Minor changes to netcam_next logic. + - Fix bug in streaming camera without content-length, recent mod broke. + - Fix bug in startup of single image reads without content-length. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + - More Netcam Stability Fixes (snap12-post1) (Christopher Price) + - Newrote url parser, better syntax checking and error handling of urls. + - Userpass now allowed in url (http://user:pass@example.com/). + Netcam_userpass has precedence, it will override a userpass embedded in + the url. + http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch + +Dietz Proepper + - Always output diff count and output image type selection + +rasca + - A lot of the code in motion.c comes from his vidcat program + which is part of the w3cam package. (jpeg creation and parts + of the image capture function) + (www.hdk-berlin.de/~rasca/w3cam/) + +Michael Reuschling + - Fixed bug which caused Motion 3.1.18 fail to save timelapse mpegs when + setting ffmpeg_timelapse = 1 (fixed by Michael Reuschling) + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x01x31x211756 + +James A. Russo. + - Implemented ffmpeg_timelapse_mode feature. + - Implemented enhanced SQL features. This adds logging of mpeg + and prediction events to the MySQL/PostgreSQL feature. + - Replaced the mime file types by a more refined filetype + scheme that allows more refined control for SQL and other + future control. + +Petter Reinholdtsen + - Adding the install option to the makefile. + +Roberto Spadim + - Fixed compatibility problem with Palantir. Fixed by making output more + compatible with RFC (\r\n). Original fixes by Roberto Spadim and Angel + Carpintero. However this fix made Firefox flicker even more than it normally + does. Final fix which works in both Palantir client, Firefox and Cambozola + was made by Kenneth Lavrsen. This closes the following bugs: + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x02x205307, + http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x05x07x042849 + +Daniel Sterling + - Studies of the performance of Motion. + - Night compensation fix. + - Changed the webcam_port value to 0 in motion-dist.conf to avoid that + people get segmentation faults when having 2 or more cameras and + webcam_port not set in the thread config files. + - Implemented improved handling of config strings. This plugs the memory + leak when changing string type options via XML-RPC. It also makes the + memory handling more elegant/optimal and finally it now allows strings + to be as long as allowed by the environment variable PATH_MAX. + - Kenneth and Daniel added more comments to motion.c and conf.c. + - Made the XMLRPC able to handle errors without crashing. + - Daniel Sterling and Kenneth Lavrsen added a feature that checks for + two threads having the same webcam_port. If this is the case the last + thread gets its webcam disabled and a warning message is written + to console and syslog. + - Fixed a calculation error in alg_diff_fast(). + +Tommy Svensson + - Wrote the original patch for supporting pan/tilt with Logitech + Quickcam Sphere/Orbit + +technolust.cx + - For hosting motion.technolust.cx + +Mark Thomas + - Created the original thread patch for motion enabling motion to + watch multiple cameras. + +Dirk Traenapp + - Added the mpeg creation on exit and SIGUSR1, also made the start + for max_mpeg_time. + - Found the 'strtok' call that caused motion to crash under RH7.0 + +Moshe Van Der Sterre + - Added 3 new tracking options: track_step_angle_x, track_step_angle_y, + and track_move_wait. The options track_step_angle control the movement + during auto tracking and are currently only active for the pwc type + tracking. The idea is that they can later also be used for the generic + tracking as it evolves. The track_move_wait controls the number of frames + after the camera has moved (auto or manual) during which motion detection + is disabled. This option should be set so low that the motion detection + is re-enabled the minute the camera is standing still again. Feature + originally made by Moshe Van Der Sterre. Kenneth Lavrsen extended it to + be more generic. + http://www.lavrsen.dk/twiki/bin/view/Motion/PwcConfiguration + +Sean Watkins + - Created a centralized logging function that became event() + +Joerg Weber + - Added the new labeling motion detection feature. + - Added the new Smartmask feature. + - Implemented new preview patch (Joerg Weber) + http://www.lavrsen.dk/twiki/bin/view/Motion/PreviewShotsPatch + - Implemented an improvement of Smartmask so that the mask is cleared when + the smart_mask_speed is set from a non-zero to zero + - Implemented an improvement of noise_tune with smart mask (and probably + also in general) + - Added Best Preview Patch + http://www.lavrsen.dk/twiki/bin/view/Motion/BestPreviewShot + - Added the new feature Setup Mode (Joerg Weber). This also enables + much more error messages given to the console when in non-daemon mode + while still preserving the messages in syslog which are important + for daemon mode debugging. + http://www.lavrsen.dk/twiki/bin/view/Motion/SetupModePatch + - Fixed a bug in noise tune which was most visible at very low light. + - Improved console output in setup mode. Now also outputs threshold. + - Improvement in the noise-tune algoritm. + - Implemented new Generic onxxxx features. + Function --- Old Option --- New Option + Start of event (first motion) --- execute --- on_event_start + End of event (no motion for gap seconds) --- New! --- on_event_end + Picture saved (jpg or ppm) --- onsave --- on_picture_save + Movie starts (mpeg file opened) --- onmpeg --- on_movie_start + Movie ends (mpeg file closed) --- onffmpegclose --- on_movie_end + Motion detected --- New! --- on_motion_detected + http://www.lavrsen.dk/twiki/bin/view/Motion/OnXxxCommandsPatch and + http://www.lavrsen.dk/twiki/bin/view/Motion/OnXxxxFeatureDiscussion + - Fixed small bug when pre_capture buffer is resized during operation. + - Changed the order of drawing the red mask in setup mode so that the + smartmask is drawn after the fixed mask. + - Improved the display of fixed mask. It is now shown as grey instead of + red. This makes it easier to see the smart mask working when you also have + a fixed mask. + - Improved the labelling algoritm so that locate feature and tracking + features includes all labelled areas above threshold. + http://www.lavrsen.dk/twiki/bin/view/Motion/ImprovedLabellingPatch + + +Tristan Willy + - Wrote Axis 2100 support and added the check for ~/.motion/motin.conf + +Folkert Van Heusden + - Maintained the code from version 3.1.9 till 3.1.12-rc1 + Including features like.. + - Error reporting to syslog. + - Better memory allocation. + low_cpu feature extention to configurable frame rate. + - First work on Logitech Sphere/Orbit tracking. + - Implemented original pre-record feature. + Misc code optimisations + - Closed 2 memory-leaks (two 'FILE *' were not closed) in the + webcam-interface. + +Robert Eugene Wood + - Inverse pixels for locate box. + +Yieldtech + - These guys are making a complete linux based security system + with motion as one of its components. + - They did the mysql support, new fileformat and the symbolic + link to the snapshots. You can find them at yieldtech.cz + +Christian W. Zuckschwerdt + - Modified the Makefile and configure files to be more flexible. + + + +Everybody who has contributed ideas, bugreport and remarks to this project diff --git a/FAQ b/FAQ new file mode 100644 index 0000000..f4cc8ff --- /dev/null +++ b/FAQ @@ -0,0 +1,24 @@ +This FAQ is no longer kept up to date +Look at this URL for a more up to date wiki based FAQ +http://www.lavrsen.dk/twiki/bin/view/Motion/FrequentlyAskedQuestions + +Q: motion crashes while parsing the config file. + +A: Appearantly the behaviour of strtok in glibc has changed somewhat. + This problem should be fixed as of 2.3 + + +Q: Were does motion look for the config file? + +A: First it will look for 'motion.conf' in the current directory, next it will + try to find '.motion/motion.conf' in your home directory (pointed to by + the HOME environment variable). If these don't exist it will try to open + '/usr/local/etc/motion.conf' if you specified /usr/local as the prefix to + configure (this is the default). + +Q: What codingstyle is used for motion? + +A: Motion follows the same style as the linux kernel. + Read Documentation/Codingstyle in the kernel tree for some good reasons. + Also be aware that motion is multithreaded and as such all functions should + be reentrant, using static variables is usually a bad idea. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..5063740 --- /dev/null +++ b/INSTALL @@ -0,0 +1,16 @@ +Very simple: + +type: + + ./configure + +followed by: + + make + +And + + make install + + +Next read the README diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..82f53ae --- /dev/null +++ b/Makefile.in @@ -0,0 +1,209 @@ +################################################################################ +# Makefile for Motion # +################################################################################ +# Copyright 2000 by Jeroen Vreeken # +# # +# This program is published under the GNU public license version 2.0 or later. # +# Please read the file COPYING for more info. # +################################################################################ +# Please visit the Motion home page: # +# http://www.lavrsen.dk/twiki/bin/view/Motion # +################################################################################ + +CC = @CC@ +INSTALL = install +VERSION = @VERSION@ + +################################################################################ +# Install locations, controlled by setting configure flags. # +################################################################################ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +mandir = @mandir@ +sysconfdir = @sysconfdir@ +datadir = @datadir@ +docdir = $(datadir)/doc/motion-$(VERSION) +examplesdir = $(docdir)/examples + +################################################################################ +# These variables contain compiler flags, object files to build and files to # +# install. # +################################################################################ +CFLAGS = @CFLAGS@ -Wall -DVERSION=\"$(VERSION)\" -D_REENTRANT \ + -Dsysconfdir=\"$(sysconfdir)\" +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +VIDEO_OBJ = @VIDEO@ +OBJ = motion.o conf.o draw.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 \ + webcam.o @FFMPEG_OBJ@ +SRC = $(OBJ:.o=.c) +DOC = CHANGELOG COPYING CREDITS INSTALL README motion_guide.html +EXAMPLES = *.conf motion.init-Debian motion.init-RH motion.init-FreeBSD.sh +PROGS = motion +DEPEND_FILE = .depend + +################################################################################ +# ALL and PROGS build Motion and, possibly, Motion-control. # +################################################################################ +all: progs + @echo "Build complete, run \"make install\" to install Motion!" + @echo + +progs: pre-build-info $(PROGS) + +################################################################################ +# PRE-BUILD-INFO outputs some general info before the build process starts. # +################################################################################ +pre-build-info: + @echo "Welcome to the setup procedure for Motion, the motion detection daemon! If you get" + @echo "error messages during this procedure, please report them to the mailing list. The" + @echo "Motion Guide contains all information you should need to get Motion up and running." + @echo "Run \"make updateguide\" to download the latest version of the Motion Guide." + @echo + @echo "Version: $(VERSION)" +ifneq (,$(findstring freebsd,$(VIDEO_OBJ))) + @echo "Platform: FreeBSD" +else + @echo "Platform: Linux (if this is incorrect, please read README.FreeBSD)" +endif + @echo + +################################################################################ +# MOTION builds motion. MOTION-OBJECTS and PRE-MOBJECT-INFO are helpers. # +################################################################################ +motion: motion-objects + @echo "Linking Motion..." + @echo "--------------------------------------------------------------------------------" + $(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + @echo "--------------------------------------------------------------------------------" + @echo "Motion has been linked." + @echo + +motion-objects: dep pre-mobject-info $(OBJ) + @echo "--------------------------------------------------------------------------------" + @echo "Motion object files compiled." + @echo + +pre-mobject-info: + @echo "Compiling Motion object files..." + @echo "--------------------------------------------------------------------------------" + +################################################################################ +# Define the compile command for C files. # +################################################################################ +#%.o: %.c +# @echo -e "\tCompiling $< into $@..." +# @$(CC) -c $(CFLAGS) $< -o $@ + +################################################################################ +# Include the dependency file if it exists. # +################################################################################ +ifeq ($(DEPEND_FILE), $(wildcard $(DEPEND_FILE))) +ifeq (,$(findstring clean,$(MAKECMDGOALS))) +-include $(DEPEND_FILE) +endif +endif + +################################################################################ +# Make the dependency file depend on all header files and all relevant source # +# files. This forces the file to be re-generated if the source/header files # +# change. Note, however, that the existing version will be included before # +# re-generation. # +################################################################################ +$(DEPEND_FILE): *.h $(SRC) + @echo "Generating dependencies, please wait..." + @$(CC) $(CFLAGS) -M $(SRC) > .tmp + @mv -f .tmp $(DEPEND_FILE) + @echo + +################################################################################ +# DEP, DEPEND and FASTDEP generate the dependency file. # +################################################################################ +dep depend fastdep: $(DEPEND_FILE) + +################################################################################ +# INSTALL installs all relevant files. # +################################################################################ +install: + @echo "Installing files..." + @echo "--------------------------------------------------------------------------------" + mkdir -p $(DESTDIR)$(bindir) + mkdir -p $(DESTDIR)$(mandir)/man1 + mkdir -p $(DESTDIR)$(sysconfdir) + mkdir -p $(DESTDIR)$(docdir) + mkdir -p $(DESTDIR)$(examplesdir) + $(INSTALL) motion.1 $(DESTDIR)$(mandir)/man1 + $(INSTALL) $(DOC) $(DESTDIR)$(docdir) + $(INSTALL) $(EXAMPLES) $(DESTDIR)$(examplesdir) + $(INSTALL) motion-dist.conf $(DESTDIR)$(sysconfdir) + for prog in $(PROGS); \ + do \ + ($(INSTALL) $$prog $(DESTDIR)$(bindir) ); \ + done + @echo "--------------------------------------------------------------------------------" + @echo "Install complete! The default configuration file, motion-dist.conf, has been" + @echo "installed to $(sysconfdir). You need to rename/copy it to $(sysconfdir)/motion.conf" + @echo "for Motion to find it. More configuration examples as well as init scripts" + @echo "can be found in $(examplesdir)." + @echo + +################################################################################ +# UNINSTALL and REMOVE uninstall already installed files. # +################################################################################ +uninstall remove: pre-build-info + @echo "Uninstalling files..." + @echo "--------------------------------------------------------------------------------" + for prog in $(PROGS); \ + do \ + ($ rm -f $(bindir)/$$prog ); \ + done + rm -f $(mandir)/man1/motion.1 + rm -f $(sysconfdir)/motion-dist.conf + rm -rf $(docdir) + @echo "--------------------------------------------------------------------------------" + @echo "Uninstall complete!" + @echo + +################################################################################ +# CLEAN is basic cleaning; removes object files and executables, but does not # +# remove files generated from the configure step. # +################################################################################ +clean: pre-build-info + @echo "Removing compiled files and binaries..." + @rm -f *~ *.jpg *.o $(PROGS) combine $(DEPEND_FILE) + +################################################################################ +# DIST restores the directory to distribution state. # +################################################################################ +dist: distclean updateguide + @chmod -R 644 * + @chmod 755 configure + @chmod 755 debian + @chmod 755 debian/rules + +################################################################################ +# DISTCLEAN removes all files generated during the configure step in addition # +# to basic cleaning. # +################################################################################ +distclean: clean + @echo "Removing files generated by configure..." + @rm -f config.status config.log config.cache Makefile motion.init-RH motion.init-Debian motion.init-FreeBSD.sh + @rm -rf autom4te.cache + @echo "You will need to re-run configure if you want to build Motion." + @echo + +################################################################################ +# UPDATEGUIDE downloads the Motion Guide from TWiki. # +################################################################################ +updateguide: pre-build-info + @echo "Downloading Motion Guide. If it fails, please check your Internet connection." + @echo + wget www.lavrsen.dk/twiki/bin/view/Motion/MotionGuideOneLargeDocument?skin=text -O motion_guide.tmp + @echo "Cleaning up and fixing links..." + @cat motion_guide.tmp | sed -e 's/\?CGISESSID=[0-9a-fA-F]*//g;s,"/twiki/,"http://www.lavrsen.dk/twiki/,g' > motion_guide.html + @rm -f motion_guide.tmp + @echo "All done, you should now have an up-to-date local copy of the Motion guide." + @echo diff --git a/README b/README new file mode 100644 index 0000000..5766ef6 --- /dev/null +++ b/README @@ -0,0 +1,65 @@ +Motion up to version 3.1.8 was created and maintained by Jeroen Vreeken. +Versions 3.1.9-3.1.12 was maintained by Kenneth Lavrsen (http://www.lavrsen.dk/) +and Folkert van Heusden (http://www.vanheusden.com/). + +From 3.1.12 Motion is project managed by Kenneth Lavrsen and maintained by +a large number of people. + +See the file CREDITS for the list of people having made contributions to the +Motion code. + +Copyright 2000-2005 by Jeroen Vreeken/Folkert Van Heusden/Kenneth Lavrsen +and the contributors mentioned in the file CREDITS. + +The source file netcam_wget.c and netcam_wget.h contains code reused from +GNU Wget. It has been merged and modified for use in the program Motion which +is also released under the terms of the GNU General Public License. +Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002 +Free Software Foundation, Inc. +The code contains additional modifications +Additional Copyright (C) 2004-2005 Christopher Price, +Angel Carpintero, and other contributing authors. + +GNU Wget and Motion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published +by the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GNU Wget and Motion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Wget; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +Installing: + +Read the motion.1 manpage for more info + +And most of all read the Motion Guide for very detailed description of both +installation and use. +The Motion Guide is part of the distribution (motion_guide.htm). +You are encouraged to look up an up to date version by visiting the Motion +homepage at +http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome +and specifically the Motion Guide at +http://www.lavrsen.dk/twiki/bin/view/Motion/MotionGuide + + +Support: + +Lots of resources at http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome +Please join the mailing list +http://www.lavrsen.dk/twiki/bin/view/Motion/MailingList +Newbies and silly questions are welcome. We prefer support through the mailing +list because more will have benefit from the answers. + + +Jeroen Vreeken +pe1rxq@amsat.org + +Kenneth Lavrsen +kenneth@lavrsen.dk diff --git a/README.FreeBSD b/README.FreeBSD new file mode 100644 index 0000000..8fdbeeb --- /dev/null +++ b/README.FreeBSD @@ -0,0 +1,75 @@ +The motion port to *BSD is still in very beta state, but +has been tested with network cameras support successfully. +This version has been tested with TV Card ( Miro PCTV pro ), +but should work by any framegrabber supported by bktr driver. + +* How configure your capture card * + +1-. Load the bktr modules +type as root : + +kldload bktr_mem.ko +kldload bktr.ko + +You can setup this in your /boot/loader.conf adding : + +bktr_mem_load="YES" +bktr_load="YES" + +2-. Configure your card settings ( tuner type , format and card ). + +Type as root : + +sysctl hw.bt848.card=1 ( Miro pctv ) +sysctl hw.bt848.tuner=10 ( PHILIPS_FR1216_PAL ) +sysctl hw.bt848.format=0 ( PAL ) + +or add to /etc/sysctl.conf adding : + +hw.bt848.card=1 +hw.bt848.tuner=10 +hw.bt848.format=0 + +* To compile motion in FreeBSD ( should work in OpenBSD and NetBSD ) : + + autoconf + ./configure + of + CC=gcc34 ./configure ( to override the default gcc version) + gmake + gmake install + +( ./configure --without-bktr , to work only with network cameras ). + +Tested in FreeBSD 4.9/4.1x/5.x . + +Packages needed (dependencies for 5.4): + + - autoconf-2.59_2 + - gcc-3.4.6_20051206 + - m4-1.4.4 + - linuxthreads-2.2.3_19 + - jpeg-6b_3 or above + - gmake-3.80_2 ( GNU Make ) + - ffmpeg-0.4.9.p1_4 ( mpeg1/4 video encoders ). + - mysql-server , mysql-client ( database backend support ). + - postgresql-devel , postgresql ( database backend support ). + + - kernel with bktr support ( GENERIC kernel has by default support ). + + + +* TODO * +-------- + +- Improve the capture method ( double buffer , async capture ). +- Detect pixelformat , palettes format , etc ... +- Implement tuner capture ( now only video / svideo capture implemented ). +- Code clean up. +- And many others ... + + Any question / fix / suggestion please send it to motion mailing list. + + + Angel Carpintero + ack@telefonica.net diff --git a/README.MacOSX b/README.MacOSX new file mode 100644 index 0000000..30af16c --- /dev/null +++ b/README.MacOSX @@ -0,0 +1,33 @@ + The motion port to MacOSX is only working with network cameras. + + Tested in lastest MacOSX + + Packages needed : + + * autoconf-2.57 or above. + * make-3.79 or above. + * This port has been made using some packages from Fink and DarwinPorts : + o ffmpeg-0.4.8 from DarwinPorts ( but it needs to be patched with ffmpeg-0.4.8-macosx.patch ). + o libjpeg-6b-16, libjpeg-shlibs-6b-16 from Fink + + How to Install + + * Get/Patch/Install ffmpeg + o Download the ffmpeg from sourceforge ( the same version used in Darwinports ). + o Get the patch-ffmpeg + + tar xfvz ffmpeg-0.4.8.tar.gz + cd ffmpeg-0.4.8/ + patch -p1 < ../ffmpeg-0.4.8-macosx.patch + ./configure --prefix=/path_to_install_ffmpeg ; make ; make install ; make installlib + ldconfig + + To install motion follow the standard procedure : + + ./configure --with-ffmpeg=/path_where_you_installed_ffmpeg ; make ; make install + + Any question / fix / suggestion please send it to motion mailing list. + + + Angel Carpintero + ack@telefonica.net diff --git a/README.axis_2100 b/README.axis_2100 new file mode 100644 index 0000000..d03258a --- /dev/null +++ b/README.axis_2100 @@ -0,0 +1,21 @@ +Using motion with the Axis 2100 network camera + +First compile the motion binary as described in README and INSTALL +Then either start motion with the -A option and your axis cameras ip address or +edit motion.conf. + + To increase the performance use this urls : + + # for multipart jpeg + http://192.168.1.10/axis-cgi/mjpg/video.cgi?showlength=1 + + or + + # for single jpeg + http://192.168.1.10/axis-cgi/jpg/image.cgi?showlength=1 + +There are only two valid picture sizes for the axis camera: 320x240 and 640x480 + +TODO: + + - Make the IP changeable at run-time. diff --git a/alg.c b/alg.c new file mode 100644 index 0000000..01aff1f --- /dev/null +++ b/alg.c @@ -0,0 +1,1068 @@ +/* alg.c + * + * Detect changes in a video stream. + * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ +#include "motion.h" + +#ifdef __freebsd__ +#include "video_freebsd.h" +#else +#include "video.h" +#endif /* __freebsd__ */ + +#include "conf.h" +#include "alg.h" +#include "event.h" + +#ifdef __MMX__ +#define HAVE_MMX +#include "mmx.h" +#endif + +#define MAX2(x, y) ((x) > (y) ? (x) : (y)) +#define MAX3(x, y, z) ((x) > (y) ? ((x) > (z) ? (x) : (z)) : ((y) > (z) ? (y) : (z))) + +/* locate the center and size of the movement. */ +void alg_locate_center_size(struct images *imgs, int width, int height, struct coord *cent) +{ + unsigned char *out=imgs->out; + int *labels=imgs->labels; + int x, y, centc=0, xdist=0, ydist=0; + + cent->x=0; + cent->y=0; + cent->maxx=0; + cent->maxy=0; + cent->minx=width; + cent->miny=height; + + /* If Labeling enabled - locate center of largest labelgroup */ + if (imgs->labelsize_max) { + /* Locate largest labelgroup */ + for (y=0; yx += x; + cent->y += y; + centc++; + } + } + } + } else { + /* Locate movement */ + for (y=0; yx += x; + cent->y += y; + centc++; + } + } + } + } + if (centc) { + cent->x=cent->x/centc; + cent->y=cent->y/centc; + } + + /* Now we find the size of the Motion */ + + /* First reset pointers back to initial value */ + centc=0; + labels=imgs->labels; + out=imgs->out; + + /* If Labeling then we find the area around largest labelgroup instead */ + if (imgs->labelsize_max) { + for (y=0; y cent->x) + xdist += x - cent->x; + else if (x < cent->x) + xdist += cent->x - x; + if (y > cent->y) + ydist += y - cent->y; + else if (y < cent->y) + ydist += cent->y - y; + centc++; + } + } + } + } else { + for (y=0; y cent->x) + xdist += x - cent->x; + else if (x < cent->x) + xdist += cent->x - x; + if (y > cent->y) + ydist += y - cent->y; + else if (y < cent->y) + ydist += cent->y - y; + centc++; + } + } + } + } + + if (centc) { + cent->minx = cent->x - xdist/centc*2; + cent->maxx = cent->x + xdist/centc*2; + /* Make the box a litle bigger in y direction to make sure the + heads fit in so we multiply by 3 instead of 2 which seems to + to work well in practical */ + cent->miny = cent->y - ydist/centc*3; + cent->maxy = cent->y + ydist/centc*2; + } + if (cent->maxx > width - 1) + cent->maxx = width - 1; + else if (cent->maxx < 0) + cent->maxx = 0; + if (cent->maxy > height - 1) + cent->maxy = height - 1; + else if (cent->maxy < 0) + cent->maxy = 0; + if (cent->minx > width - 1) + cent->minx = width - 1; + else if (cent->minx < 0) + cent->minx = 0; + if (cent->miny > height - 1) + cent->miny = height - 1; + else if (cent->miny < 0) + cent->miny = 0; + + cent->width = cent->maxx - cent->minx; + cent->height = cent->maxy - cent->miny; + + /* We want to center Y coordinate to be the center of the action. + The head of a person is important so we correct the cent.y coordinate + to match the correction to include a persons head that we just did above */ + cent->y = (cent->miny + cent->maxy)/2; + +} + + +/* draw a box around the movement */ +void alg_draw_location(struct coord *cent, struct images *imgs, int width, unsigned char *new, int mode) +{ + unsigned char *out=imgs->out; + int x, y; + + out=imgs->out; + + /* Draw a box around the movement */ + if (mode == LOCATE_BOTH){ /* both normal and motion image gets a box */ + int width_miny = width*cent->miny; + int width_maxy = width*cent->maxy; + for (x=cent->minx; x<=cent->maxx; x++) { + int width_miny_x = x+width_miny; + int width_maxy_x = x+width_maxy; + new[width_miny_x]=~new[width_miny_x]; + new[width_maxy_x]=~new[width_maxy_x]; + out[width_miny_x]=~out[width_miny_x]; + out[width_maxy_x]=~out[width_maxy_x]; + } + for (y=cent->miny; y<=cent->maxy; y++) { + int width_minx_y = cent->minx+y*width; + int width_maxx_y = cent->maxx+y*width; + new[width_minx_y]=~new[width_minx_y]; + new[width_maxx_y]=~new[width_maxx_y]; + out[width_minx_y]=~out[width_minx_y]; + out[width_maxx_y]=~out[width_maxx_y]; + } + } + else{ /* normal image only (e.g. preview shot) */ + int width_miny = width*cent->miny; + int width_maxy = width*cent->maxy; + for (x=cent->minx; x<=cent->maxx; x++) { + int width_miny_x = width_miny+x; + int width_maxy_x = width_maxy+x; + new[width_miny_x]=~new[width_miny_x]; + new[width_maxy_x]=~new[width_maxy_x]; + } + for (y=cent->miny; y<=cent->maxy; y++) { + int minx_y = cent->minx+y*width; + int maxx_y = cent->maxx+y*width; + new[minx_y]=~new[minx_y]; + new[maxx_y]=~new[maxx_y]; + } + } +} + + + +#define NORM 100 +#define ABS(x) ((x)<0 ? -(x) : (x)) +#define DIFF(x, y) (ABS((x)-(y))) +#define NDIFF(x, y) (ABS(x)*NORM/(ABS(x)+2*DIFF(x,y))) + + +void alg_noise_tune (struct context *cnt, unsigned char *new) +{ + struct images *imgs=&cnt->imgs; + int i; + unsigned char *ref=imgs->ref; + int diff, sum=0, count=0; + unsigned char *mask=imgs->mask; + unsigned char *smartmask=imgs->smartmask_final; + + i=imgs->motionsize; + + for (; i>0; i--) { + diff = *ref - *new; + + if (mask) + diff = ((diff * *mask++)/255); + + if (*smartmask){ + sum += ABS(diff) + 1; + count++; + } + + ref++; + new++; + smartmask++; + } + + if (count > 4) /* avoid divide by zero */ + sum /= count/4; + + cnt->noise = 4 + (cnt->noise+sum)/2; +} + +void alg_threshold_tune(struct context *cnt, int diffs, int motion) +{ + int i; + int sum = 0, top = diffs; + + if (!diffs) + return; + + if (motion) + diffs = cnt->threshold / 4; + + for (i = 0; i < THRESHOLD_TUNE_LENGTH - 1; i++) + { + sum += cnt->diffs_last[i]; + if (cnt->diffs_last[i+1] && !motion) + cnt->diffs_last[i] = cnt->diffs_last[i+1]; + else + cnt->diffs_last[i] = cnt->threshold / 4; + if (cnt->diffs_last[i] > top) + top = cnt->diffs_last[i]; + } + sum += cnt->diffs_last[i]; + cnt->diffs_last[i] = diffs; + + sum /= THRESHOLD_TUNE_LENGTH / 4; + if (sum < top * 2) + sum = top * 2; + if (sum < cnt->conf.max_changes) + cnt->threshold = (cnt->threshold + sum) / 2; +} + +/* +Labeling by Joerg Weber. Based on an idea from Hubert Mara. +Floodfill enhanced by Ian McConnel based on code from +http://www.acm.org/pubs/tog/GraphicsGems/ +http://www.codeproject.com/gdi/QuickFill.asp +*/ + +/* + * Filled horizontal segment of scanline y for xl<=x<=xr. + * Parent segment was on line y-dy. dy=1 or -1 + */ + +#define MAXS 10000 /* max depth of stack */ + +#define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \ + if (sp= 0 && Y+(DY) < height) \ + {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;} + +#define POP(Y, XL, XR, DY) /* pop segment off stack */ \ + {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;} + +typedef struct {short y, xl, xr, dy;} Segment; + + +static int iflood(int x, int y, + int width, int height, unsigned char *out, int *labels, int newvalue, int oldvalue) +{ + int l, x1, x2, dy; + Segment stack[MAXS], *sp = stack; /* stack of filled segments */ + int count = 0; + + if (x < 0 || x >= width || y < 0 || y >= height) + return 0; + + PUSH(y, x, x, 1); /* needed in some cases */ + PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */ + + while (sp > stack) { + /* pop segment off stack and fill a neighboring scan line */ + POP(y, x1, x2, dy); + /* + * segment of scan line y-dy for x1<=x<=x2 was previously filled, + * now explore adjacent pixels in scan line y + */ + for (x = x1; x >= 0 && out[y*width+x] != 0 && labels[y*width+x] == oldvalue; x--) { + labels[y*width+x] = newvalue; + count++; + } + + if (x >= x1) + goto skip; + + l = x + 1; + + if (l < x1) + PUSH(y, l, x1-1, -dy); /* leak on left? */ + + x = x1 + 1; + + do { + for (; x < width && out[y*width+x] != 0 && labels[y*width+x]==oldvalue; x++) { + labels[y*width+x] = newvalue; + count++; + } + + PUSH(y, l, x-1, dy); + + if (x > x2+1) + PUSH(y, x2+1, x-1, -dy); /* leak on right? */ + + skip: + + for (x++; x <= x2 && !(out[y*width+x] != 0 && labels[y*width+x]==oldvalue); x++); + + l = x; + } while (x <= x2); + } + return count; +} + +static int alg_labeling (struct context *cnt) +{ + struct images *imgs=&cnt->imgs; + unsigned char *out=imgs->out; + int *labels=imgs->labels; + int ix, iy, pixelpos; + int width=imgs->width; + int height=imgs->height; + int labelsize=0; + int current_label=2; + imgs->total_labels=0; + imgs->labelsize_max=0; + /* ALL labels above threshold are counted as labelgroup */ + imgs->labelgroup_max=0; + imgs->labels_above=0; + + /* init: 0 means no label set / not checked */ + memset(labels, 0, width*height*sizeof(int)); + pixelpos = 0; + for( iy=0; iy 0) + continue; + labelsize=iflood(ix, iy, width, height, out, labels, current_label, 0); + + if( labelsize > 0 ) { + //printf( "Label: %i (%i) Size: %i (%i,%i)\n", current_label, imgs->total_labels, labelsize, ix, iy ); + /* Label above threshold? Mark it again (add 32768 to labelnumber) */ + if (labelsize > cnt->threshold){ + labelsize=iflood(ix, iy, width, height, out, labels, current_label+32768, current_label); + imgs->labelgroup_max+=labelsize; + imgs->labels_above++; + } + + if( imgs->labelsize_max < labelsize ){ + imgs->labelsize_max=labelsize; + imgs->largest_label=current_label; + } + + imgs->total_labels++; + current_label++; + } + } + pixelpos++; /* compensate for ixtotal_labels, imgs->labelsize_max, imgs->largest_label); + /* return group of significant labels */ + return imgs->labelgroup_max; +} + +/* Dilates a 3x3 box */ +static int dilate9(unsigned char *img, int width, int height, void *buffer) +{ + /* - row1, row2 and row3 represent lines in the temporary buffer + * - window is a sliding window containing max values of the columns + * in the 3x3 matrix + * - widx is an index into the sliding window (this is faster than + * doing modulo 3 on i) + * - blob keeps the current max value + */ + int y, i, sum = 0, widx; + unsigned char *row1, *row2, *row3, *rowTemp,*yp; + unsigned char window[3], blob, latest; + + /* Set up row pointers in the temporary buffer. */ + row1 = buffer; + row2 = row1 + width; + row3 = row2 + width; + + /* Init rows 2 and 3. */ + memset(row2, 0, width); + memcpy(row3, img, width); + + /* Pointer to the current row in img. */ + yp = img; + + for (y = 0; y < height; y++) { + /* Move down one step; row 1 becomes the previous row 2 and so on. */ + rowTemp = row1; + row1 = row2; + row2 = row3; + row3 = rowTemp; + + /* If we're at the last row, fill with zeros, otherwise copy from img. */ + if(y == height - 1) + memset(row3, 0, width); + else + memcpy(row3, yp+width, width); + + /* Init slots 0 and 1 in the moving window. */ + window[0] = MAX3(row1[0], row2[0], row3[0]); + window[1] = MAX3(row1[1], row2[1], row3[1]); + + /* Init blob to the current max, and set window index. */ + blob = MAX2(window[0], window[1]); + widx = 2; + + /* Iterate over the current row; index i is off by one to eliminate + * a lot of +1es in the loop. + */ + for (i = 2; i <= width - 1; i++) { + /* Get the max value of the next column in the 3x3 matrix. */ + latest = window[widx] = MAX3(row1[i], row2[i], row3[i]); + + /* If the value is larger than the current max, use it. Otherwise, + * calculate a new max (because the new value may not be the max. + */ + if(latest >= blob) + blob = latest; + else + blob = MAX3(window[0], window[1], window[2]); + + /* Write the max value (blob) to the image. */ + if (blob != 0) { + *(yp + i - 1) = blob; + sum++; + } + + /* Wrap around the window index if necessary. */ + if(++widx == 3) + widx = 0; + } + + /* Store zeros in the vertical sides. */ + *yp = *(yp + width - 1) = 0; + yp += width; + } + + return sum; +} + +/* Dilates a + shape */ +static int dilate5(unsigned char *img, int width, int height, void *buffer) +{ + /* - row1, row2 and row3 represent lines in the temporary buffer + * - mem holds the max value of the overlapping part of two + shapes + */ + int y, i, sum = 0; + unsigned char *row1, *row2, *row3, *rowTemp, *yp; + unsigned char blob, mem, latest; + + /* Set up row pointers in the temporary buffer. */ + row1 = buffer; + row2 = row1 + width; + row3 = row2 + width; + + /* Init rows 2 and 3. */ + memset(row2, 0, width); + memcpy(row3, img, width); + + /* Pointer to the current row in img. */ + yp = img; + + for (y = 0; y < height; y++) { + /* Move down one step; row 1 becomes the previous row 2 and so on. */ + rowTemp = row1; + row1 = row2; + row2 = row3; + row3 = rowTemp; + + /* If we're at the last row, fill with zeros, otherwise copy from img. */ + if (y == height - 1) + memset(row3, 0, width); + else + memcpy(row3, yp+width, width); + + /* Init mem and set blob to force an evaluation of the entire + shape. */ + mem = MAX2(row2[0], row2[1]); + blob = 1; /* dummy value, must be > 0 */ + + for (i = 1; i < width - 1; i++) { + /* Get the max value of the "right edge" of the + shape. */ + latest = MAX3(row1[i], row2[i + 1], row3[i]); + + if (blob == 0) { + /* In case the last blob is zero, only latest matters. */ + blob = latest; + mem = row2[i + 1]; + } else { + /* Otherwise, we have to check both latest and mem. */ + blob = MAX2(mem, latest); + mem = MAX2(row2[i], row2[i+1]); + } + + /* Write the max value (blob) to the image. */ + if (blob != 0) { + *(yp + i) = blob; + sum++; + } + } + + /* Store zeros in the vertical sides. */ + *yp = *(yp + width - 1) = 0; + yp += width; + } + return sum; +} + +/* Erodes a 3x3 box */ +static int erode9(unsigned char *img, int width, int height, void *buffer, unsigned char flag) +{ + int y, i, sum = 0; + char *Row1,*Row2,*Row3; + Row1 = buffer; + Row2 = Row1 + width; + Row3 = Row1 + 2*width; + memset(Row2, flag, width); + memcpy(Row3, img, width); + for (y = 0; y < height; y++) { + memcpy(Row1, Row2, width); + memcpy(Row2, Row3, width); + if (y == height-1) + memset(Row3, flag, width); + else + memcpy(Row3, img+(y+1)*width, width); + + for (i = width-2; i >= 1; i--) { + if (Row1[i-1] == 0 || + Row1[i] == 0 || + Row1[i+1] == 0 || + Row2[i-1] == 0 || + Row2[i] == 0 || + Row2[i+1] == 0 || + Row3[i-1] == 0 || + Row3[i] == 0 || + Row3[i+1] == 0) + img[y*width+i] = 0; + else + sum++; + } + img[y*width] = img[y*width+width-1] = flag; + } + return sum; +} + +/* Erodes in a + shape */ +static int erode5(unsigned char *img, int width, int height, void *buffer, unsigned char flag) +{ + int y, i, sum = 0; + char *Row1,*Row2,*Row3; + Row1 = buffer; + Row2 = Row1 + width; + Row3 = Row1 + 2*width; + memset(Row2, flag, width); + memcpy(Row3, img, width); + for (y = 0; y < height; y++) { + memcpy(Row1, Row2, width); + memcpy(Row2, Row3, width); + if (y == height-1) + memset(Row3, flag, width); + else + memcpy(Row3, img+(y+1)*width, width); + + for (i = width-2; i >= 1; i--) { + if (Row1[i] == 0 || + Row2[i-1] == 0 || + Row2[i] == 0 || + Row2[i+1] == 0 || + Row3[i] == 0) + img[y*width+i] = 0; + else + sum++; + } + img[y*width] = img[y*width+width-1] = flag; + } + return sum; +} + +/* + * Despeckling routine to remove noisy detections. + */ +int alg_despeckle(struct context *cnt, int olddiffs) +{ + int diffs = 0; + unsigned char *out = cnt->imgs.out; + int width = cnt->imgs.width; + int height= cnt->imgs.height; + int done = 0, i, len = strlen(cnt->conf.despeckle); + unsigned char *common_buffer = cnt->imgs.common_buffer; + + for (i = 0; i < len; i++) { + switch (cnt->conf.despeckle[i]) { + case 'E': + if ((diffs = erode9(out, width, height, common_buffer, 0)) == 0) i=len; + done=1; + break; + case 'e': + if ((diffs = erode5(out, width, height, common_buffer, 0)) == 0) i=len; + done=1; + break; + case 'D': + diffs = dilate9(out, width, height, common_buffer); + done=1; + break; + case 'd': + diffs = dilate5(out, width, height, common_buffer); + done=1; + break; + /* no further despeckle after labeling! */ + case 'l': + diffs = alg_labeling(cnt); + i=len; + done=1; + break; + } + } + if (done) + return diffs; + else + return olddiffs; +} + +/* Generate actual smartmask. Calculate sensitivity based on motion */ +void alg_tune_smartmask(struct context *cnt) +{ + int i, diff; + + int motionsize = cnt->imgs.motionsize; + unsigned char *smartmask = cnt->imgs.smartmask; + unsigned char *smartmask_final = cnt->imgs.smartmask_final; + int *smartmask_buffer = cnt->imgs.smartmask_buffer; + int sensitivity=cnt->lastrate*(11-cnt->smartmask_speed); + + for (i=0; i 0) + smartmask[i]--; + /* Increase smart_mask sensitivity based on the buffered values */ + diff = smartmask_buffer[i]/sensitivity; + if (diff){ + if (smartmask[i] <= diff+80) + smartmask[i]+=diff; + else + smartmask[i]=80; + smartmask_buffer[i]%=sensitivity; + } + /* Transfer raw mask to the final stage when above trigger value */ + if (smartmask[i]>20) + smartmask_final[i]=0; + else + smartmask_final[i]=255; + } + /* Further expansion (here:erode due to inverted logic!) of the mask */ + diff = erode9(smartmask_final, cnt->imgs.width, cnt->imgs.height, cnt->imgs.common_buffer, 255); + diff = erode5(smartmask_final, cnt->imgs.width, cnt->imgs.height, cnt->imgs.common_buffer, 255); +} + +/* Increment for *smartmask_buffer in alg_diff_standard. */ +#define SMARTMASK_SENSITIVITY_INCR 5 + +int alg_diff_standard (struct context *cnt, unsigned char *new) +{ + struct images *imgs=&cnt->imgs; + int i, diffs=0; + long int level=0; + int noise=cnt->noise; + int smartmask_speed=cnt->smartmask_speed; + unsigned char *ref=imgs->ref; + unsigned char *out=imgs->out; + unsigned char *mask=imgs->mask; + unsigned char *smartmask_final=imgs->smartmask_final; + int *smartmask_buffer=imgs->smartmask_buffer; +#ifdef HAVE_MMX + mmx_t mmtemp; /* used for transferring to/from memory */ + int unload; /* counter for unloading diff counts */ +#endif + + /* If the average level of the picture is to low, compensate by + * lowering the noise threshold + */ + if (cnt->conf.nightcomp) { + i=imgs->motionsize; + for (i--; i>=0; i--) { + level+=(unsigned char)new[i]; + } + level/=imgs->motionsize; + if (level < noise*2) + noise/=2; + } + + i=imgs->motionsize; + memset(out+i, 128, i/2); /* motion pictures are now b/w i.o. green */ + /* Keeping this memset in the MMX case when zeroes are necessarily + * written anyway seems to be beneficial in terms of speed. Perhaps a + * cache thing? + */ + memset(out, 0, i); + +#ifdef HAVE_MMX + /* NOTE: The Pentium has two instruction pipes: U and V. I have grouped MMX + * instructions in pairs according to how I think they will be scheduled in + * the U and V pipes. Due to pairing constraints, the V pipe will sometimes + * be empty (for example, memory access always goes into the U pipe). + * + * The following MMX registers are kept throughout the loop: + * mm5 - 8 separate diff counters (unloaded periodically) + * mm6 - mask: 00ff 00ff 00ff 00ff + * mm7 - noise level as 8 packed bytes + * + * -- Per Jonsson + */ + + /* To avoid a div, we work with differences multiplied by 255 in the + * default case and *mask otherwise. Thus, the limit to compare with is + * 255*(noise+1)-1). + */ + mmtemp.uw[0] = mmtemp.uw[1] = mmtemp.uw[2] = mmtemp.uw[3] = + (unsigned short)(noise * 255 + 254); + + /* Reset mm5 to zero, set the mm6 mask, and store the multiplied noise + * level as four words in mm7. + */ + movq_m2r(mmtemp, mm7); /* U */ + pcmpeqb_r2r(mm6, mm6); /* V */ + + pxor_r2r(mm5, mm5); /* U */ + psrlw_i2r(8, mm6); /* V */ + + /* We must unload mm5 every 255th round, because the diffs accumulate + * in each packed byte, which can hold at most 255 diffs before it + * gets saturated. + */ + unload=255; + + for (; i>7; i-=8) { + /* Calculate abs(*ref-*new) for 8 pixels in parallel. */ + movq_m2r(*ref, mm0); /* U: mm0 = r7 r6 r5 r4 r3 r2 r1 r0 */ + pxor_r2r(mm4, mm4); /* V: mm4 = 0 */ + + movq_m2r(*new, mm1); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */ + movq_r2r(mm0, mm2); /* V: mm2 = r7 r6 r5 r4 r3 r2 r1 r0 */ + + /* These subtractions are saturated, i.e. won't go below 0. */ + psubusb_r2r(mm1, mm0); /* U: mm0 = (r7-n7) ... (r0-n0) */ + psubusb_r2r(mm2, mm1); /* V: mm1 = (n7-r7) ... (n0-r0) */ + + /* Each byte dX in mm0 is abs(nX-rX). */ + por_r2r(mm1, mm0); /* U: mm0 = d7 d6 d5 d4 d3 d2 d1 d0 */ + + /* Expand the absolute differences to words in mm0 and mm1. */ + movq_r2r(mm0, mm1); /* U: mm1 = d7 d6 d5 d4 d3 d2 d1 d0 */ + punpcklbw_r2r(mm4, mm0); /* V: mm0 = d3 d2 d1 d0 */ + + punpckhbw_r2r(mm4, mm1); /* U: mm1 = d7 d6 d5 d4 */ + + if (mask) { + /* Load and expand 8 mask bytes to words in mm2 and mm3. Then + * multiply by mm0 and mm1, respectively. + */ + movq_m2r(*mask, mm2); /* U: mm2 = m7 m6 m5 m4 m3 m2 m1 m0 */ + + movq_r2r(mm2, mm3); /* U: mm3 = m7 m6 m5 m4 m3 m2 m1 m0 */ + punpcklbw_r2r(mm4, mm2); /* v: mm2 = m3 m2 m1 m0 */ + + punpckhbw_r2r(mm4, mm3); /* U: mm3 = m7 m6 m5 m4 */ + pmullw_r2r(mm2, mm0); /* V: mm0 = (d3*m3) ... (d0*m0) */ + + pmullw_r2r(mm3, mm1); /* U: mm1 = (d7*m7) ... (d4*m4) */ + + mask+=8; + } + else { + /* Not using mask - multiply the absolute differences by 255. We + * do this by left-shifting 8 places and then subtracting dX. + */ + movq_r2r(mm0, mm2); /* U: mm2 = d3 d2 d1 d0 */ + psllw_i2r(8, mm0); /* V: mm2 = (256*d3) ... (256*d0) */ + + movq_r2r(mm1, mm3); /* U: mm3 = d7 d6 d5 d4 */ + psllw_i2r(8, mm1); /* V: mm3 = (256*d7) ... (256*d4) */ + + psubusw_r2r(mm2, mm0); /* U */ + psubusw_r2r(mm3, mm1); /* V */ + } + + /* Next, compare the multiplied absolute differences with the multiplied + * noise level (repeted as 4 words in mm7), resulting in a "motion flag" + * for each pixel. + * + * Since pcmpgtw performs signed comparisons, we have to subtract noise, + * test for equality to 0 and then invert the result. + * + * Note that it is safe to generate the "motion flags" before the + * smartmask code, as all that can happen is that individual flags get + * reset to 0 because of the smartmask. + */ + psubusw_r2r(mm7, mm0); /* U: subtract by (multiplied) noise */ + psubusw_r2r(mm7, mm1); /* V */ + + pcmpeqw_r2r(mm4, mm0); /* U: test for equality with 0 */ + pcmpeqw_r2r(mm4, mm1); /* V */ + + pand_r2r(mm6, mm0); /* U: convert 0xffff -> 0x00ff */ + pand_r2r(mm6, mm1); /* V */ + + pxor_r2r(mm6, mm0); /* U: invert the result */ + pxor_r2r(mm6, mm1); /* V */ + + /* Each fX is the "motion flag" = 0 for no motion, 0xff for motion. */ + packuswb_r2r(mm1, mm0); /* U: mm0 = f7 f6 f5 f4 f3 f2 f1 f0 */ + + if (smartmask_speed) { + /* Apply the smartmask. Basically, if *smartmask_final is 0, the + * corresponding "motion flag" in mm0 will be reset. + */ + movq_m2r(*smartmask_final, mm3); /* U: mm3 = s7 s6 s5 s4 s3 s2 s1 s0 */ + + /* ...but move the "motion flags" to memory before, in order to + * increment *smartmask_buffer properly below. + */ + movq_r2m(mm0, mmtemp); /* U */ + pcmpeqb_r2r(mm4, mm3); /* V: mm3 = 0xff where sX==0 */ + + /* ANDN negates the target before anding. */ + pandn_r2r(mm0, mm3); /* U: mm3 = 0xff where dX>noise && sX>0 */ + + movq_r2r(mm3, mm0); /* U */ + + /* Add to *smartmask_buffer. This is probably the fastest way to do it. */ + if (mmtemp.ub[0]) smartmask_buffer[0]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[1]) smartmask_buffer[1]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[2]) smartmask_buffer[2]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[3]) smartmask_buffer[3]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[4]) smartmask_buffer[4]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[5]) smartmask_buffer[5]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[6]) smartmask_buffer[6]+=SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[7]) smartmask_buffer[7]+=SMARTMASK_SENSITIVITY_INCR; + + smartmask_buffer+=8; + smartmask_final+=8; + } + + movq_m2r(*new, mm2); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */ + + /* Cancel out pixels in *new according to the "motion flags" in mm0. + * Each NX is either 0 or nX as from *new. + */ + pand_r2r(mm0, mm2); /* U: mm1 = N7 N6 N5 N4 N3 N2 N1 N0 */ + psubb_r2r(mm0, mm4); /* V: mm4 = 0x01 where dX>noise */ + + /* mm5 holds 8 separate counts - each one is increased according to + * the contents of mm4 (where each byte is either 0x00 or 0x01). + */ + movq_r2m(mm2, *out); /* U: this will stall */ + paddusb_r2r(mm4, mm5); /* V: add counts to mm5 */ + + /* Every 255th turn, we need to unload mm5 into the diffs variable, + * because otherwise the packed bytes will get saturated. + */ + if (--unload==0) { + /* Unload mm5 to memory and reset it. */ + movq_r2m(mm5, mmtemp); /* U */ + pxor_r2r(mm5, mm5); /* V: mm5 = 0 */ + + diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] + + mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7]; + unload=255; + } + + out+=8; + ref+=8; + new+=8; + } + + /* Check if there are diffs left in mm5 that need to be copied to the + * diffs variable. + */ + if (unload<255) { + movq_r2m(mm5, mmtemp); + diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] + + mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7]; + } + + emms(); + +#endif + /* Note that the non-MMX code is present even if the MMX code is present. + * This is necessary if the resolution is not a multiple of 8, in which + * case the non-MMX code needs to take care of the remaining pixels. + */ + + for (; i>0; i--) { + register unsigned char curdiff=(int)(abs(*ref - *new)); /* using a temp variable is 12% faster */ + /* apply fixed mask */ + if (mask) + curdiff=((int)(curdiff * *mask++)/255); + + if (smartmask_speed) { + if (curdiff > noise) { + /* increase smart_mask sesitivity every frame when motion + is detected. (with speed=5, mask is increased by 1 every + second. To be able to increase by 5 every second (with + speed=10) we add 5 here. NOT related to the 5 at ratio- + calculation. */ + (*smartmask_buffer) += SMARTMASK_SENSITIVITY_INCR; + /* apply smart_mask */ + if (!*smartmask_final) + curdiff=0; + } + smartmask_final++; + smartmask_buffer++; + } + /* Pixel still in motion after all the masks? */ + if (curdiff > noise) { + *out=*new; + diffs++; + } + out++; + ref++; + new++; + } + return diffs; +} + +/* + Very fast diff function, does not do nightcompensation or mask + overlaying. +*/ +static char alg_diff_fast (struct context *cnt, int max_n_changes, unsigned char *new) +{ + struct images *imgs=&cnt->imgs; + int i, diffs=0, step=imgs->motionsize/10000; + int noise=cnt->noise; + unsigned char *ref=imgs->ref; + + if (!step%2) + step++; + /* we're checking only 1 of several pixels */ + max_n_changes /= step; + + i=imgs->motionsize; + for (; i>0; i-=step) { + register unsigned char curdiff=(int)(abs((char)(*ref-*new))); /* using a temp variable is 12% faster */ + if (curdiff > noise) { + diffs++; + if (diffs > max_n_changes) + return 1; + } + ref+=step; + new+=step; + } + + return 0; +} + +/* alg_diff uses diff_fast to quickly decide if there is anything worth + * sending to diff_standard. +*/ +int alg_diff (struct context *cnt, unsigned char *new) +{ + int diffs=0; + + if (alg_diff_fast(cnt, cnt->conf.max_changes/2, new)) + diffs=alg_diff_standard(cnt, new); + + return diffs; +} + +/* Detect a sudden massive change in the picture. + It is assumed to be the light being switched on or a camera displacement. + In any way the user doesn't think it is worth capturing. + */ +int alg_lightswitch (struct context *cnt, int diffs) +{ + struct images *imgs=&cnt->imgs; + + if (cnt->conf.lightswitch < 0) + cnt->conf.lightswitch = 0; + if (cnt->conf.lightswitch > 100) + cnt->conf.lightswitch = 100; + + /* is lightswitch percent of the image changed? */ + if (diffs > (imgs->motionsize * cnt->conf.lightswitch / 100)) + return 1; + + return 0; +} + +int alg_switchfilter (struct context *cnt, int diffs, unsigned char *newimg) +{ + int linediff = diffs / cnt->imgs.height; + unsigned char *out = cnt->imgs.out; + int y, x, line; + int lines=0, vertlines=0; + + for (y=0; y < cnt->imgs.height; y++) { + line=0; + for (x=0; x < cnt->imgs.width; x++) { + if (*(out++)) { + line++; + } + } + if (line > cnt->imgs.width/18) { + vertlines++; + } + if (line > linediff*2) { + lines++; + } + } + + if (vertlines > cnt->imgs.height/10 && lines < vertlines/3 && + (vertlines > cnt->imgs.height/4 || lines - vertlines > lines/2)) { + if (cnt->conf.text_changes) { + char tmp[80]; + sprintf(tmp, "%d %d", lines, vertlines); + draw_text(newimg, cnt->imgs.width-10, 20, cnt->imgs.width, tmp, cnt->conf.text_double); + } + return diffs; + } + return 0; +} diff --git a/alg.h b/alg.h new file mode 100644 index 0000000..82412b9 --- /dev/null +++ b/alg.h @@ -0,0 +1,45 @@ +/* alg.h + * + * Detect changes in a video stream. + * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#ifndef _INCLUDE_ALG_H +#define _INCLUDE_ALG_H + +#include "motion.h" + +struct coord { + int x; + int y; + int width; + int height; + int minx; + int maxx; + int miny; + int maxy; +}; + +struct segment { + struct coord coord; + int width; + int height; + int open; + int count; +}; + +void alg_locate_center_size(struct images *, int width, int height, struct coord *); +void alg_draw_location(struct coord *, struct images *, int width, unsigned char *, int); +int alg_diff(struct context *, unsigned char *); +int alg_diff_standard(struct context *, unsigned char *); +int alg_lightswitch(struct context *, int diffs); +int alg_switchfilter(struct context *, int, unsigned char *); +void alg_noise_tune(struct context *, unsigned char *); +void alg_threshold_tune(struct context *, int, int); +int alg_despeckle(struct context *, int); +void alg_tune_smartmask(struct context *); + +#endif /* _INCLUDE_ALG_H */ diff --git a/conf.c b/conf.c new file mode 100644 index 0000000..a06d253 --- /dev/null +++ b/conf.c @@ -0,0 +1,1819 @@ +/* + ** + ** conf.c + ** + ** I originaly wrote conf.c as part of the drpoxy package + ** thanks to Matthew Pratt and others for their additions. + ** + ** Copyright 1999 Jeroen Vreeken (pe1rxq@chello.nl) + ** + ** This software is licensed under the terms of the GNU General + ** Public License (GPL). Please see the file COPYING for details. + ** + ** +*/ + +/** + * How to add a config option : + * + * 1. think twice, there are settings enough + * + * 2. add a field to 'struct config' (conf.h) and 'struct config conf' + * + * 4. add a entry to the config_params array below, if your + * option should be configurable by the config file. + */ +#include "motion.h" + +#ifdef __freebsd__ +#include "video_freebsd.h" +#else +#include "video.h" +#endif /* __freebsd__ */ + +#include "conf.h" +#include "track.h" + +#define stripnewline(x) {if ((x)[strlen(x)-1]=='\n') (x)[strlen(x) - 1] = 0; } + + +struct config conf_template = { + width: DEF_WIDTH, + height: DEF_HEIGHT, + quality: DEF_QUALITY, + rotate_deg: 0, + max_changes: DEF_CHANGES, + threshold_tune: 0, + output_normal: "on", + motion_img: 0, + output_all: 0, + gap: DEF_GAP, + maxmpegtime: DEF_MAXMPEGTIME, + snapshot_interval: 0, + locate: "off", + input: IN_DEFAULT, + norm: 0, + frame_limit: DEF_MAXFRAMERATE, + quiet: 1, + ppm: 0, + noise: DEF_NOISELEVEL, + noise_tune: 1, + mingap: 0, + lightswitch: 0, + nightcomp: 0, + low_cpu: 0, + nochild: 0, + autobright: 0, + brightness: 0, + contrast: 0, + saturation: 0, + hue: 0, + roundrobin_frames: 1, + roundrobin_skip: 1, + pre_capture: 0, + post_capture: 0, + switchfilter: 0, + ffmpeg_cap_new: 0, + ffmpeg_cap_motion: 0, + ffmpeg_bps: DEF_FFMPEG_BPS, + ffmpeg_vbr: DEF_FFMPEG_VBR, + ffmpeg_video_codec: DEF_FFMPEG_CODEC, + webcam_port: 0, + webcam_quality: 50, + webcam_motion: 0, + webcam_maxrate: 1, + webcam_localhost: 1, + webcam_limit: 0, + control_port: 0, + control_localhost: 1, + control_html_output: 1, + control_authentication:NULL, + frequency: 0, + tuner_number: 0, + timelapse: 0, + timelapse_mode: DEF_TIMELAPSE_MODE, +#ifdef __freebsd__ + tuner_device: NULL, +#endif + video_device: VIDEO_DEVICE, + vidpipe: NULL, + filepath: NULL, + jpegpath: DEF_JPEGPATH, + mpegpath: DEF_MPEGPATH, + snappath: DEF_SNAPPATH, + timepath: DEF_TIMEPATH, + on_event_start: NULL, + on_event_end: NULL, + mask_file: NULL, + smart_mask_speed: 0, + sql_log_image: 1, + sql_log_snapshot: 1, + sql_log_mpeg: 0, + sql_log_timelapse: 0, + sql_query: DEF_SQL_QUERY, + mysql_db: NULL, + mysql_host: NULL, + mysql_user: NULL, + mysql_password: NULL, + on_picture_save: NULL, + on_motion_detected: NULL, + on_movie_start: NULL, + on_movie_end: NULL, + motionvidpipe: NULL, + netcam_url: NULL, + netcam_userpass: NULL, + netcam_proxy: NULL, + pgsql_db: NULL, + pgsql_host: NULL, + pgsql_user: NULL, + pgsql_password: NULL, + pgsql_port: 5432, + text_changes: 0, + text_left: NULL, + text_right: DEF_TIMESTAMP, + text_event: DEF_EVENTSTAMP, + text_double: 0, + despeckle: NULL, + minimum_motion_frames: 1, + // debug_parameter: 0 +}; + + + +static struct context **copy_bool(struct context **, const char *, int); +static struct context **copy_int(struct context **, const char *, int); +static struct context **config_thread(struct context **cnt, const char *str, int val); + +static const char *print_bool(struct context **, char **, int, int); +static const char *print_int(struct context **, char **, int, int); +static const char *print_string(struct context **, char **, int, int); +static const char *print_thread(struct context **, char **, int, int); + +static void usage(void); + +/* Pointer magic to determine relative addresses of variables to a + struct context pointer */ +#define CNT_OFFSET(varname) ( (int)&((struct context *)NULL)->varname ) +#define CONF_OFFSET(varname) ( (int)&((struct context *)NULL)->conf.varname ) +#define TRACK_OFFSET(varname) ( (int)&((struct context *)NULL)->track.varname ) + +config_param config_params[] = { + { + "daemon", + "############################################################\n" + "# Daemon\n" + "############################################################\n\n" + "# Start in daemon (background) mode and release terminal (default: off)", + CNT_OFFSET(daemon), + copy_bool, + print_bool + }, + { + "setup_mode", + "############################################################\n" + "# Basic Setup Mode\n" + "############################################################\n\n" + "# Start in Setup-Mode, daemon disabled. (default: off)", + CONF_OFFSET(setup_mode), + copy_bool, + print_bool + }, + { + "videodevice", + "\n###########################################################\n" + "# Capture device options\n" + "############################################################\n\n" + "# Videodevice to be used for capturing (default /dev/video0)\n" + "# for FreeBSD default is /dev/bktr0", + CONF_OFFSET(video_device), + copy_string, + print_string + }, +#ifdef __freebsd__ + { + "tunerdevice", + "# Tuner device to be used for capturing using tuner as source (default /dev/tuner0)\n" + "# This is ONLY used for FreeBSD. Leave it commented out for Linux", + CONF_OFFSET(tuner_device), + copy_string, + print_string + }, +#endif + { + "input", + "# The video input to be used (default: 8)\n" + "# Should normally be set to 1 for video/TV cards, and 8 for USB cameras", + CONF_OFFSET(input), + copy_int, + print_int + }, + { + "norm", + "# The video norm to use (only for video capture and TV tuner cards)\n" + "# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL)", + CONF_OFFSET(norm), + copy_int, + print_int + }, + { + "frequency", + "# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0)", + CONF_OFFSET(frequency), + copy_int, + print_int + }, + { + "rotate", + "# Rotate image this number of degrees. The rotation affects all saved images as\n" + "# well as mpeg movies. Valid values: 0 (default = no rotation), 90, 180 and 270.", + CONF_OFFSET(rotate_deg), + copy_int, + print_int + }, + { + "width", + "# Image width (pixels). Valid range: Camera dependent, default: 352", + CONF_OFFSET(width), + copy_int, + print_int + }, + { + "height", + "# Image height (pixels). Valid range: Camera dependent, default: 288", + CONF_OFFSET(height), + copy_int, + print_int + }, + { + "framerate", + "# Maximum number of frames to be captured per second.\n" + "# Valid range: 2-100. Default: 100 (almost no limit).", + CONF_OFFSET(frame_limit), + copy_int, + print_int + }, + { + "netcam_url", + "# URL to use if you are using a network camera, size will be autodetected (incl http://)\n" + "# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined", + CONF_OFFSET(netcam_url), + copy_string, + print_string + }, + { + "netcam_userpass", + "# Username and password for network camera (only if required). Default: not defined\n" + "# Syntax is user:password", + CONF_OFFSET(netcam_userpass), + copy_string, + print_string + }, + { + "netcam_proxy", + "# URL to use for a netcam proxy server, if required, e.g. \"http://myproxy\".\n" + "# If a port number other than 80 is needed, use \"http://myproxy:1234\".\n" + "# Default: not defined", + CONF_OFFSET(netcam_proxy), + copy_string, + print_string + }, + { + "auto_brightness", + "# Let motion regulate the brightness of a video device (default: off).\n" + "# The auto_brightness feature uses the brightness option as its target value.\n" + "# If brightness is zero auto_brightness will adjust to average brightness value 128.\n" + "# Only recommended for cameras without auto brightness", + CONF_OFFSET(autobright), + copy_bool, + print_bool + }, + { + "brightness", + "# Set the initial brightness of a video device.\n" + "# If auto_brightness is enabled, this value defines the average brightness level\n" + "# which Motion will try and adjust to.\n" + "# Valid range 0-255, default 0 = disabled", + CONF_OFFSET(brightness), + copy_int, + print_int + }, + { + "contrast", + "# Set the contrast of a video device.\n" + "# Valid range 0-255, default 0 = disabled", + CONF_OFFSET(contrast), + copy_int, + print_int + }, + { + "saturation", + "# Set the saturation of a video device.\n" + "# Valid range 0-255, default 0 = disabled", + CONF_OFFSET(saturation), + copy_int, + print_int + }, + { + "hue", + "# Set the hue of a video device (NTSC feature).\n" + "# Valid range 0-255, default 0 = disabled", + CONF_OFFSET(hue), + copy_int, + print_int + }, + + { + "roundrobin_frames", + "\n############################################################\n" + "# Round Robin (multiple inputs on same video device name)\n" + "############################################################\n\n" + "# Number of frames to capture in each roundrobin step (default: 1)", + CONF_OFFSET(roundrobin_frames), + copy_int, + print_int + }, + { + "roundrobin_skip", + "# Number of frames to skip before each roundrobin step (default: 1)", + CONF_OFFSET(roundrobin_skip), + copy_int, + print_int + }, + { + "switchfilter", + "# Try to filter out noise generated by roundrobin (default: off)", + CONF_OFFSET(switchfilter), + copy_bool, + print_bool + }, + + { + "threshold", + "\n############################################################\n" + "# Motion Detection Settings:\n" + "############################################################\n\n" + "# Threshold for number of changed pixels in an image that\n" + "# triggers motion detection (default: 1500)", + CONF_OFFSET(max_changes), + copy_int, + print_int + }, + { + "threshold_tune", + "# Automatically tune the threshold down if possible (default: off)", + CONF_OFFSET(threshold_tune), + copy_bool, + print_bool + }, + { + "noise_level", + "# Noise threshold for the motion detection (default: 32)", + CONF_OFFSET(noise), + copy_int, + print_int + }, + { + "noise_tune", + "# Automatically tune the noise threshold (default: on)", + CONF_OFFSET(noise_tune), + copy_bool, + print_bool + }, + { + "night_compensate", + "# Enables motion to adjust its detection/noise level for very dark frames\n" + "# Don't use this with noise_tune on. (default: off)", + CONF_OFFSET(nightcomp), + copy_bool, + print_bool + }, + { + "despeckle", + "# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined)\n" + "# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid.\n" + "# (l)abeling must only be used once and the 'l' must be the last letter.\n" + "# Comment out to disable", + CONF_OFFSET(despeckle), + copy_string, + print_string + }, + { + "mask_file", + "# PGM file to use as a sensitivity mask.\n" + "# Full path name to. (Default: not defined)", + CONF_OFFSET(mask_file), + copy_string, + print_string + }, + { + "smart_mask_speed", + "# Dynamically create a mask file during operation (default: 0)\n" + "# Adjust speed of mask changes from 0 (off) to 10 (fast)", + CONF_OFFSET(smart_mask_speed), + copy_int, + print_int + }, + { + "lightswitch", + "# Ignore sudden massive light intensity changes given as a percentage of the picture\n" + "# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled", + CONF_OFFSET(lightswitch), + copy_int, + print_int + }, + { + "minimum_motion_frames", + "# Picture frames must contain motion at least the specified number of frames\n" + "# in a row before they are detected as true motion. At the default of 1, all\n" + "# motion is detected. Valid range: 1 to thousands, recommended 1-5", + CONF_OFFSET(minimum_motion_frames), + copy_int, + print_int + }, + { + "pre_capture", + "# Specifies the number of pre-captured (buffered) pictures from before motion\n" + "# was detected that will be output at motion detection.\n" + "# Recommended range: 0 to 5 (default: 0)\n" + "# Do not use large values! Large values will cause Motion to skip video frames and\n" + "# cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead.", + CONF_OFFSET(pre_capture), + copy_int, + print_int + }, + { + "post_capture", + "# Number of frames to capture after motion is no longer detected (default: 0)", + CONF_OFFSET(post_capture), + copy_int, + print_int + }, + { + "gap", + "# Gap is the seconds of no motion detection that triggers the end of an event\n" + "# An event is defined as a series of motion images taken within a short timeframe.\n" + "# Recommended value is 60 seconds (Default). The value 0 is allowed and disables\n" + "# events causing all Motion to be written to one single mpeg file and no pre_capture.", + CONF_OFFSET(gap), + copy_int, + print_int + }, + { + "minimum_gap", + "# Minimum gap in seconds between the storing pictures while detecting motion.\n" + "# Default: 0 = as fast as possible (given by the camera framerate)\n" + "# Note: This option has nothing to do with the option 'gap'", + CONF_OFFSET(mingap), + copy_int, + print_int + }, + { + "max_mpeg_time", + "# Maximum length in seconds of an mpeg movie\n" + "# When value is exceeded a new mpeg file is created. (Default: 0 = infinite)", + CONF_OFFSET(maxmpegtime), + copy_int, + print_int + }, + { + "low_cpu", + "# Number of frames per second to capture when not detecting\n" + "# motion (saves CPU load) (Default: 0 = disabled)", + CONF_OFFSET(low_cpu), + copy_int, + print_int + }, + { + "output_all", + "# Always save images even if there was no motion (default: off)", + CONF_OFFSET(output_all), + copy_bool, + print_bool + }, + + { + "output_normal", + "\n############################################################\n" + "# Image File Output\n" + "############################################################\n\n" + "# Output 'normal' pictures when motion is detected (default: on)\n" + "# Valid values: on, off, first, best\n" + "# When set to 'first', only the first picture of an event is saved.\n" + "# Picture with most motion of an event is saved when set to 'best'.\n" + "# Can be used as preview shot for the corresponding movie.", + CONF_OFFSET(output_normal), + copy_string, + print_string + }, + { + "output_motion", + "# Output pictures with only the pixels moving object (ghost images) (default: off)", + CONF_OFFSET(motion_img), + copy_bool, + print_bool + }, + { + "quality", + "# The quality (in percent) to be used by the jpeg compression (default: 75)", + CONF_OFFSET(quality), + copy_int, + print_int + }, + { + "ppm", + "# Output ppm images instead of jpeg (default: off)", + CONF_OFFSET(ppm), + copy_bool, + print_bool + }, + +#ifdef HAVE_FFMPEG + { + "ffmpeg_cap_new", + "\n############################################################\n" + "# Film (mpeg) File Output - ffmpeg based\n" + "############################################################\n\n" + "# Use ffmpeg to encode mpeg movies in realtime (default: off)", + CONF_OFFSET(ffmpeg_cap_new), + copy_bool, + print_bool + }, + { + "ffmpeg_cap_motion", + "# Use ffmpeg to make movies with only the pixels moving\n" + "# object (ghost images) (default: off)", + CONF_OFFSET(ffmpeg_cap_motion), + copy_bool, + print_bool + }, + { + "ffmpeg_timelapse", + "# Use ffmpeg to encode a timelapse movie\n" + "# Default value 0 = off - else save frame every Nth second", + CONF_OFFSET(timelapse), + copy_int, + print_int + }, + { + "ffmpeg_timelapse_mode", + "# The file rollover mode of the timelapse video\n" + "# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual", + CONF_OFFSET(timelapse_mode), + copy_string, + print_string + }, + { + "ffmpeg_bps", + "# Bitrate to be used by the ffmpeg encoder (default: 400000)\n" + "# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled)", + CONF_OFFSET(ffmpeg_bps), + copy_int, + print_int + }, + { + "ffmpeg_variable_bitrate", + "# Enables and defines variable bitrate for the ffmpeg encoder.\n" + "# ffmpeg_bps is ignored if variable bitrate is enabled.\n" + "# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps,\n" + "# or the range 2 - 31 where 2 means best quality and 31 is worst.", + CONF_OFFSET(ffmpeg_vbr), + copy_int, + print_int + }, + { + "ffmpeg_video_codec", + "# Codec to used by ffmpeg for the video compression.\n" + "# Timelapse mpegs are always made in mpeg1 format independent from this option.\n" + "# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4.\n" + "# mpeg1 - gives you files with extension .mpg\n" + "# mpeg4 or msmpeg4 - give you files with extension .avi\n" + "# msmpeg4 is recommended for use with Windows Media Player because\n" + "# it requires no installation of codec on the Windows client.", + CONF_OFFSET(ffmpeg_video_codec), + copy_string, + print_string + }, +#endif /* HAVE_FFMPEG */ + + { + "snapshot_interval", + "\n############################################################\n" + "# Snapshots (Traditional Periodic Webcam File Output)\n" + "############################################################\n\n" + "# Make automated snapshot every N seconds (default: 0 = disabled)", + CONF_OFFSET(snapshot_interval), + copy_int, + print_int + }, + + { + "locate", + "\n############################################################\n" + "# Text Display\n" + "# %Y = year, %m = month, %d = date,\n" + "# %H = hour, %M = minute, %S = second, %T = HH:MM:SS,\n" + "# %v = event, %q = frame number, %t = thread (camera) number,\n" + "# %D = changed pixels, %N = noise level, \\n = new line,\n" + "# %i and %J = width and height of motion area,\n" + "# %K and %L = X and Y coordinates of motion center\n" + "# %C = value defined by text_event - do not use with text_event!\n" + "# You can put quotation marks around the text to allow\n" + "# leading spaces\n" + "############################################################\n\n" + "# Locate and draw a box around the moving object.\n" + "# Valid values: on, off and preview (default: off)\n" + "# Set to 'preview' will only draw a box in preview_shot pictures.", + CONF_OFFSET(locate), + copy_string, + print_string + }, + { + "text_right", + "# Draws the timestamp using same options as C function strftime(3)\n" + "# Default: %Y-%m-%d\\n%T = date in ISO format and time in 24 hour clock\n" + "# Text is placed in lower right corner", + CONF_OFFSET(text_right), + copy_string, + print_string + }, + { + "text_left", + "# Draw a user defined text on the images using same options as C function strftime(3)\n" + "# Default: Not defined = no text\n" + "# Text is placed in lower left corner", + CONF_OFFSET(text_left), + copy_string, + print_string + }, + { + "text_changes", + "# Draw the number of changed pixed on the images (default: off)\n" + "# Will normally be set to off except when you setup and adjust the motion settings\n" + "# Text is placed in upper right corner", + CONF_OFFSET(text_changes), + copy_bool, + print_bool + }, + { + "text_event", + "# This option defines the value of the speciel event conversion specifier %C\n" + "# You can use any conversion specifier in this option except %C. Date and time\n" + "# values are from the timestamp of the first image in the current event.\n" + "# Default: %Y%m%d%H%M%S\n" + "# The idea is that %C can be used filenames and text_left/right for creating\n" + "# a unique identifier for each event.", + CONF_OFFSET(text_event), + copy_string, + print_string + }, + { + "text_double", + "# Draw characters at twice normal size on images. (default: off)", + CONF_OFFSET(text_double), + copy_bool, + print_bool + }, + + { + "target_dir", + "\n############################################################\n" + "# Target Directories and filenames For Images And Films\n" + "# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename\n" + "# you can use conversion specifiers\n" + "# %Y = year, %m = month, %d = date,\n" + "# %H = hour, %M = minute, %S = second,\n" + "# %v = event, %q = frame number, %t = thread (camera) number,\n" + "# %D = changed pixels, %N = noise level,\n" + "# %i and %J = width and height of motion area,\n" + "# %K and %L = X and Y coordinates of motion center\n" + "# %C = value defined by text_event\n" + "# Quotation marks round string are allowed.\n" + "############################################################\n\n" + "# Target base directory for pictures and films\n" + "# Recommended to use absolute path. (Default: current working directory)", + CONF_OFFSET(filepath), + copy_string, + print_string + }, + { + "snapshot_filename", + "# File path for snapshots (jpeg or ppm) relative to target_dir\n" + "# Default: "DEF_SNAPPATH"\n" + "# Default value is equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot\n" + "# File extension .jpg or .ppm is automatically added so do not include this.\n" + "# Note: A symbolic link called lastsnap.jpg created in the target_dir will always\n" + "# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap'", + CONF_OFFSET(snappath), + copy_string, + print_string + }, + { + "jpeg_filename", + "# File path for motion triggered images (jpeg or ppm) relative to target_dir\n" + "# Default: "DEF_JPEGPATH"\n" + "# Default value is equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q\n" + "# File extension .jpg or .ppm is automatically added so do not include this\n" + "# Set to 'preview' together with best-preview feature enables special naming\n" + "# convention for preview shots. See motion guide for details", + CONF_OFFSET(jpegpath), + copy_string, + print_string + }, +#ifdef HAVE_FFMPEG + { + "ffmpeg_filename", + "# File path for motion triggered ffmpeg films (mpeg) relative to target_dir\n" + "# Default: "DEF_MPEGPATH"\n" + "# Default value is equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S\n" + "# File extension .mpg or .avi is automatically added so do not include this", + CONF_OFFSET(mpegpath), + copy_string, + print_string + }, + { + "timelapse_filename", + "# File path for timelapse mpegs relative to target_dir\n" + "# Default: "DEF_TIMEPATH"\n" + "# Default value is near equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse\n" + "# File extension .mpg is automatically added so do not include this", + CONF_OFFSET(timepath), + copy_string, + print_string + }, +#endif /* HAVE_FFMPEG */ + + { + "webcam_port", + "\n############################################################\n" + "# Live Webcam Server\n" + "############################################################\n\n" + "# The mini-http server listens to this port for requests (default: 0 = disabled)", + CONF_OFFSET(webcam_port), + copy_int, + print_int + }, + { + "webcam_quality", + "# Quality of the jpeg images produced (default: 50)", + CONF_OFFSET(webcam_quality), + copy_int, + print_int + }, + { + "webcam_motion", + "# Output frames at 1 fps when no motion is detected and increase to the\n" + "# rate given by webcam_maxrate when motion is detected (default: off)", + CONF_OFFSET(webcam_motion), + copy_bool, + print_bool + }, + { + "webcam_maxrate", + "# Maximum framerate for webcam streams (default: 1)", + CONF_OFFSET(webcam_maxrate), + copy_int, + print_int + }, + { + "webcam_localhost", + "# Restrict webcam connections to localhost only (default: on)", + CONF_OFFSET(webcam_localhost), + copy_bool, + print_bool + }, + { + "webcam_limit", + "# Limits the number of images per connection (default: 0 = unlimited)\n" + "# Number can be defined by multiplying actual webcam rate by desired number of seconds\n" + "# Actual webcam rate is the smallest of the numbers framerate and webcam_maxrate", + CONF_OFFSET(webcam_limit), + copy_int, + print_int + }, + { + "control_port", + "\n############################################################\n" + "# HTTP Based Control\n" + "############################################################\n\n" + "# TCP/IP port for the http server to listen on (default: 0 = disabled)", + CONF_OFFSET(control_port), + copy_int, + print_int + }, + { + "control_localhost", + "# Restrict control connections to localhost only (default: on)", + CONF_OFFSET(control_localhost), + copy_bool, + print_bool + }, + { + "control_html_output", + "# Output for http server, select off to choose raw text plain (default: on)", + CONF_OFFSET(control_html_output), + copy_bool, + print_bool + }, + { + "control_authentication", + "# Authentication for the http based control. Syntax username:password\n" + "# Default: not defined (Disabled)", + CONF_OFFSET(control_authentication), + copy_string, + print_string + }, + { + "track_type", + "\n############################################################\n" + "# Tracking (Pan/Tilt)\n" + "############################################################\n\n" + "# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic)\n" + "# The generic type enables the definition of motion center and motion size to\n" + "# be used with the convertion specifiers for options like on_motion_detected", + TRACK_OFFSET(type), + copy_int, + print_int + }, + { + "track_auto", + "# Enable auto tracking (default: off)", + TRACK_OFFSET(active), + copy_bool, + print_bool + }, + { + "track_port", + "# Serial port of motor (default: none)", + TRACK_OFFSET(port), + copy_string, + print_string + }, + { + "track_motorx", + "# Motor number for x-axis (default: -1)", + TRACK_OFFSET(motorx), + copy_int, + print_int + }, + { + "track_maxx", + "# Maximum value on x-axis (default: 0)", + TRACK_OFFSET(maxx), + copy_int, + print_int + }, + { + "track_iomojo_id", + "# ID of an iomojo camera if used (default: 0)", + TRACK_OFFSET(iomojo_id), + copy_int, + print_int + }, + { + "track_step_angle_x", + "# Angle in degrees the camera moves per step on the X-axis\n" + "# with auto-track (default: 10)\n" + "# Currently only used with pwc type cameras", + TRACK_OFFSET(step_angle_x), + copy_int, + print_int + }, + { + "track_step_angle_y", + "# Angle in degrees the camera moves per step on the Y-axis\n" + "# with auto-track (default: 10)\n" + "# Currently only used with pwc type cameras", + TRACK_OFFSET(step_angle_y), + copy_int, + print_int + }, + { + "track_move_wait", + "# Delay to wait for after tracking movement as number\n" + "# of picture frames (default: 10)", + TRACK_OFFSET(move_wait), + copy_int, + print_int + }, + { + "track_speed", + "# Speed to set the motor to (stepper motor option) (default: 255)", + TRACK_OFFSET(speed), + copy_int, + print_int + }, + { + "track_stepsize", + "# Number of steps to make (stepper motor option) (default: 40)", + TRACK_OFFSET(stepsize), + copy_int, + print_int + }, + + { + "quiet", + "\n############################################################\n" + "# External Commands, Warnings and Logging:\n" + "# You can use conversion specifiers for the on_xxxx commands\n" + "# %Y = year, %m = month, %d = date,\n" + "# %H = hour, %M = minute, %S = second,\n" + "# %v = event, %q = frame number, %t = thread (camera) number,\n" + "# %D = changed pixels, %N = noise level,\n" + "# %i and %J = width and height of motion area,\n" + "# %K and %L = X and Y coordinates of motion center\n" + "# %C = value defined by text_event\n" + "# %f = filename with full path\n" + "# %n = number indicating filetype\n" + "# Both %f and %n are only defined for on_picture_save,\n" + "# on_movie_start and on_movie_end\n" + "# Quotation marks round string are allowed.\n" + "############################################################\n\n" + "# Do not sound beeps when detecting motion (default: on)\n" + "# Note: Motion never beeps when running in daemon mode.", + CONF_OFFSET(quiet), + copy_bool, + print_bool + }, + { + "on_event_start", + "# Command to be executed when an event starts. (default: none)\n" + "# An event starts at first motion detected after a period of no motion defined by gap ", + CONF_OFFSET(on_event_start), + copy_string, + print_string + }, + { + "on_event_end", + "# Command to be executed when an event ends after a period of no motion\n" + "# (default: none). The period of no motion is defined by option gap.", + CONF_OFFSET(on_event_end), + copy_string, + print_string + }, + { + "on_picture_save", + "# Command to be executed when a picture (.ppm|.jpg) is saved (default: none)\n" + "# To give the filename as an argument to a command append it with %f", + CONF_OFFSET(on_picture_save), + copy_string, + print_string + }, + { + "on_motion_detected", + "# Command to be executed when a motion frame is detected (default: none)", + CONF_OFFSET(on_motion_detected), + copy_string, + print_string + }, +#ifdef HAVE_FFMPEG + { + "on_movie_start", + "# Command to be executed when a movie file (.mpg|.avi) is created. (default: none)\n" + "# To give the filename as an argument to a command append it with %f", + CONF_OFFSET(on_movie_start), + copy_string, + print_string + }, + { + "on_movie_end", + "# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none)\n" + "# To give the filename as an argument to a command append it with %f", + CONF_OFFSET(on_movie_end), + copy_string, + print_string + }, +#endif /* HAVE_FFMPEG */ + +#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) + { + "sql_log_image", + "\n############################################################\n" + "# Common Options For MySQL and PostgreSQL database features.\n" + "# Options require the MySQL/PostgreSQL options to be active also.\n" + "############################################################\n\n" + "# Log to the database when creating motion triggered image file (default: on)", + CONF_OFFSET(sql_log_image), + copy_bool, + print_bool + }, + { + "sql_log_snapshot", + "# Log to the database when creating a snapshot image file (default: on)", + CONF_OFFSET(sql_log_snapshot), + copy_bool, + print_bool + }, + { + "sql_log_mpeg", + "# Log to the database when creating motion triggered mpeg file (default: off)", + CONF_OFFSET(sql_log_mpeg), + copy_bool, + print_bool + }, + { + "sql_log_timelapse", + "# Log to the database when creating timelapse mpeg file (default: off)", + CONF_OFFSET(sql_log_timelapse), + copy_bool, + print_bool + }, + { + "sql_query", + "# SQL query string that is sent to the database\n" + "# Use same conversion specifiers has for text features\n" + "# Additional special conversion specifiers are\n" + "# %n = the number representing the file_type\n" + "# %f = filename with full path\n" + "# Default value:\n" + "# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')", + CONF_OFFSET(sql_query), + copy_string, + print_string + }, + +#endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) */ + +#ifdef HAVE_MYSQL + { + "mysql_db", + "\n############################################################\n" + "# Database Options For MySQL\n" + "############################################################\n\n" + "# Mysql database to log to (default: not defined)", + CONF_OFFSET(mysql_db), + copy_string, + print_string + }, + { + "mysql_host", + "# The host on which the database is located (default: not defined)", + CONF_OFFSET(mysql_host), + copy_string, + print_string + }, + { + "mysql_user", + "# User account name for MySQL database (default: not defined)", + CONF_OFFSET(mysql_user), + copy_string, + print_string + }, + { + "mysql_password", + "# User password for MySQL database (default: not defined)", + CONF_OFFSET(mysql_password), + copy_string, + print_string + }, +#endif /* HAVE_MYSQL */ + +#ifdef HAVE_PGSQL + { + "pgsql_db", + "\n############################################################\n" + "# Database Options For PostgreSQL\n" + "############################################################\n\n" + "# PostgreSQL database to log to (default: not defined)", + CONF_OFFSET(pgsql_db), + copy_string, + print_string + }, + { + "pgsql_host", + "# The host on which the database is located (default: not defined)", + CONF_OFFSET(pgsql_host), + copy_string, + print_string + }, + { + "pgsql_user", + "# User account name for PostgreSQL database (default: not defined)", + CONF_OFFSET(pgsql_user), + copy_string, + print_string + }, + { + "pgsql_password", + "# User password for PostgreSQL database (default: not defined)", + CONF_OFFSET(pgsql_password), + copy_string, + print_string + }, + { + "pgsql_port", + "# Port on which the PostgreSQL database is located (default: 5432)", + CONF_OFFSET(pgsql_port), + copy_int, + print_int + }, +#endif /* HAVE_PGSQL */ + + { + "video_pipe", + "\n############################################################\n" + "# Video Loopback Device (vloopback project)\n" + "############################################################\n\n" + "# Output images to a video4linux loopback device\n" + "# The value '-' means next available (default: not defined)", + CONF_OFFSET(vidpipe), + copy_string, + print_string + }, + { + "motion_video_pipe", + "# Output motion images to a video4linux loopback device\n" + "# The value '-' means next available (default: not defined)", + CONF_OFFSET(motionvidpipe), + copy_string, + print_string + }, +/* + { + "debug_parameter", + "\n############################################################\n" + "# Thread Config Files - One for each camera\n" + "# If Only One Camera - Use default values in this file\n" + "############################################################\n\n", + //"# Debug option for programmers - not normally used", + CONF_OFFSET(debug_parameter), + copy_int, + print_int + }, +*/ + { + "thread", + "\n##############################################################\n" + "# Thread config files - One for each camera.\n" + "# Except if only one camera - You only need this config file.\n" + "# If you have more than one camera you MUST define one thread\n" + "# config file for each camera in addition to this config file.\n" + "##############################################################\n", + 0, + config_thread, + print_thread + }, + { NULL, NULL, 0 , NULL, NULL } +}; + +/* conf_cmdline sets the conf struct options as defined by the command line. + * Any option already set from a config file are overridden. + */ +static void conf_cmdline (struct context *cnt, int thread) +{ + struct config *conf=&cnt->conf; + int c; + + /* For the string options, we free() if necessary and malloc() + * if necessary. This is accomplished by calling mystrcpy(); + * see this function for more information. + */ + while ((c=getopt(conf->argc, conf->argv, "c:d:hns?"))!=EOF) + switch (c) { + case 'c': + if (thread==-1) strcpy(cnt->conf_filename, optarg); + break; + case 'n': + cnt->daemon=0; + break; + case 's': + conf->setup_mode=1; + break; + case 'd': + /* no validation - just take what user gives */ + debug_level = atoi(optarg); + break; + case 'h': + case '?': + default: + usage(); + exit(1); + } + optind=1; +} + + +/* conf_cmdparse sets a config option given by 'cmd' to the value given by 'arg1'. + * Based on the name of the option it searches through the struct 'config_params' + * for an option where the config_params[i].param_name matches the option. + * By calling the function pointed to by config_params[i].copy the option gets + * assigned. + */ +struct context **conf_cmdparse(struct context **cnt, const char *cmd, const char *arg1) +{ + int i = 0; + + if(!cmd) + return cnt; + + /* We search through config_params until we find a param_name that matches + * our option given by cmd (or reach the end = NULL) + */ + while( config_params[i].param_name != NULL ) { + if(!strncasecmp(cmd, config_params[i].param_name , 255 + 50)) { // Why +50? + if(config_params[i].conf_value && !arg1) + return cnt; + /* We call the function given by the pointer config_params[i].copy + * If the option is a bool, copy_bool is called. + * If the option is an int, copy_int is called. + * If the option is a string, copy_string is called. + * If the option is a thread, config_thread is called. + * The arguments to the function are: + * cnt - a pointer to the context structure + * arg1 - a pointer to the new option value (represented as string) + * config_params[i].conf_value - an integer value which is a pointer + * to the context structure member relative to the pointer cnt. + */ + cnt=config_params[i].copy( cnt, arg1, config_params[i].conf_value ); + return cnt; + } + i++; + } + + /* We reached the end of config_params without finding a matching option */ + motion_log(LOG_ERR, 0, "Unknown config option \"%s\"",cmd); + + return cnt; +} + +/* conf_process walks through an already open config file line by line + * Any line starting with '#' or ';' or empty lines are ignored as a comments. + * Any non empty line is process so that the first word is the name of an option 'cnd' + * and the rest of the line is the argument 'arg1' + * White space before the first word, between option and argument and end of the line + * is discarded. A '=' between option and first word in argument is also discarded. + * Quotation marks round the argument are also discarded. + * For each option/argument pair the function conf_cmdparse is called which takes + * care of assigning the value to the option in the config structures. + */ +static struct context **conf_process(struct context **cnt, FILE *fp) +{ + /* process each line from the config file */ + + char line[PATH_MAX], *cmd = NULL, *arg1 = NULL; + char *beg = NULL, *end = NULL; + + while (fgets(line, PATH_MAX-1, fp)) { + if(!(line[0]=='#' || line[0]==';' || strlen(line)< 2)) {/* skipcomment */ + + arg1 = NULL; + + /* trim white space and any CR or LF at the end of the line */ + end = line + strlen(line) - 1; /* Point to the last non-null character in the string */ + while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') { + end--; + } + *(end+1) = '\0'; + + /* If line is only whitespace we continue to the next line */ + if (strlen(line) == 0) + continue; + + /* trim leading whitespace from the line and find command */ + beg = line; + while (*beg == ' ' || *beg == '\t') { + beg++; + } + + cmd = beg; /* command starts here */ + + while (*beg != ' ' && *beg != '\t' && *beg != '=' && *beg != '\0') { + beg++; + } + *beg = '\0'; /* command string terminates here */ + + /* trim space between command and argument */ + beg++; + + if (strlen(beg) > 0) { + while (*beg == ' ' || *beg == '\t' || *beg == '=') { + beg++; + } + + /* If argument is in "" we will strip them off + It is important that we can use "" so that we can use + leading spaces in text_left and text_right */ + if ((beg[0]=='"' && beg[strlen(beg)-1]=='"') || + (beg[0]=='\'' && beg[strlen(beg)-1]=='\'')) { + beg[strlen(beg)-1]='\0'; + beg++; + } + + arg1 = beg; /* Argument starts here */ + } + /* else arg1 stays null pointer */ + + cnt = conf_cmdparse(cnt, cmd, arg1); + } + } + + return cnt; +} + + +/* conf_print is used to write out the config file(s) motion.conf and any thread + * config files. The function is called when using http remote control. + */ +void conf_print(struct context **cnt) +{ + const char *retval; + char *val; + int i, thread; + FILE *conffile; + + for (thread=0; cnt[thread]; thread++) { + motion_log(LOG_INFO, 1, "Writing config file to %s",cnt[thread]->conf_filename); + conffile=myfopen(cnt[thread]->conf_filename, "w"); + if (!conffile) + continue; + fprintf(conffile, "# %s\n", cnt[thread]->conf_filename); + fprintf(conffile, "#\n# This config file was generated by motion " VERSION "\n"); + fprintf(conffile, "\n\n"); + for (i=0; config_params[i].param_name; i++) { + retval=config_params[i].print(cnt, NULL, i, thread); + /*If config parameter has a value (not NULL) print it to the config file*/ + if (retval) { + fprintf(conffile, "%s\n", config_params[i].param_help); + /* If the option is a text_* and first char is a space put + quotation marks around to allow leading spaces */ + if (strncmp(config_params[i].param_name, "text", 4) || strncmp(retval, " ", 1)) + fprintf(conffile, "%s %s\n\n", config_params[i].param_name, retval); + else + fprintf(conffile, "%s \"%s\"\n\n", config_params[i].param_name, retval); + } else { + val = NULL; + config_params[i].print(cnt, &val, i, thread); + /* It can either be a thread file parameter or a disabled parameter + If it is a thread parameter write it out + Else write the disabled option to the config file but with a + comment mark in front of the parameter name */ + if (val) { + fprintf(conffile,"%s\n", config_params[i].param_help); + fprintf(conffile, "%s\n", val); + if (strlen(val)==0) + fprintf(conffile,"; thread /usr/local/etc/thread1.conf\n"); + free(val); + } else if (thread==0) { + fprintf(conffile,"%s\n", config_params[i].param_help); + fprintf(conffile,"; %s value\n\n", config_params[i].param_name); + } + } + } + + fprintf(conffile, "\n"); + fclose(conffile); + conffile=NULL; + } +} + +/************************************************************************** + * conf_load is the main function, called from motion.c + * The function sets the important context structure "cnt" including + * loading the config parameters from config files and command line. + * The following takes place in the function: + * - The default start values for cnt stored in the struct conf_template + * are copied to cnt[0] which is the default context structure common to + * all threads. + * - All config (cnt.conf) struct members pointing to a string are changed + * so that they point to a malloc'ed piece of memory containing a copy of + * the string given in conf_template. + * - motion.conf is opened and processed. The process populates the cnt[0] and + * for each thread config file it populates a cnt[1], cnt[2]... for each + * thread + * - Finally it process the options given in the command line. This is done + * for each thread cnt[i] so that the command line options overrides any + * option given by motion.conf or a thread config file. + **************************************************************************/ +struct context ** conf_load (struct context **cnt) +{ + FILE *fp=NULL; + char filename[PATH_MAX]; + int i; + /* We preserve argc and argv because they get overwritten by the memcpy command */ + char **argv = cnt[0]->conf.argv; + int argc = cnt[0]->conf.argc; + + /* Copy the template config structure with all the default config values + * into cnt[0]->conf + */ + memcpy(&cnt[0]->conf, &conf_template, sizeof(struct config)); + + /* For each member of cnt[0] which is a pointer to a string + * if the member points to a string in conf_template and is not NULL + * 1. Reserve (malloc) memory for the string + * 2. Copy the conf_template given string to the reserved memory + * 3. Change the cnt[0] member (char*) pointing to the string in reserved memory + * This ensures that we can free and malloc the string when changed + * via http remote control or config file or command line options + */ + malloc_strings(cnt[0]); + + /* Restore the argc and argv */ + cnt[0]->conf.argv = argv; + cnt[0]->conf.argc = argc; + + /* Open the motion.conf file. We try in this sequence: + * 1. commandline + * 2. current working directory + * 3. $HOME/.motion/motion.conf + * 4. sysconfig/motion.conf + */ + /* Get filename from commandline */ + cnt[0]->conf_filename[0] = 0; + conf_cmdline(cnt[0], -1); + if (cnt[0]->conf_filename[0]){ /* User has supplied filename on commandline*/ + strcpy(filename, cnt[0]->conf_filename); + fp = fopen (filename, "r"); + } + if (!fp){ /* Commandline didn't work, try current dir */ + if (cnt[0]->conf_filename[0]) + motion_log(-1, 1, "Configfile %s not found - trying defaults.", filename); + sprintf(filename, "motion.conf"); + fp = fopen (filename, "r"); + } + if (!fp) { /* specified file does not exist... try default file */ + sprintf(filename, "%s/.motion/motion.conf", getenv("HOME")); + fp = fopen (filename, "r"); + if (!fp) { + sprintf(filename, "%s/motion.conf", sysconfdir); + fp = fopen (filename, "r"); + if (!fp) /* there is no config file.... use defaults */ + motion_log(-1, 1, "could not open configfile %s",filename); + } + } + + /* Now we process the motion.conf config file and close it*/ + if (fp) { + strcpy(cnt[0]->conf_filename, filename); + motion_log(LOG_INFO, 0, "Processing thread 0 - config file %s",filename); + cnt=conf_process(cnt, fp); + fclose(fp); + } + + + /* For each thread (given by cnt[i]) being not null + * cnt is an array of pointers to a context type structure + * cnt[0] is the default context structure + * cnt[1], cnt[2], ... are context structures for each thread + * Command line options always wins over config file options + * so we go through each thread and overrides any set command line + * options + */ + i=-1; + while(cnt[++i]) + conf_cmdline(cnt[i], i); + + return cnt; +} + +/* malloc_strings goes through the members of a context structure. + * For each context structure member which is a pointer to a string it does this: + * If the member points to a string and is not NULL + * 1. Reserve (malloc) memory for the string + * 2. Copy the original string to the reserved memory + * 3. Change the cnt member (char*) pointing to the string in reserved memory + * This ensures that we can free and malloc the string if it is later changed + */ +void malloc_strings (struct context * cnt) +{ + int i = 0; + char **val; + while( config_params[i].param_name != NULL ) { + if (config_params[i].copy == copy_string) { /* if member is a string */ + /* val is made to point to a pointer to the current string */ + val = (char **)((char *)cnt+config_params[i].conf_value); + + /* if there is a string, malloc() space for it, copy + * the string to new space, and point to the new + * string. we don't free() because we're copying a + * static string. + */ + *val = mystrdup(*val); + } + i++; + } +} + +/************************************************************************ + * copy functions + * + * copy_bool - convert a bool representation to int + * copy_int - convert a string to int + * copy_string - just a string copy + * + * @param str - A char *, pointing to a string representation of the + * value. + * @param val_ptr - points to the place where to store the value relative + * to pointer pointing to the given context structure + * @cnt - points to a context structure for a thread + * + * The function is given a pointer cnt to a context structure and a pointer val_ptr + * which is an integer giving the position of the structure member relative to the + * pointer of the context structure. + * If the context structure is for thread 0 (cnt[0]->threadnr is zero) then the + * function also sets the value for all the child threads since thread 0 is the + * global thread. + * If the thread given belongs to a child thread (cnt[0]->threadnr is not zero) + * the function will only assign the value for the given thread. + ***********************************************************************/ + +/* copy_bool assigns a config option to a new boolean value. + * The boolean is given as a string in str which is coverted to 0 or 1 + * by the function. Values 1, yes and on are converted to 1 ignoring case. + * Any other value is converted to 0. + */ +static struct context **copy_bool (struct context **cnt, const char *str, int val_ptr) +{ + void *tmp; + int i; + + i=-1; + while(cnt[++i]) { + tmp = (char *)cnt[i]+(int)val_ptr; + if ( !strcmp(str, "1") || !strcasecmp(str, "yes") || !strcasecmp(str,"on")) { + *((int *)tmp) = 1; + } else { + *((int *)tmp) = 0; + } + if (cnt[0]->threadnr) + return cnt; + } + return cnt; +} + +/* copy_int assigns a config option to a new interger value. + * The integer is given as a string in str which is coverted to integer by the function. + */ +static struct context ** copy_int(struct context **cnt, const char *str, int val_ptr) +{ + void *tmp; + int i; + + i=-1; + while(cnt[++i]) { + tmp = (char *)cnt[i]+val_ptr; + *((int *)tmp) = atoi(str); + if (cnt[0]->threadnr) + return cnt; + } + return cnt; +} + +/* copy_string assigns a new string value to a config option. + * Strings are handled differently from bool and int. + * the char *conf->option that we are working on is free()'d + * (if memory for it has already been malloc()'d), and set to + * a freshly malloc()'d string with the value from str, + * or NULL if str is blank + */ +struct context **copy_string(struct context **cnt, const char *str, int val_ptr) +{ + char **tmp; + int i; + + i=-1; + while(cnt[++i]) { + tmp = (char **)((char *)cnt[i] + val_ptr); + + /* mystrcpy assigns the new string value + * including free'ing and reserving new memory for it. + */ + *tmp = mystrcpy(*tmp, str); + + /* set the option on all threads if setting the option + * for thread 0; otherwise just set that one thread's option + */ + if (cnt[0]->threadnr) + return cnt; + } + return cnt; +} + + +/* mystrcpy is used to assign string type fields (e.g. config options) + * In a way so that we the memory is malloc'ed to fit the string. + * If a field is already pointing to a string (not NULL) the memory of the + * old string is free'd and new memory is malloc'ed and filled with the + * new string is copied into the the memory and with the char pointer + * pointing to the new string. + * + * from - pointer to the new string we want to copy + * to - the pointer to the current string (or pointing to NULL) + * If not NULL the memory it points to is fre'ed. + * function returns pointer to the new string which is in malloc'ed memory + * FIXME The strings that are malloc'ed with this function should be freed + * when the motion program is terminated normally instead of relying on the + * OS to clean up. + */ +char *mystrcpy(char *to, const char *from) +{ + /* free the memory used by the to string, if such memory exists, + * and return a pointer to a freshly malloc()'d string with the + * same value as from. + */ + + if (to != NULL) + free(to); + + return mystrdup(from); +} + + +/* mystrdup return a pointer to a freshly malloc()'d string with the same + * value as the string that the input parameter 'from' points to, + * or NULL if the from string is 0 characters. + * The function truncates the string to the length given by the environment + * variable PATH_MAX to ensure that config options can always contain + * a really long path but no more than that. + */ +char *mystrdup(const char *from) +{ + char *tmp; + int stringlength; + + if (from == NULL || !strlen(from)) { + tmp = NULL; + } else { + stringlength = strlen(from); + stringlength = (stringlength < PATH_MAX ? stringlength : PATH_MAX); + tmp = (char *)mymalloc(stringlength + 1); + strncpy(tmp, from, stringlength); + + /* We must ensure the string always has a NULL terminator. + * This necessary because strncpy will not append a NULL terminator + * if the original string is greater than stringlength. + */ + tmp += stringlength; + *tmp = '\0'; + tmp -= stringlength; + } + return tmp; +} + +const char *config_type(config_param *configparam) +{ + if (configparam->copy == copy_string) + return "string"; + if (configparam->copy == copy_int) + return "int"; + if (configparam->copy == copy_bool) + return "bool"; + return "unknown"; +} + +static const char *print_bool(struct context **cnt, char **str ATTRIBUTE_UNUSED, + int parm, int threadnr) +{ + int val=config_params[parm].conf_value; + + if (threadnr && + *(int*)((char *)cnt[threadnr] + val) == *(int*)((char *)cnt[0] + val)) + return NULL; + + if (*(int*)((char *)cnt[threadnr] + val)) + return "on"; + else + return "off"; +} + +/* print_string returns a pointer to a string containing the value of the config option + * If the option is not defined NULL is returned. + * If the thread number is not 0 the string is compared with the value of the same + * option in thread 0. If the value is the same, NULL is returned which means that + * the option is not written to the thread config file. + */ +static const char *print_string(struct context **cnt, + char **str ATTRIBUTE_UNUSED, int parm, + int threadnr) +{ + int val=config_params[parm].conf_value; + const char **cptr0, **cptr1; + + /* strcmp does not like NULL so we have to check for this also */ + cptr0 = (const char **)((char *)cnt[0] + val); + cptr1 = (const char **)((char *)cnt[threadnr] + val); + if ((threadnr) && (*cptr0 != NULL) && (*cptr1 != NULL) && (!strcmp(*cptr0, *cptr1))) + return NULL; + + return *cptr1; +} + +static const char *print_int(struct context **cnt, char **str ATTRIBUTE_UNUSED, + int parm, int threadnr) +{ + static char retval[20]; + int val = config_params[parm].conf_value; + + if (threadnr && + *(int*)((char *)cnt[threadnr] + val) == *(int*)((char *)cnt[0] + val)) + return NULL; + + sprintf(retval, "%d", *(int*)((char *)cnt[threadnr] + val)); + + return retval; +} + +static const char *print_thread(struct context **cnt, char **str, + int parm ATTRIBUTE_UNUSED, int threadnr) +{ + char *retval; + int i=0; + + if (!str || threadnr) + return NULL; + + retval = mymalloc(1); + retval[0] = 0; + while (cnt[++i]) { + retval = myrealloc(retval, strlen(retval) + strlen(cnt[i]->conf_filename)+10, "print_thread"); + sprintf(retval + strlen(retval), "thread %s\n", cnt[i]->conf_filename); + } + *str = retval; + + return NULL; +} + +/* config_thread() is called during initial config file loading each time Motion + * finds a thread option in motion.conf + * The size of the context array is increased and the main context's values are + * copied to the new thread. + * + * cnt - pointer to the array of pointers pointing to the context structures + * str - pointer to a string which is the filename of the thread config file + * val - is not used. It is defined to be function header compatible with + * copy_int, copy_bool and copy_string. + */ +static struct context **config_thread(struct context **cnt, const char *str, + int val ATTRIBUTE_UNUSED) +{ + int i; + FILE *fp; + + if (cnt[0]->threadnr) + return cnt; + + fp=fopen(str, "r"); + if (!fp) { + motion_log(LOG_ERR, 1, "Thread config file %s not found",str); + return cnt; + } + + /* Find the current number of threads defined. */ + i=-1; + while (cnt[++i]); + + /* Make space for the threads + the terminating NULL pointer + * in the array of pointers to context structures + * First thread is 0 so the number of threads is i+1 + * plus an extra for the NULL pointer. This gives i+2 + */ + cnt = myrealloc(cnt, sizeof(struct context *)*(i+2), "config_thread"); + + /* Now malloc space for an additional context structure for thread nr. i */ + cnt[i] = mymalloc(sizeof(struct context)); + + /* And make this an exact clone of the context structure for thread 0 */ + memcpy(cnt[i], cnt[0], sizeof(struct context)); + + /* All the integers are copies of the actual value. + * The strings are all pointers to strings so we need to create + * unique malloc'ed space for all the strings that are not NULL and + * change the string pointers to point to the new strings. + * malloc_strings takes care of this. + */ + malloc_strings(cnt[i]); + + /* Mark the end if the array of pointers to context structures */ + cnt[i+1] = NULL; + + /* process the thread's config file and notify user on console */ + strcpy(cnt[i]->conf_filename, str); + motion_log(LOG_INFO, 0, "Processing config file %s", str); + conf_process(cnt+i, fp); + + /* Finally we close the thread config file */ + fclose(fp); + + return cnt; +} + +static void usage () +{ + printf("motion Version "VERSION", Copyright 2000-2005 Jeroen Vreeken/Folkert van Heusden/Kenneth Lavrsen\n"); + printf("\nusage:\tmotion [options]\n"); + printf("\n\n"); + printf("Possible options:\n\n"); + printf("-n\t\tRun in non-daemon mode.\n"); + printf("-s\t\tRun in setup mode.\n"); + printf("-c config\tFull path and filename of config file.\n"); + printf("-d level\t\tDebug mode.\n"); + printf("-h\t\tShow this screen.\n"); + printf("\n"); + printf("Motion is configured using a config file only. If none is supplied,\n"); + printf("it will read motion.conf from current directory, ~/.motion or %s.\n", sysconfdir); + printf("\n"); + +} diff --git a/conf.h b/conf.h new file mode 100644 index 0000000..c1a8d86 --- /dev/null +++ b/conf.h @@ -0,0 +1,154 @@ +/* + ** + ** conf.h - function prototypes for the config handling routines + ** + ** Originally written for the dproxy package by Matthew Pratt. + ** + ** Copyright 2000 Jeroen Vreeken (pe1rxq@chello.nl) + ** + ** This software is licensed under the terms of the GNU General + ** Public License (GPL). Please see the file COPYING for details. + ** + ** +*/ + +#ifndef _INCLUDE_CONF_H +#define _INCLUDE_CONF_H + +/* + more parameters may be added later. + */ +struct config { + int setup_mode; + int width; + int height; + int quality; + int rotate_deg; + int max_changes; + int threshold_tune; + const char *output_normal; + int motion_img; + int output_all; + int gap; + int maxmpegtime; + int snapshot_interval; + const char *locate; + int input; + int norm; + int frame_limit; + int quiet; + int ppm; + int noise; + int noise_tune; + int mingap; + int lightswitch; + int nightcomp; + unsigned int low_cpu; + int nochild; + int autobright; + int brightness; + int contrast; + int saturation; + int hue; + int roundrobin_frames; + int roundrobin_skip; + int pre_capture; + int post_capture; + int switchfilter; + int ffmpeg_cap_new; + int ffmpeg_cap_motion; + int ffmpeg_bps; + int ffmpeg_vbr; + const char *ffmpeg_video_codec; + int webcam_port; + int webcam_quality; + int webcam_motion; + int webcam_maxrate; + int webcam_localhost; + int webcam_limit; + int control_port; + int control_localhost; + int control_html_output; + const char *control_authentication; + int frequency; + int tuner_number; + int timelapse; + const char *timelapse_mode; +#ifdef __freebsd__ + const char *tuner_device; +#endif + const char *video_device; + const char *vidpipe; + const char *filepath; + const char *jpegpath; + const char *mpegpath; + const char *snappath; + const char *timepath; + char *on_event_start; + char *on_event_end; + const char *mask_file; + int smart_mask_speed; + int sql_log_image; + int sql_log_snapshot; + int sql_log_mpeg; + int sql_log_timelapse; + const char *sql_query; + const char *mysql_db; + const char *mysql_host; + const char *mysql_user; + const char *mysql_password; + char *on_picture_save; + char *on_motion_detected; + char *on_movie_start; + char *on_movie_end; + const char *motionvidpipe; + const char *netcam_url; + const char *netcam_userpass; + const char *netcam_proxy; + const char *pgsql_db; + const char *pgsql_host; + const char *pgsql_user; + const char *pgsql_password; + int pgsql_port; + int text_changes; + const char *text_left; + const char *text_right; + const char *text_event; + int text_double; + const char *despeckle; + int minimum_motion_frames; + // int debug_parameter; + int argc; + char **argv; +}; + +/** + * typedef for a param copy function. + */ +typedef struct context ** (* conf_copy_func)(struct context **, const char *, int); +typedef const char *(* conf_print_func)(struct context **, char **, int, int); + +/** + * description for parameters in the config file + */ +typedef struct { + const char * param_name; /* name for this parameter */ + const char * param_help; /* short explanation for parameter */ + int conf_value; /* pointer to a field in struct context */ + conf_copy_func copy; /* a function to set the value in 'config' */ + conf_print_func print; /* a function to output the value to a file */ +} config_param; + + +extern config_param config_params[]; + +struct context **conf_load (struct context **); +struct context **conf_cmdparse(struct context **, const char *, const char *); +const char *config_type(config_param *); +void conf_print (struct context **); +void malloc_strings (struct context *); +char *mystrdup(const char *); +char *mystrcpy(char *, const char *); +struct context **copy_string(struct context **, const char *, int); + +#endif /* _INCLUDE_CONF_H */ diff --git a/configure b/configure new file mode 100755 index 0000000..7404af9 --- /dev/null +++ b/configure @@ -0,0 +1,6604 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="motion.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS VERSION VIDEO CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT FFMPEG_OBJ CPP EGREP BIN_PATH LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # 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 this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --without-bktr Exclude to use bktr subsytem , that usually usefull + for devices as network cameras. + + + --without-v4l Exclude using v4l (video4linux) subsystem. + Makes Motion so it only supports network cameras. + + --with-jpeg-mmx=DIR Specify the prefix for the install path for + jpeg-mmx for optimized jpeg handling (optional). + If this is not specified motion will try to find + the library /usr/lib/libjpeg-mmx.a /usr/local/lib/libjpeg-mmx.a. + + --with-ffmpeg=DIR Specify the prefix for the install path for + libavcodec/libavformat (part of ffmpeg) be able to + encode mpeg movies realtime. + If this is not specified motion will try to find + the libraries in /usr and /usr/local. + + --with-mysql=DIR Normally, configure will scan all possible default + installation paths for mysql. When its fail, use + this command to tell configure where mysql + installation root directory is. + + --with-pgsql=DIR Include PostgreSQL support. DIR is the PostgreSQL + base install directory. If not specified configure will + search in /usr, /usr/local and /usr/local/pgsql. + + --without-optimizecpu Exclude autodetecting platform and cpu type. + This will disable the compilation of gcc + optimizing code by platform and cpu. + + --with-developer-flags Causes practically all of the possible gcc + warning flags to be set. This may produce + a large amount of warnings. + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + +VERSION=3.2.5 + + +THREAD_CFLAGS="" +THREAD_CHECK="pthread.h" + +Darwin="" +FreeBSD="" + + +echo "$as_me:$LINENO: checking for Darwin" >&5 +echo $ECHO_N "checking for Darwin... $ECHO_C" >&6 +Darwin=`uname -a | grep "Darwin"` + +if test "${Darwin}" = ""; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking for *BSD" >&5 +echo $ECHO_N "checking for *BSD... $ECHO_C" >&6 + + FreeBSD=`uname -a | grep "BSD"` + if test "${FreeBSD}" = ""; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + VIDEO="video.o" + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + THREAD_CHECK="/usr/local/include/pthread/linuxthreads/pthread.h" + THREAD_LIB_CHECK="/usr/local/lib/liblthread.so" + TEMP_CFLAGS="${CFLAGS} -D__freebsd__ -I/usr/local/include" + TEMP_LDFLAGS="${LDFLAGS} -L/usr/local/lib" + TEMP_LIBS="-L/usr/local/lib" + VIDEO="video_freebsd.o" + fi +else + TEMP_CFLAGS="${CFLAGS} -I/sw/include -D__freebsd__" + TEMP_LDFLAGS="${LDFLAGS} -L/sw/lib" + TEMP_LIBS="-L/sw/lib" + VIDEO="video_freebsd.o" + FINK_LIB="-L/sw/lib" + Darwin="yes" + echo "$as_me:$LINENO: result: $Darwin" >&5 +echo "${ECHO_T}$Darwin" >&6 +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +TEMP_LIBS="-lm ${TEMP_LIBS}" +TEMP_CFLAGS="${TEMP_CFLAGS} ${CFLAGS}" +TEMP_LDFLAGS="${TEMP_LDFLAGS} ${LDFLAGS}" + + +if test "${FreeBSD}" != ""; then + +BKTR="yes" + +# Check whether --with-bktr or --without-bktr was given. +if test "${with_bktr+set}" = set; then + withval="$with_bktr" + BKTR="$withval" + +fi; + + + if test "${BKTR}" = "no"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DWITHOUT_V4L" + fi + +else + +V4L="yes" + +# Check whether --with-v4l or --without-v4l was given. +if test "${with_v4l+set}" = set; then + withval="$with_v4l" + V4L="$withval" + +fi; + +fi + + +if test "${Darwin}" = "yes"; then + V4L="no" +fi + +if test "${V4L}" = "no"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DWITHOUT_V4L" +fi + + +if test "${FreeBSD}" != ""; then + +echo "$as_me:$LINENO: checking for linuxthreads" >&5 +echo $ECHO_N "checking for linuxthreads... $ECHO_C" >&6 + + if test -f "${THREAD_CHECK}"; then + HEADERS_THREAD_CFLAGS="-I/usr/local/include/pthread/linuxthreads" + THREADS="yes" + else + THREADS="no" + fi + + if test -f "${THREAD_LIB_CHECK}" ; then + THREADS="yes" + LIB_THREAD="-llthread -llgcc_r" + else + THREADS="no" + fi + + + if test "${THREADS}" = "yes"; then + TEMP_CFLAGS="${HEADERS_THREAD_CFLAGS} $TEMP_CFLAGS -DWITH_LINUXTREADS" + TEMP_LIBS="$TEMP_LIBS ${LIB_THREAD}" + THREAD_CFLAGS="-D_THREAD_SAFE" + PTHREAD_SUPPORT="yes" + echo "$as_me:$LINENO: result: $THREADS" >&5 +echo "${ECHO_T}$THREADS" >&6 + else + PTHREAD_SUPPORT="no" + echo "$as_me:$LINENO: result: $THREADS" >&5 +echo "${ECHO_T}$THREADS" >&6 + echo + echo You do not have linuxthread installed + echo + fi + +else + + +echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5 +echo $ECHO_N "checking for pthread_create in -lpthread... $ECHO_C" >&6 +if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_create (); +int +main () +{ +pthread_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pthread_pthread_create=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pthread_pthread_create=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_create" >&5 +echo "${ECHO_T}$ac_cv_lib_pthread_pthread_create" >&6 +if test $ac_cv_lib_pthread_pthread_create = yes; then + + TEMP_LIBS="$TEMP_LIBS -lpthread" + PTHREAD_SUPPORT="yes" + +else + + echo + echo You do not have pthread installed + echo + + +fi + + +fi + + + +JPEG_MMX="no" +JPEG_MMX_OK="not_found" + +# Check whether --with-jpeg-mmx or --without-jpeg-mmx was given. +if test "${with_jpeg_mmx+set}" = set; then + withval="$with_jpeg_mmx" + JPEG_MMX="$withval" + +fi; + + +if test "${JPEG_MMX}" = "no"; then + echo "$as_me:$LINENO: checking for libjpeg-mmx" >&5 +echo $ECHO_N "checking for libjpeg-mmx... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: skipping" >&5 +echo "${ECHO_T}skipping" >&6 +elif test "${JPEG_MMX}" = "yes"; then + echo "$as_me:$LINENO: checking for libjpeg-mmx autodetecting" >&5 +echo $ECHO_N "checking for libjpeg-mmx autodetecting... $ECHO_C" >&6 + + if test -f /usr/lib/libjpeg-mmx.a ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + JPEG_MMX_OK="found" + JPEG_MMX="/usr/lib" + elif test -f /usr/local/lib/libjpeg-mmx.a ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + JPEG_MMX_OK="found" + JPEG_MMX="/usr/local/lib" + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + fi +else + echo "$as_me:$LINENO: checking for libjpeg-mmx in -> ${JPEG_MMX} <-" >&5 +echo $ECHO_N "checking for libjpeg-mmx in -> ${JPEG_MMX} <-... $ECHO_C" >&6 + if test -f ${JPEG_MMX}/libjpeg-mmx.a ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + JPEG_MMX_OK="found" + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + fi +fi + +if test "${JPEG_MMX_OK}" = "found"; then + OLD_CFLAGS="$CFLAGS" + OLD_LIBS="$LIBS" + CFLAGS="$CFLAGS -I${JPEG_MMX}" + LIBS="$LIBS -L${JPEG_MMX}" + echo "$as_me:$LINENO: checking for jpeg_start_compress in -ljpeg-mmx" >&5 +echo $ECHO_N "checking for jpeg_start_compress in -ljpeg-mmx... $ECHO_C" >&6 +if test "${ac_cv_lib_jpeg_mmx_jpeg_start_compress+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljpeg-mmx $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char jpeg_start_compress (); +int +main () +{ +jpeg_start_compress (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_jpeg_mmx_jpeg_start_compress=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_jpeg_mmx_jpeg_start_compress=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_jpeg_mmx_jpeg_start_compress" >&5 +echo "${ECHO_T}$ac_cv_lib_jpeg_mmx_jpeg_start_compress" >&6 +if test $ac_cv_lib_jpeg_mmx_jpeg_start_compress = yes; then + TEMP_LIBS="$TEMP_LIBS -ljpeg-mmx" + TEMP_CFLAGS="${TEMP_CFLAGS} -I${JPEG_MMX}" + JPEG_SUPPORT="yes" +fi + + LIBS="$OLD_LIBS" + CFLAGS="$OLD_CFLAGS" + JPEG_SUPPORT_MMX="yes" +fi + +if test x$JPEG_SUPPORT != xyes ; then + LDFLAGS=$TEMP_LDFLAGS + + echo "$as_me:$LINENO: checking for jpeg_set_defaults in -ljpeg" >&5 +echo $ECHO_N "checking for jpeg_set_defaults in -ljpeg... $ECHO_C" >&6 +if test "${ac_cv_lib_jpeg_jpeg_set_defaults+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljpeg $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char jpeg_set_defaults (); +int +main () +{ +jpeg_set_defaults (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_jpeg_jpeg_set_defaults=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_jpeg_jpeg_set_defaults=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_jpeg_jpeg_set_defaults" >&5 +echo "${ECHO_T}$ac_cv_lib_jpeg_jpeg_set_defaults" >&6 +if test $ac_cv_lib_jpeg_jpeg_set_defaults = yes; then + + TEMP_LIBS="$TEMP_LIBS -ljpeg" + JPEG_SUPPORT="yes" + +else + + echo + echo You do not have libjpeg installed + echo + + +fi + +fi + +FFMPEG="yes" +FFMPEG_OK="no_found" +FFMPEG_OBJ="" + +# Check whether --with-ffmpeg or --without-ffmpeg was given. +if test "${with_ffmpeg+set}" = set; then + withval="$with_ffmpeg" + FFMPEG="$withval" + +fi; +if test "${FFMPEG}" = "no"; then + echo "$as_me:$LINENO: checking for ffmpeg" >&5 +echo $ECHO_N "checking for ffmpeg... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: skipping" >&5 +echo "${ECHO_T}skipping" >&6 +else if test "${FFMPEG}" = "yes"; then + echo "$as_me:$LINENO: checking for ffmpeg autodetecting" >&5 +echo $ECHO_N "checking for ffmpeg autodetecting... $ECHO_C" >&6 + +# weird hack to fix debian problem TO BE REMOVED + + if test -f /usr/bin/ffmpeg-config; then + FFMPEG_LIBS_DEB="`ffmpeg-config --libs avformat`" + FFMPEG_CFLAGS_DEB="`ffmpeg-config --cflags`" + FFMPEG_OK="found" + echo "$as_me:$LINENO: result: found for debian" >&5 +echo "${ECHO_T}found for debian" >&6 + elif test -f /usr/lib64/libavcodec.a -o -f /usr/lib64/libavcodec.so && test -f /usr/lib64/libavformat.a -o -f /usr/lib64/libavformat.so ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_OK="found" + FFMPEG="/usr/lib64" + elif test -f /usr/lib/libavcodec.a -o -f /usr/lib/libavcodec.so && test -f /usr/lib/libavformat.a -o -f /usr/lib/libavformat.so ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_OK="found" + FFMPEG="/usr/lib" + elif test -f /usr/local/lib/libavcodec.a -o -f /usr/local/lib/libavcodec.so && test -f /usr/local/lib/libavformat.a -o -f /usr/local/lib/libavformat.so ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_OK="found" + FFMPEG="/usr/local/lib" + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + echo "" + echo "**********************************************" + echo "* libavcodec.a or libavcodec.so or *" + echo "* libavformat.a or libavformat.so not found: *" + echo "* ALL FFMPEG FEATURES DISABLED *" + echo "* *" + echo "* Please read the Motion Guide for help: *" + echo "* http://motion.sourceforge.net *" + echo "**********************************************" + echo "" + fi +else + echo "$as_me:$LINENO: checking for ffmpeg in -> ${FFMPEG} <-" >&5 +echo $ECHO_N "checking for ffmpeg in -> ${FFMPEG} <-... $ECHO_C" >&6 + if test -f ${FFMPEG}/lib/libavcodec.a -o -f ${FFMPEG}/lib/libavcodec.so && test -f ${FFMPEG}/lib/libavformat.a -o -f ${FFMPEG}/lib/libavformat.so ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_OK="found" + FFMPEG="${FFMPEG}/lib" + elif test -f ${FFMPEG}/libavcodec.a -o -f ${FFMPEG}/libavcodec.so && test -f ${FFMPEG}/libavformat.a -o -f ${FFMPEG}/libavformat.so ; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_OK="found" + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + if test "${FFMPEG}" != "no"; then + echo "" + echo "**********************************************" + echo "* libavcodec.a or libavcodec.so or *" + echo "* libavformat.a or libavformat.so not found: *" + echo "* ALL FFMPEG FEATURES DISABLED *" + echo "* *" + echo "* Please read the Motion Guide for help: *" + echo "* http://motion.sourceforge.net *" + echo "**********************************************" + echo "" + fi + fi +fi + +# +# Now check for ffmpeg headers ( avformat.h ) if ffmpeg libs were found +# + +if test "${FFMPEG_OK}" = "found"; then + echo "$as_me:$LINENO: checking for ffmpeg headers" >&5 +echo $ECHO_N "checking for ffmpeg headers... $ECHO_C" >&6 + + if test "${FFMPEG_CFLAGS_DEB}" != "" ; then + FFMPEG_CFLAGS="${FFMPEG_CFLAGS_DEB}" + echo "$as_me:$LINENO: result: found for debian" >&5 +echo "${ECHO_T}found for debian" >&6 + elif test -f ${FFMPEG}/include/avformat.h; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_CFLAGS="-I${FFMPEG}/include" + elif test -f ${FFMPEG}/avformat.h; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_CFLAGS="-I${FFMPEG}" + elif test -f `(dirname ${FFMPEG}) 2>/dev/null || +$as_expr X${FFMPEG} : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X${FFMPEG} : 'X\(//\)[^/]' \| \ + X${FFMPEG} : 'X\(//\)$' \| \ + X${FFMPEG} : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X${FFMPEG} | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/include/avformat.h; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_CFLAGS="-I`(dirname ${FFMPEG}) 2>/dev/null || +$as_expr X${FFMPEG} : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X${FFMPEG} : 'X\(//\)[^/]' \| \ + X${FFMPEG} : 'X\(//\)$' \| \ + X${FFMPEG} : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X${FFMPEG} | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/include" + elif test -f `(dirname ${FFMPEG}) 2>/dev/null || +$as_expr X${FFMPEG} : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X${FFMPEG} : 'X\(//\)[^/]' \| \ + X${FFMPEG} : 'X\(//\)$' \| \ + X${FFMPEG} : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X${FFMPEG} | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/include/ffmpeg/avformat.h; then + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + FFMPEG_CFLAGS="-I`(dirname ${FFMPEG}) 2>/dev/null || +$as_expr X${FFMPEG} : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X${FFMPEG} : 'X\(//\)[^/]' \| \ + X${FFMPEG} : 'X\(//\)$' \| \ + X${FFMPEG} : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X${FFMPEG} | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/include/ffmpeg" + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + FFMPEG_OK="no_found" + echo "**********************************************" + echo "* avformat.h not found: *" + echo "* ALL FFMPEG FEATURES DISABLED *" + echo "* *" + echo "* Please read the Motion Guide for help: *" + echo "* http://motion.sourceforge.net *" + echo "**********************************************" + echo "" + fi + +# +# If ffmpeg libs and headers have been found +# + + if test "${FFMPEG_OK}" = "found"; then + if test "${FFMPEG_LIBS_DEB}" != ""; then + TEMP_LIBS="$TEMP_LIBS ${FFMPEG_LIBS_DEB}" +# TEMP_LDFLAGS="${TEMP_LDFLAGS} ${FFMPEG_LIBS_DEB}" + else + TEMP_LIBS="$TEMP_LIBS -L${FFMPEG} -lavformat -lavcodec -lm -lz" + TEMP_LDFLAGS="${TEMP_LDFLAGS} -L${FFMPEG}" + fi + + TEMP_CFLAGS="${TEMP_CFLAGS} -DHAVE_FFMPEG ${FFMPEG_CFLAGS}" + + FFMPEG_OBJ="ffmpeg.o" + + fi +fi +fi + + +MYSQL="yes" + +echo "$as_me:$LINENO: checking for MySQL" >&5 +echo $ECHO_N "checking for MySQL... $ECHO_C" >&6 + +# Check whether --with-mysql or --without-mysql was given. +if test "${with_mysql+set}" = set; then + withval="$with_mysql" + MYSQL="$withval" + +fi; + +if test "${MYSQL}" = "yes"; then + # Autodetect + for w in /usr/include /usr/local/include /usr/mysql /usr/local/mysql /usr/local/mysql/include /opt /opt/mysql; do + # check for plain setups + if test -f $w/mysql.h; then + MYSQL_INCDIR=$w + break + fi + # check for "/usr/include/" type setups + if test -f $w/mysql/mysql.h; then + MYSQL_INCDIR=$w/mysql + break + fi + # check for "/usr//include" type setups + if test -f $w/mysql/include/mysql.h; then + MYSQL_INCDIR=$w/mysql/include + break + fi + done + + for w in /usr/lib /usr/local/lib /usr/mysql /usr/local/mysql /usr/local/mysql/lib /opt /opt/mysql; do + # check for plain setups + if test -f $w/libmysqlclient.a -o -f $w/libmysqlclient.so; then + MYSQL_LIBDIR=$w + break + fi + # check for "/usr/lib/" type setups + if test -f $w/mysql/libmysqlclient.a -o -f $w/mysql/libmysqlclient.so; then + MYSQL_LIBDIR=$w/mysql + break + fi + # check for "/usr//lib" type setups + if test -f $w/mysql/lib/libmysqlclient.a -o -f $w/mysql/lib/libmysqlclient.so; then + MYSQL_LIBDIR=$w/mysql/lib + break + fi + done +else + # Manual detection for /include/ + # and /include. + if test -f $withval/include/mysql/mysql.h; then + MYSQL_INCDIR=$withval/include/mysql + elif test -f $withval/include/mysql.h; then + MYSQL_INCDIR=$withval/include + fi + + # Manual detection for /lib/ + # and /lib. + + if test -f $withval/lib/mysql/libmysqlclient.a -o -f $withval/lib/mysql/libmysqlclient.so; then + MYSQL_LIBDIR=$withval/lib/mysql + elif test -f $withval/lib/libmysqlclient.a -o -f $withval/lib/libmysqlclient.so; then + MYSQL_LIBDIR=$withval/lib + fi +fi + +## Did we find anything? +if test "${MYSQL}" = "no"; then + echo "$as_me:$LINENO: result: skipped" >&5 +echo "${ECHO_T}skipped" >&6 +else + if test -z "$MYSQL_LIBDIR" ; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo Invalid MySQL directory - unable to find libmysqlclient.a or libmysqlclient.so. + elif test -z "$MYSQL_INCDIR" ; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo Invalid MySQL directory - unable to find mysql.h. + else + TEMP_LIBS="$TEMP_LIBS -L$MYSQL_LIBDIR -lmysqlclient" + #Add -lz for some mysql installs.... + TEMP_LIBS="$TEMP_LIBS -lz" + TEMP_CFLAGS="$TEMP_CFLAGS -DHAVE_MYSQL -I $MYSQL_INCDIR" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + MYSQL_SUPPORT="yes" + fi +fi + + +PGSQL="yes" + +echo "$as_me:$LINENO: checking for PostgreSQL" >&5 +echo $ECHO_N "checking for PostgreSQL... $ECHO_C" >&6 + +# Check whether --with-pgsql or --without-pgsql was given. +if test "${with_pgsql+set}" = set; then + withval="$with_pgsql" + PGSQL="$withval" + +fi; +if test "${PGSQL}" = "no"; then + echo "$as_me:$LINENO: result: skipped" >&5 +echo "${ECHO_T}skipped" >&6 +fi + +if test "${PGSQL}" = "yes"; then + for i in /usr /usr/local /usr/local/pgsql $PHP_PGSQL; do + if test -r $i/include/libpq-fe.h; then PGSQL_DIR=$i; PGSQL_INCDIR=$i/include + elif test -r $i/include/pgsql/libpq-fe.h; then PGSQL_DIR=$i; PGSQL_INCDIR=$i/include/pgsql + elif test -r $i/include/postgresql/libpq-fe.h; then PGSQL_DIR=$i; PGSQL_INCDIR=$i/include/postgresql + fi + done + + if test -z "$PGSQL_DIR"; then + echo "$as_me:$LINENO: result: Cannot find libpq-fe.h. Please specify the installation path of PostgreSQL" >&5 +echo "${ECHO_T}Cannot find libpq-fe.h. Please specify the installation path of PostgreSQL" >&6 + else + PGSQL_INCLUDE="-I$PGSQL_INCDIR" + PGSQL_LIBDIR=$PGSQL_DIR/lib + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + test -d $PGSQL_DIR/lib/pgsql && PGSQL_LIBDIR=$PGSQL_DIR/lib/pgsql + LDFLAGS="$TEMP_LDFLAGS -L$PGSQL_LIBDIR" + echo "$as_me:$LINENO: checking for PQcmdTuples in -lpq" >&5 +echo $ECHO_N "checking for PQcmdTuples in -lpq... $ECHO_C" >&6 +if test "${ac_cv_lib_pq_PQcmdTuples+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpq $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char PQcmdTuples (); +int +main () +{ +PQcmdTuples (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pq_PQcmdTuples=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pq_PQcmdTuples=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pq_PQcmdTuples" >&5 +echo "${ECHO_T}$ac_cv_lib_pq_PQcmdTuples" >&6 +if test $ac_cv_lib_pq_PQcmdTuples = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PQCMDTUPLES 1 +_ACEOF + +fi + + echo "$as_me:$LINENO: checking for PQoidValue in -lpq" >&5 +echo $ECHO_N "checking for PQoidValue in -lpq... $ECHO_C" >&6 +if test "${ac_cv_lib_pq_PQoidValue+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpq $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char PQoidValue (); +int +main () +{ +PQoidValue (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pq_PQoidValue=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pq_PQoidValue=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pq_PQoidValue" >&5 +echo "${ECHO_T}$ac_cv_lib_pq_PQoidValue" >&6 +if test $ac_cv_lib_pq_PQoidValue = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PQOIDVALUE 1 +_ACEOF + +fi + + echo "$as_me:$LINENO: checking for PQclientEncoding in -lpq" >&5 +echo $ECHO_N "checking for PQclientEncoding in -lpq... $ECHO_C" >&6 +if test "${ac_cv_lib_pq_PQclientEncoding+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpq $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char PQclientEncoding (); +int +main () +{ +PQclientEncoding (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pq_PQclientEncoding=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pq_PQclientEncoding=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pq_PQclientEncoding" >&5 +echo "${ECHO_T}$ac_cv_lib_pq_PQclientEncoding" >&6 +if test $ac_cv_lib_pq_PQclientEncoding = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PQCLIENTENCODING 1 +_ACEOF + +fi + + echo "$as_me:$LINENO: checking for pg_encoding_to_char in -lpq" >&5 +echo $ECHO_N "checking for pg_encoding_to_char in -lpq... $ECHO_C" >&6 +if test "${ac_cv_lib_pq_pg_encoding_to_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpq $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pg_encoding_to_char (); +int +main () +{ +pg_encoding_to_char (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pq_pg_encoding_to_char=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pq_pg_encoding_to_char=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pq_pg_encoding_to_char" >&5 +echo "${ECHO_T}$ac_cv_lib_pq_pg_encoding_to_char" >&6 +if test $ac_cv_lib_pq_pg_encoding_to_char = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT 1 +_ACEOF + +fi + + LDFLAGS="" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PGSQL 1 +_ACEOF + + TEMP_LIBS="$TEMP_LIBS -L$PGSQL_LIBDIR -lpq" + TEMP_CFLAGS="$TEMP_CFLAGS -DHAVE_PGSQL $PGSQL_INCLUDE" + PostgreSQL_SUPPORT="yes" + fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + +for ac_header in stdio.h stdlib.h unistd.h fcntl.h time.h signal.h sys/ioctl.h sys/mman.h linux/videodev.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking for short int" >&5 +echo $ECHO_N "checking for short int... $ECHO_C" >&6 +if test "${ac_cv_type_short_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((short int *) 0) + return 0; +if (sizeof (short int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_short_int=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_short_int=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_short_int" >&5 +echo "${ECHO_T}$ac_cv_type_short_int" >&6 + +echo "$as_me:$LINENO: checking size of short int" >&5 +echo $ECHO_N "checking size of short int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_short_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_short_int" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short int))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short int))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short int))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_short_int=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (short int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (short int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (short int)); } +unsigned long ulongval () { return (long) (sizeof (short int)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (short int))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (short int)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (short int)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_short_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (short int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (short int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_short_int=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_short_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_short_int" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SHORT_INT $ac_cv_sizeof_short_int +_ACEOF + + +if test "$ac_cv_sizeof_short_int" = "4"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DTYPE_32BIT=\"short int\"" +else + echo "$as_me:$LINENO: checking for int" >&5 +echo $ECHO_N "checking for int... $ECHO_C" >&6 +if test "${ac_cv_type_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((int *) 0) + return 0; +if (sizeof (int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_int=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_int=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_int" >&5 +echo "${ECHO_T}$ac_cv_type_int" >&6 + +echo "$as_me:$LINENO: checking size of int" >&5 +echo $ECHO_N "checking size of int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_int" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_int=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (int)); } +unsigned long ulongval () { return (long) (sizeof (int)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (int))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (int)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (int)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_int=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_int" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT $ac_cv_sizeof_int +_ACEOF + + + if test "$ac_cv_sizeof_int" = "4"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DTYPE_32BIT=\"int\"" + else + echo "$as_me:$LINENO: checking for long int" >&5 +echo $ECHO_N "checking for long int... $ECHO_C" >&6 +if test "${ac_cv_type_long_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long int *) 0) + return 0; +if (sizeof (long int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_int=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_int=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_int" >&5 +echo "${ECHO_T}$ac_cv_type_long_int" >&6 + +echo "$as_me:$LINENO: checking size of long int" >&5 +echo $ECHO_N "checking size of long int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long_int" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long int))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long int))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long int))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_long_int=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (long int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (long int)); } +unsigned long ulongval () { return (long) (sizeof (long int)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (long int))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (long int)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (long int)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (long int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long_int=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long_int" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG_INT $ac_cv_sizeof_long_int +_ACEOF + + + if test "$ac_cv_sizeof_long_int" = "4"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DTYPE_32BIT=\"long int\"" + fi + fi +fi + + +OPTIMIZECPU="yes" + + +# Check whether --with-optimizecpu or --without-optimizecpu was given. +if test "${with_optimizecpu+set}" = set; then + withval="$with_optimizecpu" + OPTIMIZECPU="$withval" + +fi; + +DEVELOPER_FLAGS="no" + + +# Check whether --with-developer-flags or --without-developer-flags was given. +if test "${with_developer_flags+set}" = set; then + withval="$with_developer_flags" + DEVELOPER_FLAGS="$withval" + +fi; + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + + +if test "${FreeBSD}" != ""; then + OPTIMIZECPU="" +fi + +if test "${OPTIMIZECPU}" = "yes"; then + +CPU_NAME="unknown" +CPU_TYPE="unknown" +if test -e "/proc/cpuinfo" ; then + intel[30]="-march=i386" + intel[32]="-march=i386" + intel[34]="-march=i386" + intel[40]="-march=i486" + intel[41]="-march=i486" + intel[42]="-march=i486" + intel[43]="-march=i486" + intel[44]="-march=i486" + intel[45]="-march=i486" + intel[47]="-march=i486" + intel[48]="-march=i486" + intel[51]="-march=pentium" + intel[52]="-march=pentium" + intel[54]="-march=pentium-mmx" + intel[61]="-march=pentiumpro" + intel[63]="-march=pentium2" + intel[65]="-march=pentium2" + intel[66]="-march=pentium2" + intel[67]="-march=pentium3" + intel[68]="-march=pentium3" + intel[610]="-march=pentium3" + intel[611]="-march=pentium3" + intel[150]="-march=pentium4" + intel[151]="-march=pentium4" + intel[152]="-march=pentium4" + amd[50]="-march=i586" + amd[51]="-march=i586" + amd[52]="-march=i586" + amd[53]="-march=i586" + amd[56]="-march=k6" + amd[57]="-march=k6" + amd[58]="-march=k6-2" + amd[59]="-march=k6-3" + amd[61]="-march=athlon" + amd[62]="-march=athlon" + amd[63]="-march=athlon" + amd[64]="-march=athlon" + amd[66]="-march=athlon" + amd[67]="-march=athlon" + amd[68]="-march=athlon" + amd[155]="-march=athlon64" + CPU_TYPE="known" + CPU_FAMILY=`cat /proc/cpuinfo | grep "cpu family" | head -n1` + CPU_MODEL=`cat /proc/cpuinfo | grep model[^\ ] | head -n1` + CPU_NAME=`cat /proc/cpuinfo | grep "model name" | head -n1` + CPU_FLAGS=`cat /proc/cpuinfo | grep "flags" | head -n1` + CPU_VENDOR=`cat /proc/cpuinfo | grep "vendor_id" | head -n1` + CPU_FAMILY=${CPU_FAMILY#*: } + CPU_MODEL=${CPU_MODEL#*: } + CPU_NAME=${CPU_NAME#*: } + CPU_FLAGS=${CPU_FLAGS#*: } + CPU_VENDOR=${CPU_VENDOR#*: } + if test "x${CPU_VENDOR}" = "xGenuineIntel" ; then + CPU_OPTIONS=${intel[$CPU_FAMILY$CPU_MODEL]} + fi + if test "x${CPU_VENDOR}" = "xAuthenticAMD" ; then + CPU_OPTIONS=${amd[$CPU_FAMILY$CPU_MODEL]} + fi + if test "x${CPU_OPTIONS}" = "x" ; then + CPU_TYPE="unknown" + fi + CPU_EXT="" + for i in $CPU_FLAGS ; do + case $i in + fpu) + CPU_FPU="-mfpmath=387" + ;; + mmx) + CPU_EXT="$CPU_EXT -mmmx" + ;; + sse) + CPU_FPU="-mfpmath=sse -msse" + ;; + sse2) + CPU_FPU="-mfpmath=sse -msse2" + ;; + 3dnow) + CPU_EXT="$CPU_EXT -m3dnow" + ;; + esac + done + CPU_OPTIONS="$CPU_OPTIONS $CPU_FPU $CPU_EXT" +fi +if test "x${CPU_TYPE}" = "xunknown"; then + CPU_TYPE=`( uname -p ) 2>&1` + case $CPU_TYPE in + i386) + CPU_OPTIONS="-march=i386" + ;; + i486) + CPU_OPTIONS="-march=i486" + ;; + Pentium2) + CPU_OPTIONS="-march=pentium2" + ;; + Pentiumpro) + CPU_OPTIONS="-march=pentiumpro" + ;; + Pentium*) + CPU_OPTIONS="-march=pentium" + ;; + k6) + CPU_OPTIONS="-march=k6" + ;; + k6-2) + CPU_OPTIONS="-march=k6-2" + ;; + k6-3) + CPU_OPTIONS="-march=k6-3" + ;; + *) + CPU_OPTIONS="" + CPU_TYPE="unknown" + ;; + esac + if test "x${CPU_TYPE}" = "xunknown"; then + CPU_TYPE=`( uname -m ) 2>&1` + case $CPU_TYPE in + i386) + CPU_OPTIONS="-march=i386" + ;; + i486) + CPU_OPTIONS="-march=i486" + ;; + i586) + CPU_OPTIONS="-march=i586" + ;; + i686) + CPU_OPTIONS="-march=i686" + ;; + Pentium2) + CPU_OPTIONS="-march=pentium2" + ;; + Pentiumpro) + CPU_OPTIONS="-march=pentiumpro" + ;; + k6) + CPU_OPTIONS="-march=k6" + ;; + k6-2) + CPU_OPTIONS="-march=k6-2" + ;; + k6-3) + CPU_OPTIONS="-march=k6-3" + ;; + *) + CPU_OPTIONS="" + ;; + esac + fi +fi +echo "Detected CPU: $CPU_NAME" +COMPILER=$CC +for I in "$TMPDIR" "$TEMPDIR" "/tmp" ; do + test "$I" && break +done +TMPC="$I/cpu_test-$RANDOM-$$.c" +TMPO="$I/cpu_test-$RANDOM-$$.o" +cat > $TMPC << EOF +int main(void) { return 0; } +EOF +( $COMPILER $CPU_OPTIONS -o $TMPO $TMPC ) 2>&1 +TMP="$?" +rm -f $TMPO +rm -f $TMPC + + +if test "x${TMP}" = "x1" ; then + CPU_OPTIONS="" + echo "No CPU optimizations will be added" +else + echo "CPU optimization: $CPU_OPTIONS" +fi + +else + CPU_OPTIONS="" +fi + + +echo "$as_me:$LINENO: checking for bswap instruction" >&5 +echo $ECHO_N "checking for bswap instruction... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF + + /* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +unsigned int __x=0; + register unsigned int __v; + __asm("bswap %0" : "=r" (__v) : "0" (__x)); + ; + return 0; +} + +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + TEMP_CFLAGS="${TEMP_CFLAGS} -DHAVE_BSWAP" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +if test "${DEVELOPER_FLAGS}" = "yes"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -W -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wredundant-decls -Wno-long-long" +fi + +CFLAGS="${TEMP_CFLAGS} $UNAME_DEFS $CPU_OPTIONS" + +LIBS="${TEMP_LIBS}" +LDFLAGS="${TEMP_LDFLAGS}" + +if test $prefix = "NONE";then + BIN_PATH="$ac_default_prefix" + if test $exec_prefix = "NONE"; then + BIN_PATH="$BIN_PATH/bin" + else + BIN_PATH="$BIN_PATH/$bindir" + fi +else + if test $exec_prefix = "NONE";then + BIN_PATH="$prefix/bin" + else + BIN_PATH="$prefix/$bindir" + fi +fi + + + + ac_config_files="$ac_config_files motion.init-FreeBSD.sh motion.init-Debian motion.init-RH motion.spec Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "motion.init-FreeBSD.sh" ) CONFIG_FILES="$CONFIG_FILES motion.init-FreeBSD.sh" ;; + "motion.init-Debian" ) CONFIG_FILES="$CONFIG_FILES motion.init-Debian" ;; + "motion.init-RH" ) CONFIG_FILES="$CONFIG_FILES motion.init-RH" ;; + "motion.spec" ) CONFIG_FILES="$CONFIG_FILES motion.spec" ;; + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@VERSION@,$VERSION,;t t +s,@VIDEO@,$VIDEO,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@FFMPEG_OBJ@,$FFMPEG_OBJ,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@BIN_PATH@,$BIN_PATH,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + +echo "" +echo " ************************" +echo " * Configure status *" +echo " ************************" +echo "" + + +if test "${Darwin}" != ""; then + echo "OS : Darwin" +elif test "${FreeBSD}" != ""; then + echo "OS : *BSD" +else + echo "OS : Linux" +fi + +if test "${PTHREAD_SUPPORT}" = "yes"; then + echo "pthread Support: Yes" +else + echo "pthread Support: No" + echo "**********************************************" + echo "** Fatal Error YOU MUST HAVE pthread Support *" + echo "**********************************************" +fi + +if test "${JPEG_SUPPORT_MMX}" = "yes"; then + echo "jpeg-mmx Support: Yes" +elif test "${JPEG_SUPPORT}" = "yes"; then + echo "jpeg Support: Yes" +else + echo "jpeg Support: No" + echo "**********************************************" + echo "** Fatal Error YOU MUST HAVE jpeg Support ***" + echo "**********************************************" +fi + + +if test "${FreeBSD}" != ""; then + if test "${BKTR}" = "yes"; then + echo "BKTR included: Yes" + else + echo "BKTR included: No" + fi + + +else + if test "${V4L}" = "yes"; then + echo "V4L included: Yes" + else + echo "V4L included: No" + fi +fi + +if test "${FFMPEG_OK}" = "found"; then + echo "FFmpeg Support: Yes" +else + echo "FFmpeg Support: No" +fi + +if test "${MYSQL_SUPPORT}" = "yes"; then + echo "MYSQL Support: Yes" +else + echo "MYSQL Support: No" +fi + +if test "${PostgreSQL_SUPPORT}" = "yes"; then + echo "PostgreSQL Support: Yes" +else + echo "PostgreSQL Support: No" +fi +echo "" + +echo "CFLAGS: $CFLAGS" +echo "LIBS: $LIBS" +echo "LDFLAGS: $LDFLAGS" + +echo +echo "Install prefix: $prefix" +echo + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..20cf86d --- /dev/null +++ b/configure.in @@ -0,0 +1,882 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT(motion.c) + +VERSION=3.2.5 +AC_SUBST(VERSION) + +THREAD_CFLAGS="" +THREAD_CHECK="pthread.h" + +Darwin="" +FreeBSD="" + + +AC_MSG_CHECKING(for Darwin) +Darwin=`uname -a | grep "Darwin"` + +if test "${Darwin}" = ""; then + AC_MSG_RESULT(no) + AC_MSG_CHECKING(for *BSD) + + FreeBSD=`uname -a | grep "BSD"` + if test "${FreeBSD}" = ""; then + AC_MSG_RESULT(no) + VIDEO="video.o" + else + AC_MSG_RESULT(yes) + THREAD_CHECK="/usr/local/include/pthread/linuxthreads/pthread.h" + THREAD_LIB_CHECK="/usr/local/lib/liblthread.so" + TEMP_CFLAGS="${CFLAGS} -D__freebsd__ -I/usr/local/include" + TEMP_LDFLAGS="${LDFLAGS} -L/usr/local/lib" + TEMP_LIBS="-L/usr/local/lib" + VIDEO="video_freebsd.o" + fi +else + TEMP_CFLAGS="${CFLAGS} -I/sw/include -D__freebsd__" + TEMP_LDFLAGS="${LDFLAGS} -L/sw/lib" + TEMP_LIBS="-L/sw/lib" + VIDEO="video_freebsd.o" + FINK_LIB="-L/sw/lib" + Darwin="yes" + AC_MSG_RESULT($Darwin) +fi + + +AC_SUBST(VIDEO) + +dnl Checks for programs. +AC_PROG_CC + +TEMP_LIBS="-lm ${TEMP_LIBS}" +TEMP_CFLAGS="${TEMP_CFLAGS} ${CFLAGS}" +TEMP_LDFLAGS="${TEMP_LDFLAGS} ${LDFLAGS}" + + +if test "${FreeBSD}" != ""; then + +dnl +dnl Check to Exclude BKTR +dnl +BKTR="yes" +AC_ARG_WITH(bktr, +[ --without-bktr Exclude to use bktr subsytem , that usually usefull + for devices as network cameras. + ] + , +BKTR="$withval" +) + + + if test "${BKTR}" = "no"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DWITHOUT_V4L" + fi + +else + +dnl +dnl Check to Exclude V4L +dnl +V4L="yes" +AC_ARG_WITH(v4l, +[ --without-v4l Exclude using v4l (video4linux) subsystem. + Makes Motion so it only supports network cameras. + ], +V4L="$withval" +) + +fi + + +if test "${Darwin}" = "yes"; then + V4L="no" +fi + +if test "${V4L}" = "no"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DWITHOUT_V4L" +fi + + +if test "${FreeBSD}" != ""; then + +AC_MSG_CHECKING(for linuxthreads) + +dnl +dnl Check for thread header +dnl + if test -f "${THREAD_CHECK}"; then + HEADERS_THREAD_CFLAGS="-I/usr/local/include/pthread/linuxthreads" + THREADS="yes" + else + THREADS="no" + fi + +dnl +dnl Check for thread lib +dnl + if test -f "${THREAD_LIB_CHECK}" ; then + THREADS="yes" + LIB_THREAD="-llthread -llgcc_r" + else + THREADS="no" + fi + +dnl Checks for Library linuxthreads for FreeBSD +dnl +dnl linuxthreads on freeBSD, ports collection +dnl /usr/local/include/pthreads/linuxthreads/pthread.h +dnl #include +dnl /usr/local/lib/libpthread.so +dnl + + if test "${THREADS}" = "yes"; then + TEMP_CFLAGS="${HEADERS_THREAD_CFLAGS} $TEMP_CFLAGS -DWITH_LINUXTREADS" + TEMP_LIBS="$TEMP_LIBS ${LIB_THREAD}" + THREAD_CFLAGS="-D_THREAD_SAFE" + PTHREAD_SUPPORT="yes" + AC_MSG_RESULT($THREADS) + else + PTHREAD_SUPPORT="no" + AC_MSG_RESULT($THREADS) + echo + echo You do not have linuxthread installed + echo + fi + +else + +dnl Checks for Library pthread ( no cross platform ) +AC_CHECK_LIB(pthread,pthread_create,[ + TEMP_LIBS="$TEMP_LIBS -lpthread" + PTHREAD_SUPPORT="yes" + ],[ + echo + echo You do not have pthread installed + echo + ] +) + +fi + + + +dnl +dnl Check for the special mmx accelerated jpeg library +dnl +JPEG_MMX="no" +JPEG_MMX_OK="not_found" +AC_ARG_WITH(jpeg-mmx, +[ --with-jpeg-mmx[=DIR] Specify the prefix for the install path for + jpeg-mmx for optimized jpeg handling (optional). + If this is not specified motion will try to find + the library /usr/lib/libjpeg-mmx.a /usr/local/lib/libjpeg-mmx.a. + ], +JPEG_MMX="$withval" +) + +dnl +dnl --without-jpeg-mmx or with-jpeg-mmx=no +dnl + +if test "${JPEG_MMX}" = "no"; then + AC_MSG_CHECKING(for libjpeg-mmx) + AC_MSG_RESULT(skipping) +elif test "${JPEG_MMX}" = "yes"; then + dnl AUTODETECT STATIC LIB + AC_MSG_CHECKING(for libjpeg-mmx autodetecting) + + if test -f /usr/lib/libjpeg-mmx.a ; then + AC_MSG_RESULT(found) + JPEG_MMX_OK="found" + JPEG_MMX="/usr/lib" + elif test -f /usr/local/lib/libjpeg-mmx.a ; then + AC_MSG_RESULT(found) + JPEG_MMX_OK="found" + JPEG_MMX="/usr/local/lib" + else + AC_MSG_RESULT(not found) + fi +else + AC_MSG_CHECKING(for libjpeg-mmx in -> [${JPEG_MMX}] <-) + if test -f ${JPEG_MMX}/libjpeg-mmx.a ; then + AC_MSG_RESULT(found) + JPEG_MMX_OK="found" + else + AC_MSG_RESULT(not found) + fi +fi + +if test "${JPEG_MMX_OK}" = "found"; then + OLD_CFLAGS="$CFLAGS" + OLD_LIBS="$LIBS" + CFLAGS="$CFLAGS -I${JPEG_MMX}" + LIBS="$LIBS -L${JPEG_MMX}" + AC_CHECK_LIB(jpeg-mmx, jpeg_start_compress, + [ TEMP_LIBS="$TEMP_LIBS -ljpeg-mmx" + TEMP_CFLAGS="${TEMP_CFLAGS} -I${JPEG_MMX}" + JPEG_SUPPORT="yes"],,) + LIBS="$OLD_LIBS" + CFLAGS="$OLD_CFLAGS" + JPEG_SUPPORT_MMX="yes" +fi + +dnl +dnl Look for _a_ jpeg lib that will work. +dnl +if test x$JPEG_SUPPORT != xyes ; then + dnl Checks for libraries + LDFLAGS=$TEMP_LDFLAGS + + AC_CHECK_LIB(jpeg, jpeg_set_defaults, [ + TEMP_LIBS="$TEMP_LIBS -ljpeg" + JPEG_SUPPORT="yes" + ], [ + echo + echo You do not have libjpeg installed + echo + ] + ) +fi + +dnl +dnl Check for libavcodec and libavformat from ffmpeg +dnl +FFMPEG="yes" +FFMPEG_OK="no_found" +FFMPEG_OBJ="" +AC_ARG_WITH(ffmpeg, +[ --with-ffmpeg[=DIR] Specify the prefix for the install path for + libavcodec/libavformat (part of ffmpeg) be able to + encode mpeg movies realtime. + If this is not specified motion will try to find + the libraries in /usr and /usr/local. + ], +FFMPEG="$withval" +) +dnl +dnl --without-ffmpeg or with-ffmpeg=no +dnl +if test "${FFMPEG}" = "no"; then + AC_MSG_CHECKING(for ffmpeg) + AC_MSG_RESULT(skipping) +dnl +dnl with-ffmpeg= or nothing +dnl +else if test "${FFMPEG}" = "yes"; then + dnl AUTODETECT STATIC/SHARED LIB + AC_MSG_CHECKING(for ffmpeg autodetecting) + +# weird hack to fix debian problem TO BE REMOVED + + if test -f /usr/bin/ffmpeg-config; then + FFMPEG_LIBS_DEB="`ffmpeg-config --libs avformat`" + FFMPEG_CFLAGS_DEB="`ffmpeg-config --cflags`" + FFMPEG_OK="found" + AC_MSG_RESULT(found for debian) + elif test -f /usr/lib64/libavcodec.a -o -f /usr/lib64/libavcodec.so && test -f /usr/lib64/libavformat.a -o -f /usr/lib64/libavformat.so ; then + AC_MSG_RESULT(found) + FFMPEG_OK="found" + FFMPEG="/usr/lib64" + elif test -f /usr/lib/libavcodec.a -o -f /usr/lib/libavcodec.so && test -f /usr/lib/libavformat.a -o -f /usr/lib/libavformat.so ; then + AC_MSG_RESULT(found) + FFMPEG_OK="found" + FFMPEG="/usr/lib" + elif test -f /usr/local/lib/libavcodec.a -o -f /usr/local/lib/libavcodec.so && test -f /usr/local/lib/libavformat.a -o -f /usr/local/lib/libavformat.so ; then + AC_MSG_RESULT(found) + FFMPEG_OK="found" + FFMPEG="/usr/local/lib" + else + AC_MSG_RESULT(not found) + echo "" + echo "**********************************************" + echo "* libavcodec.a or libavcodec.so or *" + echo "* libavformat.a or libavformat.so not found: *" + echo "* ALL FFMPEG FEATURES DISABLED *" + echo "* *" + echo "* Please read the Motion Guide for help: *" + echo "* http://motion.sourceforge.net *" + echo "**********************************************" + echo "" + fi +else + AC_MSG_CHECKING(for ffmpeg in -> [${FFMPEG}] <-) + if test -f ${FFMPEG}/lib/libavcodec.a -o -f ${FFMPEG}/lib/libavcodec.so && test -f ${FFMPEG}/lib/libavformat.a -o -f ${FFMPEG}/lib/libavformat.so ; then + AC_MSG_RESULT(found) + FFMPEG_OK="found" + FFMPEG="${FFMPEG}/lib" + elif test -f ${FFMPEG}/libavcodec.a -o -f ${FFMPEG}/libavcodec.so && test -f ${FFMPEG}/libavformat.a -o -f ${FFMPEG}/libavformat.so ; then + AC_MSG_RESULT(found) + FFMPEG_OK="found" + else + AC_MSG_RESULT(not found) + if test "${FFMPEG}" != "no"; then + echo "" + echo "**********************************************" + echo "* libavcodec.a or libavcodec.so or *" + echo "* libavformat.a or libavformat.so not found: *" + echo "* ALL FFMPEG FEATURES DISABLED *" + echo "* *" + echo "* Please read the Motion Guide for help: *" + echo "* http://motion.sourceforge.net *" + echo "**********************************************" + echo "" + fi + fi +fi + +# +# Now check for ffmpeg headers ( avformat.h ) if ffmpeg libs were found +# + +if test "${FFMPEG_OK}" = "found"; then + AC_MSG_CHECKING(for ffmpeg headers) + + if test "${FFMPEG_CFLAGS_DEB}" != "" ; then + FFMPEG_CFLAGS="${FFMPEG_CFLAGS_DEB}" + AC_MSG_RESULT(found for debian) + elif test -f ${FFMPEG}/include/avformat.h; then + AC_MSG_RESULT(found) + FFMPEG_CFLAGS="-I${FFMPEG}/include" + elif test -f ${FFMPEG}/avformat.h; then + AC_MSG_RESULT(found) + FFMPEG_CFLAGS="-I${FFMPEG}" + elif test -f `AS_DIRNAME([${FFMPEG}])`/include/avformat.h; then + AC_MSG_RESULT(found) + FFMPEG_CFLAGS="-I`AS_DIRNAME([${FFMPEG}])`/include" + elif test -f `AS_DIRNAME([${FFMPEG}])`/include/ffmpeg/avformat.h; then + AC_MSG_RESULT(found) + FFMPEG_CFLAGS="-I`AS_DIRNAME([${FFMPEG}])`/include/ffmpeg" + else + AC_MSG_RESULT(not found) + FFMPEG_OK="no_found" + echo "**********************************************" + echo "* avformat.h not found: *" + echo "* ALL FFMPEG FEATURES DISABLED *" + echo "* *" + echo "* Please read the Motion Guide for help: *" + echo "* http://motion.sourceforge.net *" + echo "**********************************************" + echo "" + fi + +# +# If ffmpeg libs and headers have been found +# + + if test "${FFMPEG_OK}" = "found"; then + if test "${FFMPEG_LIBS_DEB}" != ""; then + TEMP_LIBS="$TEMP_LIBS ${FFMPEG_LIBS_DEB}" +# TEMP_LDFLAGS="${TEMP_LDFLAGS} ${FFMPEG_LIBS_DEB}" + else + TEMP_LIBS="$TEMP_LIBS -L${FFMPEG} -lavformat -lavcodec -lm -lz" + TEMP_LDFLAGS="${TEMP_LDFLAGS} -L${FFMPEG}" + fi + + TEMP_CFLAGS="${TEMP_CFLAGS} -DHAVE_FFMPEG ${FFMPEG_CFLAGS}" + + FFMPEG_OBJ="ffmpeg.o" + AC_SUBST(FFMPEG_OBJ) + fi +fi +fi + + +MYSQL="yes" + +AC_MSG_CHECKING(for MySQL) +AC_ARG_WITH(mysql, +[ --with-mysql[=DIR] Normally, configure will scan all possible default + installation paths for mysql. When its fail, use + this command to tell configure where mysql + installation root directory is. + ], +MYSQL="$withval" +dnl if not given argument, assume standard +) + +if test "${MYSQL}" = "yes"; then + # Autodetect + for w in /usr/include /usr/local/include /usr/mysql /usr/local/mysql /usr/local/mysql/include /opt /opt/mysql; do + # check for plain setups + if test -f $w/mysql.h; then + MYSQL_INCDIR=$w + break + fi + # check for "/usr/include/" type setups + if test -f $w/mysql/mysql.h; then + MYSQL_INCDIR=$w/mysql + break + fi + # check for "/usr//include" type setups + if test -f $w/mysql/include/mysql.h; then + MYSQL_INCDIR=$w/mysql/include + break + fi + done + + for w in /usr/lib /usr/local/lib /usr/mysql /usr/local/mysql /usr/local/mysql/lib /opt /opt/mysql; do + # check for plain setups + if test -f $w/libmysqlclient.a -o -f $w/libmysqlclient.so; then + MYSQL_LIBDIR=$w + break + fi + # check for "/usr/lib/" type setups + if test -f $w/mysql/libmysqlclient.a -o -f $w/mysql/libmysqlclient.so; then + MYSQL_LIBDIR=$w/mysql + break + fi + # check for "/usr//lib" type setups + if test -f $w/mysql/lib/libmysqlclient.a -o -f $w/mysql/lib/libmysqlclient.so; then + MYSQL_LIBDIR=$w/mysql/lib + break + fi + done +else + # Manual detection for /include/ + # and /include. + if test -f $withval/include/mysql/mysql.h; then + MYSQL_INCDIR=$withval/include/mysql + elif test -f $withval/include/mysql.h; then + MYSQL_INCDIR=$withval/include + fi + + # Manual detection for /lib/ + # and /lib. + + if test -f $withval/lib/mysql/libmysqlclient.a -o -f $withval/lib/mysql/libmysqlclient.so; then + MYSQL_LIBDIR=$withval/lib/mysql + elif test -f $withval/lib/libmysqlclient.a -o -f $withval/lib/libmysqlclient.so; then + MYSQL_LIBDIR=$withval/lib + fi +fi + +## Did we find anything? +if test "${MYSQL}" = "no"; then + AC_MSG_RESULT(skipped) +else + if test -z "$MYSQL_LIBDIR" ; then + AC_MSG_RESULT(no) + echo Invalid MySQL directory - unable to find libmysqlclient.a or libmysqlclient.so. + elif test -z "$MYSQL_INCDIR" ; then + AC_MSG_RESULT(no) + echo Invalid MySQL directory - unable to find mysql.h. + else + TEMP_LIBS="$TEMP_LIBS -L$MYSQL_LIBDIR -lmysqlclient" + #Add -lz for some mysql installs.... + TEMP_LIBS="$TEMP_LIBS -lz" + TEMP_CFLAGS="$TEMP_CFLAGS -DHAVE_MYSQL -I $MYSQL_INCDIR" + AC_MSG_RESULT(yes) + MYSQL_SUPPORT="yes" + fi +fi + +dnl Start Check for Postgresql +AC_DEFUN(PGSQL_INC_CHK,[if test -r $i$1/libpq-fe.h; then PGSQL_DIR=$i; PGSQL_INCDIR=$i$1]) +PGSQL="yes" + +AC_MSG_CHECKING(for PostgreSQL) +AC_ARG_WITH(pgsql, +[ --with-pgsql[=DIR] Include PostgreSQL support. DIR is the PostgreSQL + base install directory. If not specified configure will + search in /usr, /usr/local and /usr/local/pgsql. + ], +PGSQL="$withval" +dnl if not given argument, assume standard +) +if test "${PGSQL}" = "no"; then + AC_MSG_RESULT(skipped) +fi + +if test "${PGSQL}" = "yes"; then + for i in /usr /usr/local /usr/local/pgsql $PHP_PGSQL; do + PGSQL_INC_CHK(/include) + el[]PGSQL_INC_CHK(/include/pgsql) + el[]PGSQL_INC_CHK(/include/postgresql) + fi + done + + if test -z "$PGSQL_DIR"; then + AC_MSG_RESULT(Cannot find libpq-fe.h. Please specify the installation path of PostgreSQL) + else + PGSQL_INCLUDE="-I$PGSQL_INCDIR" + PGSQL_LIBDIR=$PGSQL_DIR/lib + AC_MSG_RESULT(yes) + test -d $PGSQL_DIR/lib/pgsql && PGSQL_LIBDIR=$PGSQL_DIR/lib/pgsql + LDFLAGS="$TEMP_LDFLAGS -L$PGSQL_LIBDIR" + AC_CHECK_LIB(pq, PQcmdTuples,AC_DEFINE(HAVE_PQCMDTUPLES,1,[ ])) + AC_CHECK_LIB(pq, PQoidValue,AC_DEFINE(HAVE_PQOIDVALUE,1,[ ])) + AC_CHECK_LIB(pq, PQclientEncoding,AC_DEFINE(HAVE_PQCLIENTENCODING,1,[ ])) + AC_CHECK_LIB(pq, pg_encoding_to_char,AC_DEFINE(HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT,1,[ ])) + LDFLAGS="" + AC_DEFINE(HAVE_PGSQL,1,[ ]) + TEMP_LIBS="$TEMP_LIBS -L$PGSQL_LIBDIR -lpq" + TEMP_CFLAGS="$TEMP_CFLAGS -DHAVE_PGSQL $PGSQL_INCLUDE" + PostgreSQL_SUPPORT="yes" + fi +fi +dnl End Postgresql + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(stdio.h stdlib.h unistd.h fcntl.h time.h signal.h sys/ioctl.h sys/mman.h linux/videodev.h) + +dnl Check sizes of integer types +AC_CHECK_SIZEOF(short int) +if test "$ac_cv_sizeof_short_int" = "4"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DTYPE_32BIT=\"short int\"" +else + AC_CHECK_SIZEOF(int) + if test "$ac_cv_sizeof_int" = "4"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DTYPE_32BIT=\"int\"" + else + AC_CHECK_SIZEOF(long int) + if test "$ac_cv_sizeof_long_int" = "4"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -DTYPE_32BIT=\"long int\"" + fi + fi +fi + + +OPTIMIZECPU="yes" + +AC_ARG_WITH(optimizecpu, +[ --without-optimizecpu Exclude autodetecting platform and cpu type. + This will disable the compilation of gcc + optimizing code by platform and cpu. + ], +OPTIMIZECPU="$withval" +) + +DEVELOPER_FLAGS="no" + +AC_ARG_WITH(developer-flags, +[ --with-developer-flags Causes practically all of the possible gcc + warning flags to be set. This may produce + a large amount of warnings.], +DEVELOPER_FLAGS="$withval" +) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST + +if test "${FreeBSD}" != ""; then + OPTIMIZECPU="" +fi + +if test "${OPTIMIZECPU}" = "yes"; then + +dnl Try to autodetect cpu type +CPU_NAME="unknown" +CPU_TYPE="unknown" +if test -e "/proc/cpuinfo" ; then + intel[[30]]="-march=i386" + intel[[32]]="-march=i386" + intel[[34]]="-march=i386" + intel[[40]]="-march=i486" + intel[[41]]="-march=i486" + intel[[42]]="-march=i486" + intel[[43]]="-march=i486" + intel[[44]]="-march=i486" + intel[[45]]="-march=i486" + intel[[47]]="-march=i486" + intel[[48]]="-march=i486" + intel[[51]]="-march=pentium" + intel[[52]]="-march=pentium" + intel[[54]]="-march=pentium-mmx" + intel[[61]]="-march=pentiumpro" + intel[[63]]="-march=pentium2" + intel[[65]]="-march=pentium2" + intel[[66]]="-march=pentium2" + intel[[67]]="-march=pentium3" + intel[[68]]="-march=pentium3" + intel[[610]]="-march=pentium3" + intel[[611]]="-march=pentium3" + intel[[150]]="-march=pentium4" + intel[[151]]="-march=pentium4" + intel[[152]]="-march=pentium4" + amd[[50]]="-march=i586" + amd[[51]]="-march=i586" + amd[[52]]="-march=i586" + amd[[53]]="-march=i586" + amd[[56]]="-march=k6" + amd[[57]]="-march=k6" + amd[[58]]="-march=k6-2" + amd[[59]]="-march=k6-3" + amd[[61]]="-march=athlon" + amd[[62]]="-march=athlon" + amd[[63]]="-march=athlon" + amd[[64]]="-march=athlon" + amd[[66]]="-march=athlon" + amd[[67]]="-march=athlon" + amd[[68]]="-march=athlon" + amd[[155]]="-march=athlon64" + CPU_TYPE="known" + CPU_FAMILY=`cat /proc/cpuinfo | grep "cpu family" | head -n1` + CPU_MODEL=`cat /proc/cpuinfo | grep model[[^\ ]] | head -n1` + CPU_NAME=`cat /proc/cpuinfo | grep "model name" | head -n1` + CPU_FLAGS=`cat /proc/cpuinfo | grep "flags" | head -n1` + CPU_VENDOR=`cat /proc/cpuinfo | grep "vendor_id" | head -n1` + CPU_FAMILY=${CPU_FAMILY#*: } + CPU_MODEL=${CPU_MODEL#*: } + CPU_NAME=${CPU_NAME#*: } + CPU_FLAGS=${CPU_FLAGS#*: } + CPU_VENDOR=${CPU_VENDOR#*: } + if test "x${CPU_VENDOR}" = "xGenuineIntel" ; then + CPU_OPTIONS=${intel[[$CPU_FAMILY$CPU_MODEL]]} + fi + if test "x${CPU_VENDOR}" = "xAuthenticAMD" ; then + CPU_OPTIONS=${amd[[$CPU_FAMILY$CPU_MODEL]]} + fi + if test "x${CPU_OPTIONS}" = "x" ; then + CPU_TYPE="unknown" + fi + CPU_EXT="" + for i in $CPU_FLAGS ; do + case $i in + fpu) + CPU_FPU="-mfpmath=387" + ;; + mmx) + CPU_EXT="$CPU_EXT -mmmx" + ;; + sse) + CPU_FPU="-mfpmath=sse -msse" + ;; + sse2) + CPU_FPU="-mfpmath=sse -msse2" + ;; + 3dnow) + CPU_EXT="$CPU_EXT -m3dnow" + ;; + esac + done + CPU_OPTIONS="$CPU_OPTIONS $CPU_FPU $CPU_EXT" +fi +if test "x${CPU_TYPE}" = "xunknown"; then + CPU_TYPE=`( uname -p ) 2>&1` + case $CPU_TYPE in + i386) + CPU_OPTIONS="-march=i386" + ;; + i486) + CPU_OPTIONS="-march=i486" + ;; + Pentium2) + CPU_OPTIONS="-march=pentium2" + ;; + Pentiumpro) + CPU_OPTIONS="-march=pentiumpro" + ;; + Pentium*) + CPU_OPTIONS="-march=pentium" + ;; + k6) + CPU_OPTIONS="-march=k6" + ;; + k6-2) + CPU_OPTIONS="-march=k6-2" + ;; + k6-3) + CPU_OPTIONS="-march=k6-3" + ;; + *) + CPU_OPTIONS="" + CPU_TYPE="unknown" + ;; + esac + if test "x${CPU_TYPE}" = "xunknown"; then + CPU_TYPE=`( uname -m ) 2>&1` + case $CPU_TYPE in + i386) + CPU_OPTIONS="-march=i386" + ;; + i486) + CPU_OPTIONS="-march=i486" + ;; + i586) + CPU_OPTIONS="-march=i586" + ;; + i686) + CPU_OPTIONS="-march=i686" + ;; + Pentium2) + CPU_OPTIONS="-march=pentium2" + ;; + Pentiumpro) + CPU_OPTIONS="-march=pentiumpro" + ;; + k6) + CPU_OPTIONS="-march=k6" + ;; + k6-2) + CPU_OPTIONS="-march=k6-2" + ;; + k6-3) + CPU_OPTIONS="-march=k6-3" + ;; + *) + CPU_OPTIONS="" + ;; + esac + fi +fi +echo "Detected CPU: $CPU_NAME" +dnl Now we check if the compiler supports the detected cpu +COMPILER=$CC +for I in "$TMPDIR" "$TEMPDIR" "/tmp" ; do + test "$I" && break +done +TMPC="$I/cpu_test-$RANDOM-$$.c" +TMPO="$I/cpu_test-$RANDOM-$$.o" +cat > $TMPC << EOF +int main(void) { return 0; } +EOF +( $COMPILER $CPU_OPTIONS -o $TMPO $TMPC ) 2>&1 +TMP="$?" +rm -f $TMPO +rm -f $TMPC + + +if test "x${TMP}" = "x1" ; then + CPU_OPTIONS="" + echo "No CPU optimizations will be added" +else + echo "CPU optimization: $CPU_OPTIONS" +fi + +else + CPU_OPTIONS="" +fi + + +AC_MSG_CHECKING(for bswap instruction) +AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ ], + [unsigned int __x=0; + register unsigned int __v; + __asm("bswap %0" : "=r" (__v) : "0" (__x));]) + ], + [ + TEMP_CFLAGS="${TEMP_CFLAGS} -DHAVE_BSWAP" + AC_MSG_RESULT(yes) + ], + [ + AC_MSG_RESULT(no) + ]) + + +if test "${DEVELOPER_FLAGS}" = "yes"; then + TEMP_CFLAGS="${TEMP_CFLAGS} -W -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wredundant-decls -Wno-long-long" +fi + +CFLAGS="${TEMP_CFLAGS} $UNAME_DEFS $CPU_OPTIONS" + +LIBS="${TEMP_LIBS}" +LDFLAGS="${TEMP_LDFLAGS}" + +dnl +dnl Add the right exec path for rc scripts +dnl +if test $prefix = "NONE";then + BIN_PATH="$ac_default_prefix" + if test $exec_prefix = "NONE"; then + BIN_PATH="$BIN_PATH/bin" + else + BIN_PATH="$BIN_PATH/$bindir" + fi +else + if test $exec_prefix = "NONE";then + BIN_PATH="$prefix/bin" + else + BIN_PATH="$prefix/$bindir" + fi +fi + +AC_SUBST(BIN_PATH) + +AC_OUTPUT( +motion.init-FreeBSD.sh +motion.init-Debian +motion.init-RH +motion.spec +Makefile +) + +echo "" +echo " ************************" +echo " * Configure status *" +echo " ************************" +echo "" + + +if test "${Darwin}" != ""; then + echo "OS : Darwin" +elif test "${FreeBSD}" != ""; then + echo "OS : *BSD" +else + echo "OS : Linux" +fi + +if test "${PTHREAD_SUPPORT}" = "yes"; then + echo "pthread Support: Yes" +else + echo "pthread Support: No" + echo "**********************************************" + echo "** Fatal Error YOU MUST HAVE pthread Support *" + echo "**********************************************" +fi + +if test "${JPEG_SUPPORT_MMX}" = "yes"; then + echo "jpeg-mmx Support: Yes" +elif test "${JPEG_SUPPORT}" = "yes"; then + echo "jpeg Support: Yes" +else + echo "jpeg Support: No" + echo "**********************************************" + echo "** Fatal Error YOU MUST HAVE jpeg Support ***" + echo "**********************************************" +fi + + +if test "${FreeBSD}" != ""; then + if test "${BKTR}" = "yes"; then + echo "BKTR included: Yes" + else + echo "BKTR included: No" + fi + + +else + if test "${V4L}" = "yes"; then + echo "V4L included: Yes" + else + echo "V4L included: No" + fi +fi + +if test "${FFMPEG_OK}" = "found"; then + echo "FFmpeg Support: Yes" +else + echo "FFmpeg Support: No" +fi + +if test "${MYSQL_SUPPORT}" = "yes"; then + echo "MYSQL Support: Yes" +else + echo "MYSQL Support: No" +fi + +if test "${PostgreSQL_SUPPORT}" = "yes"; then + echo "PostgreSQL Support: Yes" +else + echo "PostgreSQL Support: No" +fi +echo "" + +echo "CFLAGS: $CFLAGS" +echo "LIBS: $LIBS" +echo "LDFLAGS: $LDFLAGS" + +echo +echo "Install prefix: $prefix" +echo + diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..2ab79c1 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,22 @@ + Motion contains a skel to build a deb package easily . + + You need to have installed some packages first : + + - dpkg-dev + - fakeroot + + To create the deb package ( from motion-3.2.4 ) : + + tar xfvz motion-3.2.4.tar.gz + cd motion-3.2.4/ + dpkg-buildpackage -tc -b -rfakeroot + + now you'll have a motion_3.2.4-1_i386.deb package in : + + ../motion-3.2.4 , so you can install typing : + + dpkg -i ../motion_3.2.4-1_i386.deb + + + Angel Carpintero + ack@telefonica.net diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..303b5fb --- /dev/null +++ b/debian/changelog @@ -0,0 +1,21 @@ +motion (3.2.4-1) unstable; urgency=low + + * New upstream package + + -- Angel Carpintero Sat, 17 Dec 2005 08:50:18 +0100 + +motion (3.2.3-1) unstable; urgency=low + + * New upstream package. + + -- Angel Carpintero Fri, 19 Aug 2005 19:15:18 +0100 + +motion (3.2.1-1) unstable; urgency=low + + * New upstream package (Just an example). + + -- Angel Carpintero Sun, 29 May 2005 20:57:18 +0100 + +Local variables: +mode: debian-changelog +End: diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..0ab6645 --- /dev/null +++ b/debian/control @@ -0,0 +1,29 @@ +Source: motion +Section: graphics +Priority: optional +Build-Depends: libjpeg62-dev, sharutils, nasm, zlib1g-dev, libmysqlclient10-dev, postgresql-dev, libavcodec-dev, libavformat-dev +Maintainer: Angel Carpintero +Standards-Version: 3.6.1.0 + +Package: motion +Architecture: any +Depends: ${shlibs:Depends} +Recommends: ffmpeg +Suggests: mysql-client, postgresql-client +Description: V4L capture program supporting motion detection + Motion is a program that monitors the video signal from + one or more cameras and is able to detect if a significant + part of the picture has changed. Or in other words, it can + detect motion. + . + Motion is a command line based tool. It has no graphical + user interface. Everything is setup either via the + command line or via configuration files. + . + The output from motion can be: + - jpg files + - ppm format files + - mpeg video sequences + . + Also, motion has its own minimalistic web server. Thus, + you can access the webcam output from motion via a browser. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..cae6a18 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,15 @@ +This package was originally debianized by Paul Hedderly (DH/DSS) on +Wed, 20 Sep 2000 12:39:09 +0100. + +Package is currently maintained by Frederik Dannemare since +Mon, 28 Jun 2004 17:28:24 +0200. + +Upstream source: http://motion.sourceforge.net/ + +Upstream Author: Kenneth Lavrsen + +Copyright: Copyright 2000-2004 by Jeroen Vreeken/Folkert Van Heusden/Kenneth Lavrsen + +This software is released under the GNU GENERAL PUBLIC LICENSE Version 2. +See /usr/share/common-licenses/GPL-2 for details. + diff --git a/debian/motion.1 b/debian/motion.1 new file mode 100644 index 0000000..8793d0f --- /dev/null +++ b/debian/motion.1 @@ -0,0 +1,793 @@ +.TH MOTION 1 2005-12-13 "Motion" "Motion Options and Config Files" +.SH NAME +motion \- Detect motion using a video4linux device +.SH SYNOPSIS +.B motion +[ -hns ] [ -c config file path] +.SH DESCRIPTION +.I Motion +uses a video4linux device to detect motion. If motion is detected both normal +and motion pictures will be taken. Motion can also take actions to notify you +if needed. Creation of automated snapshots is also possible. +.SH OPTIONS +.TP +.B \-c +Full path and filename of config file. E.g. /home/kurt/motion.conf. Default is /usr/local/etc unless specified differently when building Motion. Many RPMs and debian packages will most likely use /etc or /etc/motion as default. +.TP +.B \-h +Show help screen. +.TP +.B \-n +Run in non-daemon mode. +.TP +.B \-s +Run in setup mode. Also forces non-daemon mode +.TP +.B \-d +Run in debug mode. +.TP +.SH "CONFIG FILE OPTIONS" +These are the options that can be used in the config file. +.I They are overridden by the commandline! +All number values are integer numbers (no decimals allowed). +Boolean options can be on or off (values "1", "yes" and "on" all means true and any other value means false). +.TP +.B auto_brightness boolean +Values: on, off / Default: off +.br +Let motion regulate the brightness of a video device. Only recommended for cameras without auto brightness +.TP +.B brightness integer +Values: 0 - 255 / Default: 0 (disabled) +.br +The brightness level for the video device. +.TP +.B contrast boolean +Values: 0 - 255 / Default: 0 (disabled) +.br +The contrast level for the video device. +.TP +.B control_authentication string +Values: Max 4096 characters / Default: Not defined +.br +To protect HTTP Control by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. This option must be placed in motion.conf and not in a thread config file. +.TP +.B control_html_output boolean +Values: on, off / Default: on +.br +Enable HTML in the answer sent back to a browser connecting to the control_port. This option must be placed in motion.conf and not in a thread config file. +.TP +.B control_localhost boolean +Values: on, off / Default: on +.br +Limits the http (html) control to the localhost. This option must be placed in motion.conf and not in a thread config file. +.TP +.B control_port integer +Values: 0 - 65535 / Default: 0 (disabled) +.br +Sets the port number for the http (html using browser) based remote control. This option must be placed in motion.conf and not in a thread config file. +.TP +.B daemon boolean +Values: on, off / Default: off +.br +Start in daemon (background) mode and release terminal. This option must be placed in motion.conf and not in a thread config file. +.TP +.B despeckle string +Values: EedDl / Default: Not defined +.br +Despeckle motion image using combinations of (E/e)rode or (D/d)ilate. And ending with optional (l)abeling. +.TP +.B ffmpeg_bps integer +Values: 0 - 9999999 / Default: 400000 +.br +Bitrate of mpegs produced by ffmpeg. Bitrate is bits per second. Default: 400000 (400kbps). Higher value mans better quality and larger files. Option requires that ffmpeg libraries are installed. +.TP +.B ffmpeg_cap_motion boolean +Values: on, off / Default: off +.br +Use ffmpeg libraries to encode motion type mpeg movies where you only see the pixels that changes. +.TP +.B ffmpeg_cap_new boolean +Values: on, off / Default: off +.br +Use ffmpeg libraries to encode mpeg movies in realtime. +.TP +.B ffmpeg_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d%H%M%S +.br +File path for motion triggered ffmpeg movies (mpeg) relative to target_dir. +.TP +.B ffmpeg_timelapse boolean +Values: 0 - 2147483647 / Default: 0 (disabled) +.br +Create a timelapse movie saving a picture frame at the interval in seconds set by this parameter. Set it to 0 if not used. +.TP +.B ffmpeg_timelapse_mode discrete strings +Values: hourly, daily, weekly-sunday, weekly-monday, monthly, manual / Default: daily +.br +The file rollover mode of the timelapse video. +.TP +.B ffmpeg_variable_bitrate integer +Values: 0, 2 - 31 / Default: 0 (disabled) +.br +Enables and defines variable bitrate for the ffmpeg encoder. ffmpeg_bps is ignored if variable bitrate is enabled. Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, or the range 2 - 31 where 2 means best quality and 31 is worst. +.TP +.B ffmpeg_video_codec discrete strings +Values: mpeg1 (ffmpeg-0.4.8 only), mpeg4, msmpeg4 / Default: mpeg4 +.br +Codec to be used by ffmpeg for the video compression. Timelapse mpegs are always made in mpeg1 format independent from this option. +.TP +.B framerate integer +Values: 2 - 100 / Default: 100 (no limit) +.br +Maximum number of frames to be captured from the camera per second. +.TP +.B frequency boolean +Values: 0 - 999999 / Default: 0 (Not set) +.br +The frequency to set the tuner to (kHz). Valid range: per tuner spec, default: 0 (Don't set it) +.TP +.B gap integer +Values: 0 - 2147483647 / Default: 60 +.br +Gap is the seconds of no motion detection that triggers the end of an event. An event is defined as a series of motion images taken within a short timeframe. +.TP +.B height integer +Values: Device Dependent / Default: 288 +.br +The height of each frame in pixels. +.TP +.B hue integer +Values: 0 - 255 / Default: 0 (disabled) +.br +The hue level for the video device. +.TP +.B input integer +Values: 0 - 7, 8 = disabled / Default: 8 (disabled) +.br +Input channel to use expressed as an integer number starting from 0. Should normally be set to 1 for video/TV cards, and 8 for USB cameras. +.TP +.B jpeg_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d%H%M%S-%q +.br +File path for motion triggered images (jpeg or ppm) relative to target_dir. Value 'preview' makes a jpeg filename with the same name body as the associated saved mpeg movie file. +.TP +.B lightswitch integer +Values: 0 - 100 / Default: 0 (disabled) +.br +Ignore sudden massive light intensity changes given as a percentage of the picture area that changed intensity. +.TP +.B locate boolean +Values: on, off, preview / Default: off +.br +Locate and draw a box around the moving object. Value 'preview' makes Motion only draw a box on a saved preview jpeg image and not on the saved mpeg movie. +.TP +.B low_cpu integer +Values: 0 - 100 / Default: 0 (disabled) +.br +When this option is not zero motion will be in a low cpu mode while not detecting motion. In low cpu mode Motion reduces the framerate to the value given for this option. Value zero means disabled. +.TP +.B mask_file string +Values: Max 4095 characters / Default: Not defined +.br +PGM file to use as a sensitivity mask. This picture MUST have the same width and height as the frames being captured and be in binary format. +.TP +.B max_mpeg_time integer +Values: 0 (infinite) - 2147483647 / Default: 3600 +.br +The maximum length of an mpeg movie in seconds. Set this to zero for unlimited length. +.TP +.B minimum_gap integer +Values: 0 - 2147483647 / Default: 0 (no minimum) +.br +The minimum time between two shots in seconds. +.TP +.B minimum_motion_frames boolean +Values: 1 - 1000s / Default: 1 +.br +Picture frames must contain motion at least the specified number of frames in a row before they are detected as true motion. At the default of 1, all motion is detected. Valid range is 1 to thousands, but it is recommended to keep it within 1-10. +.TP +.B motion_video_pipe string +Values: Max 4095 characters / Default: Not defined +.br +The video4linux video loopback input device for motion images. If a particular pipe is to be used then use the device filename of this pipe, if a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. Default: not set +.TP +.B mysql_db string +Values: Max 4095 characters / Default: Not defined +.br +Name of the MySQL database. +.TP +.B mysql_host string +Values: Max 4095 characters / Default: Not defined +.br +IP address or domain name for the MySQL server. Use "localhost" if motion and MySQL runs on the same server. +.TP +.B mysql_password string +Values: Max 4095 characters / Default: Not defined +.br +The MySQL password. +.TP +.B mysql_user string +Values: Max 4095 characters / Default: Not defined +.br +The MySQL user name. +.TP +.B netcam_proxy string +Values: Max 4095 characters / Default: Not defined +.br +URL to use for a netcam proxy server, if required. The syntax is http://myproxy:portnumber +.TP +.B netcam_url string +Values: Max 4095 characters / Default: Not defined +.br +Specify an url to a downloadable jpeg file or raw mjpeg stream to use as input device. Such as an AXIS 2100 network camera. +.TP +.B netcam_userpass string +Values: Max 4095 characters / Default: Not defined +.br +For network cameras protected by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. +.TP +.B night_compensate boolean +Values: on, off / Default: off +.br +When this option is set the noise threshold will be lowered if the picture is dark. This will improve the sensitivity in dark places. However it might also increase the number of false alarms since most cameras also compensate for this with their AGC which will increase noise. +.TP +.B noise_level integer +Values: 1 - 255 / Default: 32 +.br +The noise level is used as a threshold for distinguishing between noise and motion. +.TP +.B noise_tune boolean +Values: on, off / Default: on +.br +Activates the automatic tuning of noise level. +.TP +.B norm discrete strings +Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour) / Default: 0 (PAL) +.br +Select the norm of the video device. Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +.TP +.B on_event_end string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an event ends after a period of no motion. The period of no motion is defined by option gap. You can use Conversion Specifiers and spaces as part of the command. +.TP +.B on_event_start string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an event starts. An event starts at first motion detected after a period of no motion defined by gap. You can use ConversionSpecifiers and spaces as part of the command. +.TP +.B on_motion_detected string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when a motion frame is detected. You can use Conversion Specifiers and spaces as part of the command. +.TP +.B on_movie_end string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an ffmpeg movie is closed at the end of an event. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +.TP +.B on_movie_start string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an mpeg movie is created. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +.TP +.B on_picture_save string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an image is saved. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +.TP +.B output_all boolean +Values: on, off / Default: off +.br +Picture are saved continuously as if motion was detected all the time. +.TP +.B output_motion boolean +Values: on, off / Default: off +.br +Output pictures with only the moving object. This feature generates the special motion type movies where you only see the pixels that changes as a graytone image. If labelling is enabled you see the largest area in blue. Smartmask is shown in red. +.TP +.B output_normal discrete strings +Values: on, off, first, best / Default: on +.br +Normal image is an image that is stored when motion is detected. It is the same image that was taken by the camera. I.e. not a motion image like defined by output_motion. Default is that normal images are stored. +.TP +.B pgsql_db string +Values: Max 4095 characters / Default: Not defined +.br +Name of the PostgreSQL database. +.TP +.B pgsql_host string +Values: Max 4095 characters / Default: Not defined +.br +IP address or domain name for the PostgreSQL server. Use "localhost" if motion and PostgreSQL runs on the same server. +.TP +.B pgsql_password string +Values: Max 4095 characters / Default: Not defined +.br +The PostgreSQL password. +.TP +.B pgsql_port integer +Values: 0 - 65535 / Default: 5432 +.br +The PostgreSQL server port number. +.TP +.B pgsql_user string +Values: Max 4095 characters / Default: Not defined +.br +The PostgreSQL user name. +.TP +.B post_capture integer +Values: 0 - 2147483647 / Default: 0 (disabled) +.br +Specifies the number of frames to be captured after motion has been detected. +.TP +.B ppm boolean +Values: on, off / Default: off +.br +Output ppm images instead of jpeg. This uses less CPU time, but causes a LOT of hard disk I/O, and it is generally slower than jpeg. +.TP +.B pre_capture integer +Values: 0 - 100s / Default: 0 (disabled) +.br +Specifies the number of previous frames to be outputted at motion detection. Recommended range: 0 to 5, default=0. Do not use large values! Large values will cause Motion to skip video frames and cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead. +.TP +.B quality integer +Values: 1 - 100 / Default: 75 +.br +The quality for the jpeg images in percent. +.TP +.B quiet boolean +Values: on, off / Default: off +.br +Be quiet, don't output beeps when detecting motion. +.TP +.B rotate discrete strings +Values: 0, 90, 180, 270 / Default: 0 +.br +Rotate image the given number of degrees. The rotation affects all saved images as well as mpeg movies. +.TP +.B roundrobin_frames integer +Values: 1 - 2147483647 / Default: 1 +.br +Specifies the number of frames to capture before switching inputs, this way also slow switching (e.g. every second) is possible. +.TP +.B roundrobin_skip integer +Values: 1 - 2147483647 / Default: 1 +.br +Specifies the number of frames to skip after a switch. (1 if you are feeling lucky, 2 if you want to be safe). +.TP +.B saturation integer +Values: 0 - 255 / Default: 0 (disabled) +.br +The colour saturation level for the video device. +.TP +.B setup_mode boolean +Values: on, off / Default: off +.br +Run Motion in setup mode. +.TP +.B smart_mask_speed integer +Values: 0 - 10 / Default: 0 (disabled) +.br +Slugginess of the smart mask. Default is 0 = DISABLED. 1 is slow, 10 is fast. +.TP +.B snapshot_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d%H%M%S-snapshot +.br +File path for snapshots (jpeg or ppm) relative to target_dir. +.TP +.B snapshot_interval integer +Values: 0 - 2147483647 / Default: 0 (disabled) +.br +Make automated snapshots every 'snapshot_interval' seconds. +.TP +.B sql_log_image boolean +Values: on, off / Default: on +.br +Log to the database when creating motion triggered image file. +.TP +.B sql_log_mpeg boolean +Values: on, off / Default: off +.br +Log to the database when creating motion triggered mpeg file. +.TP +.B sql_log_snapshot boolean +Values: on, off / Default: on +.br +Log to the database when creating a snapshot image file. +.TP +.B sql_log_timelapse boolean +Values: on, off / Default: off +.br +Log to the database when creating timelapse mpeg file +.TP +.B sql_query string +Values: Max 4095 characters / Default: insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +.br +SQL query string that is sent to the database. The values for each field are given by using convertion specifiers +.TP +.B switchfilter boolean +Values: on, off / Default: off +.br +Turns the switch filter on or off. The filter can distinguish between most switching noise and real motion. With this you can even set roundrobin_skip to 1 without generating much false detection. +.TP +.B target_dir string +Values: Max 4095 characters / Default: Not defined = current working directory +.br +Target directory for picture and movie files. +.TP +.B text_changes boolean +Values: on, off / Default: off +.br +Turns the text showing changed pixels on/off. +.TP +.B text_double boolean +Values: on, off / Default: off +.br +Draw characters at twice normal size on images. +.TP +.B text_event string +Values: Max 4095 characters / Default: %Y%m%d%H%M%S +.br +This option defines the value of the speciel event conversion specifier %C. You can use any conversion specifier in this option except %C. Date and time values are from the timestamp of the first image in the current event. +.TP +.B text_left string +Values: Max 4095 characters / Default: Not defined +.br +User defined text overlayed on each in the lower left corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > | , . : - + _ \n and conversion specifiers (codes starting by a %). +.TP +.B text_right string +Values: Max 4095 characters / Default: %Y-%m-%d\n%T +.br +User defined text overlayed on each in the lower right corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > | , . : - + _ \n and conversion specifiers (codes starting by a %). Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +.TP +.B thread string +Values: Max 4095 characters / Default: Not defined +.br +Specifies full path and filename for a thread config file. Each camera needs a thread config file containing the options that are unique to the camera. If you only have one camera you do not need thread config files. If you have two or more cameras you need one thread config file for each camera in addition to motion.conf. This option must be placed in motion.conf and not in a thread config file. +.TP +.B threshold integer +Values: 1 - 2147483647 / Default: 1500 +.br +Threshold for declaring motion. The threshold is the number of changed pixels counted after noise filtering, masking, despeckle, and labelling. +.TP +.B threshold_tune boolean +Values: on, off / Default: off +.br +Activates the automatic tuning of threshold level. +.TP +.B timelapse_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d-timelapse +.br +File path for timelapse mpegs relative to target_dir (ffmpeg only). +.TP +.B track_auto boolean +Values: on, off / Default: off +.br +Enable auto tracking +.TP +.B track_iomojo_id integer +Values: 0 - 2147483647 / Default: 0 +.br +Use this option if you have an iomojo smilecam connected to the serial port instead of a general stepper motor controller. +.TP +.B track_maxx integer +Values: 0 - 2147483647 / Default: 0 +.br +The maximum position for servo x. +.TP +.B track_motorx integer +Values: -1 - 2147483647 / Default: -1 +.br +The motor number that is used for controlling the x-axis. +.TP +.B track_move_wait integer +Values: 0 - 2147483647 / Default: 10 +.br +Delay during which tracking is disabled after auto tracking has moved the camera. Delay is defined as number of picture frames. +.TP +.B track_port string +Values: Max 4095 characters / Default: Not defined +.br +This is the device name of the serial port to which the stepper motor interface is connected. +.TP +.B track_speed integer +Values: 0 - 255 / Default: 255 +.br +Speed to set the motor to. +.TP +.B track_step_angle_x integer +Values: 0-90 / Default: 10 +.br +Angle in degrees the camera moves per step on the X-axis with auto tracking. Currently only used with pwc type cameras. +.TP +.B track_step_angle_y integer +Values: 0-40 / Default: 10 +.br +Angle in degrees the camera moves per step on the Y-axis with auto tracking. Currently only used with pwc type cameras. +.TP +.B track_stepsize integer +Values: 0 - 255 / Default: 40 +.br +Number of steps to make. +.TP +.B track_type discrete strings +Values: 0 (none), 1 (stepper), 2 (iomojo), 3 (pwc), 4 (generic) / Default: 0 (None) +.br +Type of tracker. +.TP +.B tunerdevice string +Values: Max 4095 characters / Default: /dev/tuner0 +.br +The tuner device used for controlling the tuner in a tuner card. This option is only used when Motion is compiled for FreeBSD. +.TP +.B video_pipe string +Values: Max 4095 characters / Default: Not defined +.br +The video4linux video loopback input device for normal images. If a particular pipe is to be used then use the device filename of this pipe. If a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. +.TP +.B videodevice string +Values: Max 4095 characters / Default: /dev/video0 (FreeBSD: /dev/bktr0) +.br +The video device to be used for capturing. Default for Linux is /dev/video0. for FreeBSD the default is /dev/bktr0. +.TP +.B webcam_limit integer +Values: 0 - 2147483647 / Default: 0 (unlimited) +.br +Limit the number of frames to number frames. After 'webcam_limit' number of frames the connection will be closed by motion. The value 0 means unlimited. +.TP +.B webcam_localhost boolean +Values: on, off / Default: on +.br +Limits the access to the webcam to the localhost. +.TP +.B webcam_maxrate integer +Values: 1 - 100 / Default: 1 +.br +Limit the framerate of the webcam. Default is 1. Set the value to 100 for practically unlimited. +.TP +.B webcam_motion boolean +Values: on, off / Default: off +.br +If set to 'on' Motion sends slows down the webcam stream to 1 picture per second when no motion is detected. When motion is detected the stream runs as defined by webcam_maxrate. When 'off' the webcam stream always runs as defined by webcam_maxrate. +.TP +.B webcam_port integer +Values: 0 - 65535 / Default: 0 (disabled) +.br +TCP port on which motion will listen for incoming connects with its webcam server. +.TP +.B webcam_quality integer +Values: 1 - 100 / Default: 50 +.br +Quality setting in percent for the mjpeg picture frames transferred over the webcam connection. Keep it low to restrict needed bandwidth. +.TP +.B width integer +Values: Device Dependent / Default: 352 +.br +The width in pixels of each frame. Valid range is camera dependent. + +.SH SIGNALS +Motion responds to the following signals: +.TP +.B SIGHUP +The config file will be reread. +.TP +.B SIGTERM +If needed motion will create an mpeg file of the last event and exit +.TP +.B SIGUSR1 +Motion will create an mpeg file of the current event. +.SH NOTES +.TP +.B Snapshot +A snapshot is a picture taken at regular intervals independently of any movement in the picture. +.TP +.B Motion image +A "motion" image/mpeg shows the pixels that have actually changed during the last frames. These pictures are not very useful for normal presentation to the public but they are quite useful for testing and tuning and making mask files as you can see exactly where motion sees something moving. Motion is shown in greytones. If labelling is enabled the largest area is marked as blue. Smart mask is shown in read. +.TP +.B Normal image +A "normal" image is the real image taken by the camera with text overlayed. +.TP +.B Threads and config files +If Motion was invoked with command line option -c pathname Motion will expect the config file to be as specified. When you specify the config file on the command line with -c you can call it anything. +.br +If you do not specify -c or the filename you give Motion does not exist, Motion will search for the configuration file called 'motion.conf' in the following order: +.br +1. Current directory from where motion was invoked +.br +2. Then in a directory called '.motion' in the current users home directory (shell environment variable $HOME). E.g. /home/goofy/.motion/motion.conf +.br +3. The directory defined by the --sysconfdir=DIR when running .configure during installation of Motion (If this option was not defined the default is /usr/local/etc/) +.br +If you have write access to /usr/local/etc then the editor recommends having only one motion.conf file in the default /usr/local/etc/ directory. +.br +Motion has a configuration file in the distribution package called motion-dist.conf. When you run 'make install' this files gets copied to the /usr/local/etc directory. +.br +The configuration file needs to be renamed from motion-dist.conf to motion.conf. The original file is called motion-dist.conf so that your perfectly working motion.conf file does not accidentally get overwritten when you re-install or upgrade to a newer version of Motion. +.br +If you have more than one camera you should not try and invoke Motion more times. Motion is made to work with more than one camera in a very elegant way and the way to do it is to create a number of thread config files. Motion will then create an extra tread of itself for each camera. If you only have one camera you only need the motion.conf file. The minute you have two or more cameras you must have one thread config file per camera besides the motion.conf file. +.br +So if you have for example two cameras you need motion.conf and two thread config files. Total of 3 config files. +.br +An option that is common to all cameras can be placed in motion.conf. (You can also put all parameters in the thread files but that makes a lot of editing when you change a common thing). +.br +An option that is unique to a camera must be defined in each thread file. +.br +The first camera is defined in the first thread file called from motion.conf. The 2nd camera is defined in the 2nd thread file called from motion.conf etc. +.br +Any option defined in motion.conf will be used for all cameras except for the cameras in which the same option is defined in a thread config file. +.br +Motion reads its configuration parameters in the following sequence. If the same parameter exists more than one place the last one read wins. +.br +1. Motion reads the configuration file motion.conf from the beginning of the file going down line by line. +.br +2. If the option "thread" is defined in motion.conf, the thread configuration file(s) is/(are) read. +.br +3. Motion continues reading the rest of the motion.conf file. Any options from here will overrule the same option previously defines in a thread config file. +.br +4. Motion reads the command line option again overruling any previously defined options. +.br +So always call the thread config files in the end of the motion.conf file. If you define options in motion.conf AFTER the thread file calls, the same options in the thread files will never be used. So always put the thread file call at the end of motion.conf. +.br +If motion is built without specific features such as ffmpeg, mysql etc it will ignore the options that belongs to these features. You do not have to remove them or comment them out. +.br +If you run the http control command http://host:port/0/config/writeyes, motion will overwrite motion.conf and all the thread.conf files by autogenerated config files neatly formatted and only with the features included that Motion was built with. If you later re-build Motion with more features or upgrade to a new version, you can use your old config files, run the motion.conf.write command, and you will have new config files with the new options included all set to their default values. This makes upgrading very easy to do. +.TP +.B Conversion Specifiers for Advanced Filename and Text Features +The table below shows all the supported Conversion Specifiers you can use in the options text_left, text_right, snapshot_filename, jpeg_filename, ffmpeg_filename, timelapse_filename, on_event_start, on_event_end, on_picture_save, on_movie_start, on_movie_end, and on_motion_detected. +.br +In text_left and text_right you can additionally use '\n' for new line. +.TP +.B %a +The abbreviated weekday name according to the current locale. +.TP +.B %A +The full weekday name according to the current locale. +.TP +.B %b +The abbreviated month name according to the current locale. +.TP +.B %B +The full month name according to the current locale. +.TP +.B %c +The preferred date and time representation for the current locale. +.TP +.B %C +Text defined by the text_event feature +.TP +.B %d +The day of the month as a decimal number (range 01 to 31). +.TP +.B %D +Number of pixels detected as Motion. If labelling is enabled the number is the number of pixels in the largest labelled motion area. +.TP +.B %E +Modifier: use alternative format, see below. +.TP +.B %f +File name - used in the on_picture_save, on_movie_start, on_movie_end, and sql_query features. +.TP +.B %F +Equivalent to %Y-%m-%d (the ISO 8601 date format). +.TP +.B %H +The hour as a decimal number using a 24-hour clock (range 00 to 23). +.TP +.B %i +Width of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on). +.TP +.B %I +The hour as a decimal number using a 12-hour clock (range 01 to 12). +.TP +.B %j +The day of the year as a decimal number (range 001 to 366). +.TP +.B %J +Height of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on). +.TP +.B %k +The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) +.TP +.B %K +X coordinate in pixels of the center point of motion. Origin is upper left corner. +.TP +.B %l +The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) +.TP +.B %L +Y coordinate in pixels of the center point of motion. Origin is upper left corner and number is positive moving downwards (I may change this soon). +.TP +.B %m +The month as a decimal number (range 01 to 12). +.TP +.B %M +The minute as a decimal number (range 00 to 59). +.TP +.B %n +Filetype as used in the on_picture_save, on_movie_start, on_movie_end, and sql_query features. +.TP +.B %N +Noise level. +.TP +.B %o +Threshold. The number of detected pixels required to trigger motion. When threshold_tune is 'on' this can be used to show the current tuned value of threshold. +.TP +.B %p +Either 'AM' or 'PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. +.TP +.B %P +Like %p but in lowercase: `am' or `pm' or a corresponding string for the current locale. +.TP +.B %q +Picture frame number within current second. For jpeg filenames this should always be included in the filename if you save more then 1 picture per second to ensure unique filenames. It is not needed in filenames for mpegs. +.TP +.B %Q +Number of detected labels found by the despeckle feature +.TP +.B %r +The time in a.m. or p.m. notation. +.TP +.B %R +The time in 24-hour notation (%H:%M). +.TP +.B %s +The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC. +.TP +.B %S +The second as a decimal number (range 00 to 61). +.TP +.B %t +Thread number (camera number) +.TP +.B %T +The time in 24-hour notation (%H:%M:%S). +.TP +.B %u +The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w. +.TP +.B %U +The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W. +.TP +.B %v +Event number. An event is a series of motion detections happening with less than 'gap' seconds between them. +.TP +.B %V +The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W. +.TP +.B %w +The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. +.TP +.B %W +The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. +.TP +.B %x +The preferred date representation for the current locale without the time. +.TP +.B %X +The preferred time representation for the current locale without the date. +.TP +.B %y +The year as a decimal number without a century (range 00 to 99). +.TP +.B %Y +The year as a decimal number including the century. +.TP +.B %z +The time-zone as hour offset from GMT. +.TP +.B %Z +The time zone or name or abbreviation. + +.TP +.B More information +Motion homepage: http://motion.sourceforge.net/ + +Motion Guide (user and installation guide): +.br +http://www.lavrsen.dk/twiki/bin/view/Motion/MotionGuide +.SH AUTHORS +Jeroen Vreeken (pe1rxq@amsat.org), +Folkert van Heusden, +Kenneth Lavrsen (kenneth@lavrsen.dk) diff --git a/debian/motion.conf b/debian/motion.conf new file mode 100644 index 0000000..c0a982c --- /dev/null +++ b/debian/motion.conf @@ -0,0 +1,577 @@ +# Rename this distribution example file to motion.conf +# +# This config file was generated by motion 3.2.4 + + +############################################################ +# Daemon +############################################################ + +# Start in daemon (background) mode and release terminal (default: off) +daemon off + +############################################################ +# Basic Setup Mode +############################################################ + +# Start in Setup-Mode, daemon disabled. (default: off) +setup_mode off + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +videodevice /dev/video0 + +# Tuner device to be used for capturing using tuner as source (default /dev/tuner0) +# This is ONLY used for FreeBSD. Leave it commented out for Linux +; tunerdevice /dev/tuner0 + +# The video input to be used (default: 8) +# Should normally be set to 1 for video/TV cards, and 8 for USB cameras +input 8 + +# The video norm to use (only for video capture and TV tuner cards) +# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +norm 0 + +# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0) +frequency 0 + +# Rotate image this number of degrees. The rotation affects all saved images as +# well as mpeg movies. Valid values: 0 (default = no rotation), 90, 180 and 270. +rotate 0 + +# Image width (pixels). Valid range: Camera dependent, default: 352 +width 320 + +# Image height (pixels). Valid range: Camera dependent, default: 288 +height 240 + +# Maximum number of frames to be captured per second. +# Valid range: 2-100. Default: 100 (almost no limit). +framerate 2 + +# URL to use if you are using a network camera, size will be autodetected (incl http://) +# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined +; netcam_url value + +# Username and password for network camera (only if required). Default: not defined +# Syntax is user:password +; netcam_userpass value + +# URL to use for a netcam proxy server, if required, e.g. "http://myproxy". +# If a port number other than 80 is needed, use "http://myproxy:1234". +# Default: not defined +; netcam_proxy value + +# Let motion regulate the brightness of a video device (default: off). +# The auto_brightness feature uses the brightness option as its target value. +# If brightness is zero auto_brightness will adjust to average brightness value 128. +# Only recommended for cameras without auto brightness +auto_brightness off + +# Set the initial brightness of a video device. +# If auto_brightness is enabled, this value defines the average brightness level +# which Motion will try and adjust to. +# Valid range 0-255, default 0 = disabled +brightness 0 + +# Set the contrast of a video device. +# Valid range 0-255, default 0 = disabled +contrast 0 + +# Set the saturation of a video device. +# Valid range 0-255, default 0 = disabled +saturation 0 + +# Set the hue of a video device (NTSC feature). +# Valid range 0-255, default 0 = disabled +hue 0 + + +############################################################ +# Round Robin (multiple inputs on same video device name) +############################################################ + +# Number of frames to capture in each roundrobin step (default: 1) +roundrobin_frames 1 + +# Number of frames to skip before each roundrobin step (default: 1) +roundrobin_skip 1 + +# Try to filter out noise generated by roundrobin (default: off) +switchfilter off + + +############################################################ +# Motion Detection Settings: +############################################################ + +# Threshold for number of changed pixels in an image that +# triggers motion detection (default: 1500) +threshold 1500 + +# Automatically tune the threshold down if possible (default: off) +threshold_tune off + +# Noise threshold for the motion detection (default: 32) +noise_level 32 + +# Automatically tune the noise threshold (default: on) +noise_tune on + +# Enables motion to adjust its detection/noise level for very dark frames +# Don't use this with noise_tune on. (default: off) +night_compensate off + +# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined) +# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid. +# (l)abeling must only be used once and the 'l' must be the last letter. +# Comment out to disable +despeckle EedDl + +# PGM file to use as a sensitivity mask. +# Full path name to. (Default: not defined) +; mask_file value + +# Dynamically create a mask file during operation (default: 0) +# Adjust speed of mask changes from 0 (off) to 10 (fast) +smart_mask_speed 0 + +# Ignore sudden massive light intensity changes given as a percentage of the picture +# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled +lightswitch 0 + +# Picture frames must contain motion at least the specified number of frames +# in a row before they are detected as true motion. At the default of 1, all +# motion is detected. Valid range: 1 to thousands, recommended 1-10 +minimum_motion_frames 1 + +# Specifies the number of pre-captured (buffered) pictures from before motion +# was detected that will be output at motion detection. +# Recommended range: 0 to 5 (default: 0) +# Do not use large values! Large values will cause Motion to skip video frames and +# cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead. +pre_capture 0 + +# Number of frames to capture after motion is no longer detected (default: 0) +post_capture 0 + +# Gap is the seconds of no motion detection that triggers the end of an event +# An event is defined as a series of motion images taken within a short timeframe. +# Recommended value is 60 seconds (Default). The value 0 is allowed and disables +# events causing all Motion to be written to one single mpeg file and no pre_capture. +gap 60 + +# Minimum gap in seconds between the storing pictures while detecting motion. +# Default: 0 = as fast as possible (given by the camera framerate) +# Note: This option has nothing to do with the option 'gap' +minimum_gap 0 + +# Maximum length in seconds of an mpeg movie +# When value is exceeded a new mpeg file is created. (Default: 0 = infinite) +max_mpeg_time 0 + +# Number of frames per second to capture when not detecting +# motion (saves CPU load) (Default: 0 = disabled) +low_cpu 0 + +# Always save images even if there was no motion (default: off) +output_all off + + +############################################################ +# Image File Output +############################################################ + +# Output 'normal' pictures when motion is detected (default: on) +# Valid values: on, off, first, best +# When set to 'first', only the first picture of an event is saved. +# Picture with most motion of an event is saved when set to 'best'. +# Can be used as preview shot for the corresponding movie. +output_normal on + +# Output pictures with only the pixels moving object (ghost images) (default: off) +output_motion off + +# The quality (in percent) to be used by the jpeg compression (default: 75) +quality 75 + +# Output ppm images instead of jpeg (default: off) +ppm off + + +############################################################ +# Film (mpeg) File Output - ffmpeg based +############################################################ + +# Use ffmpeg to encode mpeg movies in realtime (default: off) +ffmpeg_cap_new on + +# Use ffmpeg to make movies with only the pixels moving +# object (ghost images) (default: off) +ffmpeg_cap_motion off + +# Use ffmpeg to encode a timelapse movie +# Default value 0 = off - else save frame every Nth second +ffmpeg_timelapse 0 + +# The file rollover mode of the timelapse video +# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual +ffmpeg_timelapse_mode daily + +# Bitrate to be used by the ffmpeg encoder (default: 400000) +# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled) +ffmpeg_bps 500000 + +# Enables and defines variable bitrate for the ffmpeg encoder. +# ffmpeg_bps is ignored if variable bitrate is enabled. +# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, +# or the range 2 - 31 where 2 means best quality and 31 is worst. +ffmpeg_variable_bitrate 0 + +# Codec to used by ffmpeg for the video compression. +# Timelapse mpegs are always made in mpeg1 format independent from this option. +# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4. +# mpeg1 - gives you files with extension .mpg +# mpeg4 or msmpeg4 - give you files with extension .avi +# msmpeg4 is recommended for use with Windows Media Player because +# it requires no installation of codec on the Windows client. +ffmpeg_video_codec mpeg4 + + +############################################################ +# Snapshots (Traditional Periodic Webcam File Output) +############################################################ + +# Make automated snapshot every N seconds (default: 0 = disabled) +snapshot_interval 0 + + +############################################################ +# Text Display +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, %T = HH:MM:SS, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, \n = new line, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event - do not use with text_event! +# You can put quotation marks around the text to allow +# leading spaces +############################################################ + +# Locate and draw a box around the moving object. +# Valid values: on, off and preview (default: off) +# Set to 'preview' will only draw a box in preview_shot pictures. +locate off + +# Draws the timestamp using same options as C function strftime(3) +# Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +# Text is placed in lower right corner +text_right %Y-%m-%d\n%T-%q + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +; text_left CAMERA %t + +# Draw the number of changed pixed on the images (default: off) +# Will normally be set to off except when you setup and adjust the motion settings +# Text is placed in upper right corner +text_changes off + +# This option defines the value of the speciel event conversion specifier %C +# You can use any conversion specifier in this option except %C. Date and time +# values are from the timestamp of the first image in the current event. +# Default: %Y%m%d%H%M%S +# The idea is that %C can be used filenames and text_left/right for creating +# a unique identifier for each event. +text_event %Y%m%d%H%M%S + +# Draw characters at twice normal size on images. (default: off) +text_double off + + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute path. (Default: current working directory) +target_dir /tmp + +# File path for snapshots (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-snapshot +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot +# File extension .jpg or .ppm is automatically added so do not include this. +# Note: A symbolic link called lastsnap.jpg created in the target_dir will always +# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' +snapshot_filename %v-%Y%m%d%H%M%S-snapshot + +# File path for motion triggered images (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-%q +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q +# File extension .jpg or .ppm is automatically added so do not include this +# Set to 'preview' together with best-preview feature enables special naming +# convention for preview shots. See motion guide for details +jpeg_filename %v-%Y%m%d%H%M%S-%q + +# File path for motion triggered ffmpeg films (mpeg) relative to target_dir +# Default: %v-%Y%m%d%H%M%S +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S +# File extension .mpg or .avi is automatically added so do not include this +ffmpeg_filename %v-%Y%m%d%H%M%S + +# File path for timelapse mpegs relative to target_dir +# Default: %Y%m%d-timelapse +# Default value is near equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse +# File extension .mpg is automatically added so do not include this +timelapse_filename %Y%m%d-timelapse + + +############################################################ +# Live Webcam Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +webcam_port 8081 + +# Quality of the jpeg images produced (default: 50) +webcam_quality 50 + +# Output frames at 1 fps when no motion is detected and increase to the +# rate given by webcam_maxrate when motion is detected (default: off) +webcam_motion off + +# Maximum framerate for webcam streams (default: 1) +webcam_maxrate 1 + +# Restrict webcam connections to localhost only (default: on) +webcam_localhost on + +# Limits the number of images per connection (default: 0 = unlimited) +# Number can be defined by multiplying actual webcam rate by desired number of seconds +# Actual webcam rate is the smallest of the numbers framerate and webcam_maxrate +webcam_limit 0 + + +############################################################ +# HTTP Based Control +############################################################ + +# TCP/IP port for the http server to listen on (default: 0 = disabled) +control_port 8080 + +# Restrict control connections to localhost only (default: on) +control_localhost on + +# Output for http server, select off to choose raw text plain (default: on) +control_html_output on + +# Authentication for the http based control. Syntax username:password +# Default: not defined (Disabled) +; control_authentication username:password + + +############################################################ +# Tracking (Pan/Tilt) +############################################################ + +# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic) +# The generic type enables the definition of motion center and motion size to +# be used with the convertion specifiers for options like on_motion_detected +track_type 0 + +# Enable auto tracking (default: off) +track_auto off + +# Serial port of motor (default: none) +; track_port value + +# Motor number for x-axis (default: -1) +track_motorx -1 + +# Maximum value on x-axis (default: 0) +track_maxx 0 + +# ID of an iomojo camera if used (default: 0) +track_iomojo_id 0 + +# Angle in degrees the camera moves per step on the X-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_x 10 + +# Angle in degrees the camera moves per step on the Y-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_y 10 + +# Delay to wait for after auto-track movement as number +# of picture frames (default: 10) +track_move_wait 10 + +# Speed to set the motor to (default: 255) +track_speed 255 + +# Number of steps to make (default: 40) +track_stepsize 40 + + +############################################################ +# External Commands, Warnings and Logging: +# You can use conversion specifiers for the on_xxxx commands +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# %f = filename with full path +# %n = number indicating filetype +# Both %f and %n are only defined for on_picture_save, +# on_movie_start and on_movie_end +# Quotation marks round string are allowed. +############################################################ + +# Do not sound beeps when detecting motion (default: on) +# Note: Motion never beeps when running in daemon mode. +quiet on + +# Command to be executed when an event starts. (default: none) +# An event starts at first motion detected after a period of no motion defined by gap +; on_event_start value + +# Command to be executed when an event ends after a period of no motion +# (default: none). The period of no motion is defined by option gap. +; on_event_end value + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# To give the filename as an argument to a command append it with %f +; on_picture_save value + +# Command to be executed when a motion frame is detected (default: none) +; on_motion_detected value + +# Command to be executed when a movie file (.mpg|.avi) is created. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_start value + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_end value + + +############################################################ +# Common Options For MySQL and PostgreSQL database features. +# Options require the MySQL/PostgreSQL options to be active also. +############################################################ + +# Log to the database when creating motion triggered image file (default: on) +sql_log_image on + +# Log to the database when creating a snapshot image file (default: on) +sql_log_snapshot on + +# Log to the database when creating motion triggered mpeg file (default: off) +sql_log_mpeg off + +# Log to the database when creating timelapse mpeg file (default: off) +sql_log_timelapse off + +# SQL query string that is sent to the database +# Use same conversion specifiers has for text features +# Additional special conversion specifiers are +# %n = the number representing the file_type +# %f = filename with full path +# Default value: +# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') + + +############################################################ +# Database Options For MySQL +############################################################ + +# Mysql database to log to (default: not defined) +; mysql_db value + +# The host on which the database is located (default: not defined) +; mysql_host value + +# User account name for MySQL database (default: not defined) +; mysql_user value + +# User password for MySQL database (default: not defined) +; mysql_password value + + +############################################################ +# Database Options For PostgreSQL +############################################################ + +# PostgreSQL database to log to (default: not defined) +; pgsql_db value + +# The host on which the database is located (default: not defined) +; pgsql_host value + +# User account name for PostgreSQL database (default: not defined) +; pgsql_user value + +# User password for PostgreSQL database (default: not defined) +; pgsql_password value + +# Port on which the PostgreSQL database is located (default: 5432) +; pgsql_port 5432 + + +############################################################ +# Video Loopback Device (vloopback project) +############################################################ + +# Output images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; video_pipe value + +# Output motion images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; motion_video_pipe value + + +############################################################## +# Thread config files - One for each camera. +# Except if only one camera - You only need this config file. +# If you have more than one camera you MUST define one thread +# config file for each camera in addition to this config file. +############################################################## + +# Remember: If you have more than one camera you must have one +# thread file for each camera. E.g. 2 cameras requires 3 files: +# This motion.conf file AND thread1.conf and thread2.conf. +# Only put the options that are unique to each camera in the +# thread config files. +; thread /usr/local/etc/thread1.conf +; thread /usr/local/etc/thread2.conf +; thread /usr/local/etc/thread3.conf +; thread /usr/local/etc/thread4.conf + diff --git a/debian/motion.config b/debian/motion.config new file mode 100644 index 0000000..bb98244 --- /dev/null +++ b/debian/motion.config @@ -0,0 +1,13 @@ +#!/bin/sh +# config script for motion + +set -e + +. /usr/share/debconf/confmodule + +if dpkg --compare-versions "$2" lt-nl "3.1.19-1"; then + db_input high motion/moved_conf_dir || true + db_go || true +fi + +exit 0 diff --git a/debian/motion.dirs b/debian/motion.dirs new file mode 100644 index 0000000..7d8d457 --- /dev/null +++ b/debian/motion.dirs @@ -0,0 +1,3 @@ +usr/bin +usr/share/man/man1 +etc/motion diff --git a/debian/motion.docs b/debian/motion.docs new file mode 100644 index 0000000..9af2035 --- /dev/null +++ b/debian/motion.docs @@ -0,0 +1,5 @@ +FAQ +README +README.axis_2100 +CREDITS +motion_guide.html diff --git a/debian/motion.examples b/debian/motion.examples new file mode 100644 index 0000000..e26e3a4 --- /dev/null +++ b/debian/motion.examples @@ -0,0 +1,6 @@ +motion.init-Debian +motion-dist.conf +thread1.conf +thread2.conf +thread3.conf +thread4.conf diff --git a/debian/motion.manpages b/debian/motion.manpages new file mode 100644 index 0000000..656cc8a --- /dev/null +++ b/debian/motion.manpages @@ -0,0 +1 @@ +debian/motion.1 diff --git a/debian/motion.postinst b/debian/motion.postinst new file mode 100644 index 0000000..7e9f57f --- /dev/null +++ b/debian/motion.postinst @@ -0,0 +1,13 @@ +#!/bin/sh +# postinst script for motion + +set -e + + +. /usr/share/debconf/confmodule + +db_stop + +#DEBHELPER# + +exit 0 diff --git a/debian/motion.preinst b/debian/motion.preinst new file mode 100644 index 0000000..35363a7 --- /dev/null +++ b/debian/motion.preinst @@ -0,0 +1,15 @@ +#!/bin/sh +# preinst script for motion + +set -e + +. /usr/share/debconf/confmodule + +if dpkg --compare-versions "$2" lt-nl "3.1.14-1"; then + db_input high motion/moved_conf_dir || true + db_go || true +fi + +#DEBHELPER# + +exit 0 diff --git a/debian/motion.templates b/debian/motion.templates new file mode 100644 index 0000000..dc39bb1 --- /dev/null +++ b/debian/motion.templates @@ -0,0 +1,11 @@ +Template: motion/moved_conf_dir +Type: note +Description: Location of Motion's config files has changed + The default location of Motion's configuration files has moved from /etc/ + to /etc/motion/. So if you already have a motion.conf in /etc/ from a + previous installation of Motion, you should move this to /etc/motion/ in + order for it to take effect whenever Motion is used. + . + The same thing goes for any thread*.conf files you may have. You should + move these to /etc/motion/ as well (in this case, also remember to update + the path to the thread*.conf files inside motion.conf). diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..f636e79 --- /dev/null +++ b/debian/rules @@ -0,0 +1,118 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -Wall -g +#CFLAGS += `ffmpeg-config --cflags` + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + + +config.status: configure + dh_testdir + # Add here commands to configure the package. + CFLAGS="$(CFLAGS)" ./configure \ + --prefix=/usr \ + --bindir=\$${prefix}/bin \ + --build=${DEB_BUILD_GNU_TYPE} \ + --host=${DEB_HOST_GNU_TYPE} \ + --datadir=\$${prefix}/share \ + --mandir=\$${datadir}/man \ + --infodir=\$${datadir}/info \ + --sysconfdir=/etc/motion \ + --with-ffmpeg \ + --with-mysql \ + --without-pgsql \ + --without-optimizecpu \ + --without-jpeg-mmx + + touch configure-stamp + + +build: build-stamp + +build-stamp: config.status + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + + touch build-stamp + + +clean: clean-patched +clean-patched: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean +ifneq "$(wildcard /usr/share/misc/config.sub)" "" + cp -f /usr/share/misc/config.sub config.sub +endif +ifneq "$(wildcard /usr/share/misc/config.guess)" "" + cp -f /usr/share/misc/config.guess config.guess +endif + + dh_clean + + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/motion. + $(MAKE) install DESTDIR=$(CURDIR)/debian/motion + # Offer a minimal default config file. + cp debian/motion.conf debian/motion/etc/motion + # Remove unwanted/misplaced files. + rm -rf $(CURDIR)/debian/motion/usr/share/doc/motion-3.2.4 + rm -f $(CURDIR)/debian/motion/etc/motion/motion-dist.conf + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs CHANGELOG + dh_installdocs + dh_installman + dh_installmenu + dh_installexamples + dh_installdebconf + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/draw.c b/draw.c new file mode 100644 index 0000000..a60d2cc --- /dev/null +++ b/draw.c @@ -0,0 +1,1178 @@ +/* + * draw.c + * + * Routines for drawing text on images + * + * Copyright 2000, Jeroen Vreeken + * This program is published under the GNU public license version 2 + * See also the file 'COPYING' + * + */ + +#include +#include "motion.h" + +/* highest ascii value is 126 (~) */ +#define ASCII_MAX 127 + +unsigned char *small_char_arr_ptr[ASCII_MAX]; + +unsigned char *big_char_arr_ptr[ASCII_MAX]; + +struct draw_char { + unsigned char ascii; + unsigned char pix[8][7]; +}; + +struct big_char { + unsigned char ascii; + unsigned char pix[16][14]; +}; + +struct draw_char draw_table[]= { + { + ' ', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '0', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,2,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,2,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '1', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '2', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,1,2,2,1,0}, + {0,1,2,1,1,0,0}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + }, + { + '3', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,1,2,2,1,0}, + {0,1,0,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '4', + { + {0,0,0,0,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,1,2,2,1,0}, + {0,1,2,1,2,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,0,0,1,0,0} + } + }, + { + '5', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,2,0}, + {0,1,1,1,1,2,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + '6', + { + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '7', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,2,1}, + {0,0,0,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,0,0,0,0} + } + }, + { + '8', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '9', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,1,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '"', + { + {0,0,1,0,1,0,0}, + {0,1,2,1,2,1,0}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '/', + { + {0,0,0,0,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,0,0,0,0} + } + }, + { + '(', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + ')', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + '@', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,2,2,2,1}, + {1,2,1,2,2,2,1}, + {1,2,1,1,1,1,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '~', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,0,0,0,0}, + {0,1,2,1,0,1,0}, + {1,2,1,2,1,2,1}, + {0,1,0,1,2,1,0}, + {0,0,0,0,1,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '#', + { + {0,0,1,0,1,0,0}, + {0,1,2,1,2,1,0}, + {1,2,2,2,2,2,1}, + {0,1,2,1,2,1,0}, + {0,1,2,1,2,1,0}, + {1,2,2,2,2,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0} + } + }, + { + '<', + { + {0,0,0,0,0,1,0}, + {0,0,0,1,1,2,1}, + {0,1,1,2,2,1,0}, + {1,2,2,1,1,0,0}, + {0,1,1,2,2,1,0}, + {0,0,0,1,1,2,1}, + {0,0,0,0,0,1,0}, + {0,0,0,0,0,0,0} + } + }, + { + '>', + { + {0,1,0,0,0,0,0}, + {1,2,1,1,0,0,0}, + {0,1,2,2,1,1,0}, + {0,0,1,1,2,2,1}, + {0,1,2,2,1,1,0}, + {1,2,1,1,0,0,0}, + {0,1,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '|', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + ',', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,0,0,0,0} + } + }, + { + '.', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,1,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + ':', + { + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,1,0,0,0}, + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,1,0,0,0} + } + }, + { + '-', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '+', + { + {0,0,0,0,0,0,0}, + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '_', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + }, + { + '\'', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + 'a', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'b', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'c', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'd', + { + {0,0,0,0,0,1,0}, + {0,0,0,0,1,2,1}, + {0,0,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'e', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,2,2,1,0}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'f', + { + {0,0,0,0,1,1,0}, + {0,0,0,1,2,2,1}, + {0,0,1,2,1,1,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'g', + { + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,1,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'h', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,1,1,0,0}, + {1,2,1,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'i', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'j', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,1,2,1,0,0}, + {1,2,2,1,0,0,0}, + {0,1,1,0,0,0,0} + } + }, + { + 'k', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,1,0,0}, + {1,2,1,1,2,1,0}, + {1,2,1,2,1,0,0}, + {1,2,2,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'l', + { + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,0,1,0,0} + } + }, + { + 'm', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,0,1,0,0}, + {1,2,2,1,2,1,0}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {0,1,0,1,0,1,0} + } + }, + { + 'n', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,1,1,0,0}, + {1,2,1,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'o', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'p', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,0,0,0,0}, + } + }, + { + 'q', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,2,1}, + {0,0,0,0,1,2,1} + } + }, + { + 'r', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,1,1,0,0}, + {1,2,1,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,0,0,1,0}, + {1,2,1,0,0,0,0}, + {0,1,0,0,0,0,0} + } + }, + { + 's', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,2,2,1,1,0}, + {0,1,1,2,2,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 't', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,0,1,0,0} + } + }, + { + 'u', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,2,2,1}, + {0,1,2,2,1,2,1}, + {0,0,1,1,0,1,0} + } + }, + { + 'v', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'w', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,2,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0} + } + }, + { + 'x', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,1,0,0}, + {1,2,1,1,2,1,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {1,2,1,1,2,1,0}, + {0,1,0,0,1,0,0} + } + }, + { + 'y', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {1,2,1,0,0,0,0} + } + }, + { + 'z', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {0,1,1,2,1,0,0}, + {0,1,2,1,1,0,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'A', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'B', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'C', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,0,0,1,0}, + {1,2,1,0,0,1,0}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'D', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'E', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + }, + { + 'F', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {0,1,0,0,0,0,0} + } + }, + { + 'G', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,1,0}, + {1,2,1,2,2,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'H', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'I', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'J', + { + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,2,1}, + {0,0,0,0,1,2,1}, + {0,1,0,0,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'K', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,1,2,1,0}, + {1,2,1,2,1,0,0}, + {1,2,2,2,1,0,0}, + {1,2,1,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'L', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'M', + { + {0,1,1,0,1,1,0}, + {1,2,2,1,2,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,1,1,2,}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'N', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,2,1,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,1,2,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'O', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'P', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {0,1,0,0,0,0,0} + } + }, + { + 'Q', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,1,2,1,0}, + {0,1,2,2,1,2,1}, + {0,0,1,1,0,1,0} + } + }, + { + 'R', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,2,1,0,0}, + {1,2,1,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'S', + { + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,2,1}, + {0,1,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'T', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,2,1,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'U', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'V', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'W', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0} + } + }, + { + 'X', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'Y', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'Z', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + } +}; + +struct big_char big_table[sizeof(draw_table) / sizeof(struct draw_char)]; + +#define NEWLINE "\\n" + +static int draw_textn (unsigned char *image, int startx, int starty, int width, char *text, int len, int factor) +{ + int pos, x, y, line_offset, next_char_offs; + unsigned char *image_ptr, *char_ptr, **char_arr_ptr; + + if (startx>width/2) + startx -= len*(6*(factor+1)); + + if (startx < 0) + startx = 0; + + if (startx+len*6*(factor+1) >= width) + len = (width-startx-1)/(6*(factor+1)); + + line_offset = width - 7*(factor+1); + next_char_offs = width*8*(factor+1) - 6*(factor+1); + + image_ptr = image + startx + starty*width; + + char_arr_ptr = factor ? big_char_arr_ptr : small_char_arr_ptr; + + for (pos = 0; pos < len; pos++) { + char_ptr = char_arr_ptr[(int)text[pos]]; + for (y=8*(factor+1); y--;) { + for (x=7*(factor+1); x--;) { + switch(*char_ptr) { + case 1: + *image_ptr = 0; + break; + case 2: + *image_ptr = 255; + break; + default: + break; + } + image_ptr++; + char_ptr++; + } + image_ptr += line_offset; + } + image_ptr -= next_char_offs; + } + return 0; +} + +int draw_text (unsigned char *image, int startx, int starty, int width, char *text, int factor) +{ + int num_nl = 0; + char *end, *begin; + const int line_space = (factor + 1) * 9; + + /* Count the number of newlines in "text" so we scroll it up the image */ + end = text; + while ((end = strstr(end, NEWLINE))) { + num_nl++; + end += sizeof(NEWLINE)-1; + } + starty -= line_space * num_nl; + + begin = end = text; + while ((end = strstr(end, NEWLINE))) { + int len = end-begin; + draw_textn(image, startx, starty, width, begin, len, factor); + end += sizeof(NEWLINE)-1; + begin = end; + starty += line_space; + } + draw_textn(image, startx, starty, width, begin, strlen(begin), factor); + return 0; +} + +int initialize_chars(void) +{ + unsigned int i=0, x, y; + + /* Fill the structure 'big_table' with double sized characters. */ + while(draw_table[i].ascii) { + big_table[i].ascii = draw_table[i].ascii; + for(x=0; x < 14; x++) { + for(y=0; y < 16; y++) { + big_table[i].pix[y][x] = draw_table[i].pix[y/2][x/2]; + } + } + i++; + } + + /* first init all char ptr's to a space character */ + for (i=0; i < ASCII_MAX; i++) { + small_char_arr_ptr[i]=&draw_table[0].pix[0][0]; + big_char_arr_ptr[i]=&big_table[0].pix[0][0]; + } + + /* build [big_]char_arr_ptr table to point to each available ascii */ + for (i=0; i < sizeof(draw_table) / sizeof(struct draw_char); i++) { + small_char_arr_ptr[(int)draw_table[i].ascii]=&draw_table[i].pix[0][0]; + big_char_arr_ptr[(int)draw_table[i].ascii]=&big_table[i].pix[0][0]; + } + return 0; +} + diff --git a/event.c b/event.c new file mode 100644 index 0000000..f3ccad9 --- /dev/null +++ b/event.c @@ -0,0 +1,647 @@ +/* + event.c + + Generalised event handling for motion + + Copyright Jeroen Vreeken, 2002 + This software is distributed under the GNU Public License Version 2 + see also the file 'COPYING'. + +*/ + +#include "ffmpeg.h" /* must be first to avoid 'shadow' warning */ +#include "motion.h" +#include "event.h" + +#ifdef __freebsd__ +#include "video_freebsd.h" +#else +#include "video.h" +#endif /* __freebsd__ */ + +#include "picture.h" + +/* + * Various functions (most doing the actual action) + */ + +/* Execute 'command' with 'arg' as its argument. + * if !arg command is started with no arguments + * Before we call execl we need to close all the file handles + * that the fork inherited from the parent in order not to pass + * the open handles on to the shell + */ +static void exec_command(struct context *cnt, char *command, char *filename, int filetype) +{ + char stamp[PATH_MAX]; + mystrftime(cnt, stamp, sizeof(stamp), command, cnt->currenttime_tm, filename, filetype); + + if (!fork()) { + int i; + + /* Detach from parent */ + setsid(); + + /* + * Close any file descripter except console because we will + * like to see error messages + */ + for (i = getdtablesize(); i > 2; --i) + close(i); + + execl("/bin/sh", "sh", "-c", stamp, " &", NULL); + + /* if above function succeeds the program never reach here */ + motion_log(LOG_ERR, 1, "Unable to start external command '%s'", stamp); + + exit(1); + } + else if (cnt->conf.setup_mode) + motion_log(-1, 0, "Executing external command '%s'", stamp); +} + +/* + * Event handlers + */ + +static void event_newfile(struct context *cnt ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, + char *filename, void *ftype, struct tm *tm ATTRIBUTE_UNUSED) +{ + motion_log(-1, 0, "File of type %ld saved to: %s", (unsigned long)ftype, filename); +} + + +static void event_beep(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *dummy ATTRIBUTE_UNUSED, + char *filename ATTRIBUTE_UNUSED, + void *ftype ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (!cnt->conf.quiet) + printf("\a"); +} + +/* on_picture_save_command handles both on_picture_save and on_movie_start + * If arg = FTYPE_IMAGE_ANY on_picture_save script is executed + * If arg = FTYPE_MPEG_ANY on_movie_start script is executed + * The scripts are executed with the filename of picture or movie appended + * to the config parameter. + */ +static void on_picture_save_command(struct context *cnt, + int type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, + char *filename, void *arg, struct tm *tm ATTRIBUTE_UNUSED) +{ + int filetype = (unsigned long)arg; + + if ((filetype & FTYPE_IMAGE_ANY) != 0 && cnt->conf.on_picture_save) + exec_command(cnt, cnt->conf.on_picture_save, filename, filetype); + + if ((filetype & FTYPE_MPEG_ANY) != 0 && cnt->conf.on_movie_start) + exec_command(cnt, cnt->conf.on_movie_start, filename, filetype); +} + +static void on_motion_detected_command(struct context *cnt, + int type ATTRIBUTE_UNUSED, unsigned char *dummy1 ATTRIBUTE_UNUSED, + char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->conf.on_motion_detected) + exec_command(cnt, cnt->conf.on_motion_detected, NULL, 0); +} + +#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) + +static void event_sqlnewfile(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *dummy ATTRIBUTE_UNUSED, + char *filename, void *arg, struct tm *tm ATTRIBUTE_UNUSED) +{ + int sqltype = (unsigned long)arg; + + /* Only log the file types we want */ + if (!(cnt->conf.mysql_db || cnt->conf.pgsql_db) || (sqltype & cnt->sql_mask) == 0) + return; + + /* We place the code in a block so we only spend time making space in memory + * for the sqlquery and timestr when we actually need it. + */ + { + char sqlquery[PATH_MAX]; + char timestr[20]; + char eventtimestr[20]; + + strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", cnt->currenttime_tm); + strftime(eventtimestr, sizeof(eventtimestr), "%Y-%m-%d %T", cnt->eventtime_tm); + mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query, cnt->currenttime_tm, filename, sqltype); + + +#ifdef HAVE_MYSQL + if (cnt->conf.mysql_db) { + if (mysql_query(cnt->database, sqlquery) != 0) + motion_log(LOG_ERR, 1, "Mysql query failed"); + } +#endif /* HAVE_MYSQL */ + +#ifdef HAVE_PGSQL + if (cnt->conf.pgsql_db) { + PGresult *res; + + res = PQexec(cnt->database_pg, sqlquery); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + motion_log(LOG_ERR, 1, "PGSQL query failed"); + PQclear(res); + } + } +#endif /* HAVE_PGSQL */ + + } +} + +#endif /* defined HAVE_MYSQL || defined HAVE_PGSQL */ + + +static void on_event_start_command(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *dummy1 ATTRIBUTE_UNUSED, + char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->conf.on_event_start) + exec_command(cnt, cnt->conf.on_event_start, NULL, 0); +} + +static void on_event_end_command(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *dummy1 ATTRIBUTE_UNUSED, + char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->conf.on_event_end) + exec_command(cnt, cnt->conf.on_event_end, NULL, 0); +} + +static void event_stop_webcam(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *dummy1 ATTRIBUTE_UNUSED, + char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->conf.webcam_port){ + webcam_stop(cnt); + } +} + +static void event_webcam_put(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, + void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->conf.webcam_port) + webcam_put(cnt, img); +} + +#ifndef WITHOUT_V4L +#ifndef __freebsd__ +static void event_vid_putpipe(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *img, char *dummy ATTRIBUTE_UNUSED, void *devpipe, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (*(int *)devpipe >= 0) { + if (vid_putpipe(*(int *)devpipe, img, cnt->imgs.size) == -1) + motion_log(LOG_ERR, 1, "Failed to put image into video pipe"); + } +} +#endif /* __freebsd__ */ +#endif /* WITHOUT_V4L */ + + +const char *imageext(struct context *cnt) +{ + if (cnt->conf.ppm) + return "ppm"; + return "jpg"; +} + +static void event_image_detect(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *newimg, char *dummy1 ATTRIBUTE_UNUSED, + void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) +{ + struct config *conf=&cnt->conf; + char fullfilename[PATH_MAX]; + char filename[PATH_MAX]; + char fullfilenamem[PATH_MAX]; + char filenamem[PATH_MAX]; + + if (conf->motion_img || cnt->new_img==NEWIMG_ON || cnt->preview_shot) { + const char *jpegpath; + + /* conf.jpegpath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.jpegpath) + jpegpath = cnt->conf.jpegpath; + else + jpegpath = DEF_JPEGPATH; + + mystrftime(cnt, filename, sizeof(filename), jpegpath, currenttime_tm, NULL, 0); + /* motion images gets same name as normal images plus an appended 'm' */ + sprintf(filenamem, "%sm", filename); + sprintf(fullfilename, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); + sprintf(fullfilenamem, "%s/%s.%s", cnt->conf.filepath, filenamem, imageext(cnt)); + } + if (conf->motion_img) { + put_picture(cnt, fullfilenamem, cnt->imgs.out, FTYPE_IMAGE_MOTION); + } + if (cnt->new_img==NEWIMG_ON || cnt->preview_shot) { + put_picture(cnt, fullfilename, newimg, FTYPE_IMAGE); + } +} + +static void event_image_snapshot(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, + void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) +{ + char fullfilename[PATH_MAX]; + + if ( strcmp(cnt->conf.snappath, "lastsnap") ) { + char filename[PATH_MAX]; + char filepath[PATH_MAX]; + char linkpath[PATH_MAX]; + const char *snappath; + /* conf.snappath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.snappath) + snappath = cnt->conf.snappath; + else + snappath = DEF_SNAPPATH; + + mystrftime(cnt, filepath, sizeof(filepath), snappath, currenttime_tm, NULL, 0); + sprintf(filename, "%s.%s", filepath, imageext(cnt)); + sprintf(fullfilename, "%s/%s", cnt->conf.filepath, filename); + put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); + + /* Update symbolic link *after* image has been written so that + the link always points to a valid file. */ + sprintf(linkpath, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); + remove(linkpath); + if (symlink(filename, linkpath)) { + motion_log(LOG_ERR, 1, "Could not create symbolic link [%s]", filename); + return; + } + } else { + sprintf(fullfilename, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); + remove(fullfilename); + put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); + } + + cnt->snapshot = 0; +} + +#ifdef HAVE_FFMPEG +static void grey2yuv420p(unsigned char *u, unsigned char *v, int width, int height) +{ + memset(u, 128, width*height/4); + memset(v, 128, width*height/4); +} + +static void on_movie_end_command(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *dummy ATTRIBUTE_UNUSED, char *filename, + void *arg, struct tm *tm ATTRIBUTE_UNUSED) +{ + int filetype = (unsigned long) arg; + + if ((filetype & FTYPE_MPEG_ANY) && cnt->conf.on_movie_end) + exec_command(cnt, cnt->conf.on_movie_end, filename, filetype); +} + +static void event_ffmpeg_newfile(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, + void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) +{ + int width=cnt->imgs.width; + int height=cnt->imgs.height; + unsigned char *convbuf, *y, *u, *v; + int fps; + char stamp[PATH_MAX]; + const char *mpegpath; + + if (!cnt->conf.ffmpeg_cap_new && !cnt->conf.ffmpeg_cap_motion) + return; + + /* conf.mpegpath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.mpegpath) + mpegpath = cnt->conf.mpegpath; + else + mpegpath = DEF_MPEGPATH; + + mystrftime(cnt, stamp, sizeof(stamp), mpegpath, currenttime_tm, NULL, 0); + + /* motion mpegs get the same name as normal mpegs plus an appended 'm' */ + sprintf(cnt->motionfilename, "%s/%sm", cnt->conf.filepath, stamp); + sprintf(cnt->newfilename, "%s/%s", cnt->conf.filepath, stamp); + + if (cnt->conf.ffmpeg_cap_new) { + if (cnt->imgs.type==VIDEO_PALETTE_GREY) { + convbuf=mymalloc((width*height)/2); + y=img; + u=convbuf; + v=convbuf+(width*height)/4; + grey2yuv420p(u, v, width, height); + } else { + convbuf=NULL; + y=img; + u=img+width*height; + v=u+(width*height)/4; + } + if (cnt->conf.low_cpu) + fps=cnt->conf.frame_limit; + else + fps=cnt->lastrate; + if (fps>30) + fps=30; + if (fps<2) + fps=2; + if ( (cnt->ffmpeg_new = + ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->newfilename, y, u, v, + cnt->imgs.width, cnt->imgs.height, fps, cnt->conf.ffmpeg_bps, + cnt->conf.ffmpeg_vbr)) == NULL) { + motion_log(LOG_ERR, 1, "ffopen_open error creating file [%s]",cnt->newfilename); + cnt->finish=1; + return; + } + ((struct ffmpeg *)cnt->ffmpeg_new)->udata=convbuf; + event(cnt, EVENT_FILECREATE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); + } + if (cnt->conf.ffmpeg_cap_motion) { + if (cnt->imgs.type==VIDEO_PALETTE_GREY) { + convbuf=mymalloc((width*height)/2); + y=cnt->imgs.out; + u=convbuf; + v=convbuf+(width*height)/4; + grey2yuv420p(u, v, width, height); + } else { + y=cnt->imgs.out; + u=cnt->imgs.out+width*height; + v=u+(width*height)/4; + convbuf=NULL; + } + if (cnt->conf.low_cpu) + fps=cnt->conf.frame_limit; + else + fps=cnt->lastrate; + if (fps>30) + fps=30; + if (fps<2) + fps=2; + if ( (cnt->ffmpeg_motion = + ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->motionfilename, y, u, v, + cnt->imgs.width, cnt->imgs.height, fps, cnt->conf.ffmpeg_bps, + cnt->conf.ffmpeg_vbr)) == NULL){ + motion_log(LOG_ERR, 1, "ffopen_open error creating file [%s]", cnt->motionfilename); + cnt->finish=1; + return; + } + cnt->ffmpeg_motion->udata=convbuf; + event(cnt, EVENT_FILECREATE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); + } +} + +static void event_ffmpeg_timelapse(struct context *cnt, + int type ATTRIBUTE_UNUSED, unsigned char *img, + char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, + struct tm *currenttime_tm) +{ + int width = cnt->imgs.width; + int height = cnt->imgs.height; + unsigned char *convbuf, *y, *u, *v; + + if (!cnt->ffmpeg_timelapse) { + char tmp[PATH_MAX]; + const char *timepath; + + /* conf.timepath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.timepath) + timepath = cnt->conf.timepath; + else + timepath = DEF_TIMEPATH; + + mystrftime(cnt, tmp, sizeof(tmp), timepath, currenttime_tm, NULL, 0); + sprintf(cnt->timelapsefilename, "%s/%s", cnt->conf.filepath, tmp); + + if (cnt->imgs.type == VIDEO_PALETTE_GREY) { + convbuf = mymalloc((width*height)/2); + y = img; + u = convbuf; + v = convbuf+(width*height)/4; + grey2yuv420p(u, v, width, height); + } else { + convbuf = NULL; + y = img; + u = img+width*height; + v = u+(width*height)/4; + } + + if ( (cnt->ffmpeg_timelapse = + ffmpeg_open((char *)TIMELAPSE_CODEC, cnt->timelapsefilename, y, u, v, + cnt->imgs.width, cnt->imgs.height, 24, cnt->conf.ffmpeg_bps, + cnt->conf.ffmpeg_vbr)) == NULL) { + motion_log(LOG_ERR, 1, "ffopen_open error creating file [%s]", cnt->timelapsefilename); + cnt->finish=1; + return; + } + + cnt->ffmpeg_timelapse->udata = convbuf; + event(cnt, EVENT_FILECREATE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL); + } + + y = img; + + if (cnt->imgs.type == VIDEO_PALETTE_GREY) + u = cnt->ffmpeg_timelapse->udata; + else + u = img+width*height; + + v = u+(width*height)/4; + ffmpeg_put_other_image(cnt->ffmpeg_timelapse, y, u, v); + +} + +static void event_ffmpeg_put(struct context *cnt, int type ATTRIBUTE_UNUSED, + unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, + void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->ffmpeg_new) + { + int width=cnt->imgs.width; + int height=cnt->imgs.height; + unsigned char *y = img; + unsigned char *u, *v; + + if (cnt->imgs.type == VIDEO_PALETTE_GREY) + u = cnt->ffmpeg_timelapse->udata; + else + u = y + (width * height); + + v = u + (width * height) / 4; + ffmpeg_put_other_image(cnt->ffmpeg_new, y, u, v); + } + + if (cnt->ffmpeg_motion) { + ffmpeg_put_image(cnt->ffmpeg_motion); + } +} + +static void event_ffmpeg_closefile(struct context *cnt, + int type ATTRIBUTE_UNUSED, unsigned char *dummy1 ATTRIBUTE_UNUSED, + char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + + if (cnt->ffmpeg_new) { + if (cnt->ffmpeg_new->udata) + free(cnt->ffmpeg_new->udata); + ffmpeg_close(cnt->ffmpeg_new); + cnt->ffmpeg_new=NULL; + + event(cnt, EVENT_FILECLOSE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); + } + if (cnt->ffmpeg_motion) { + if (cnt->ffmpeg_motion->udata) + free(cnt->ffmpeg_motion->udata); + ffmpeg_close(cnt->ffmpeg_motion); + cnt->ffmpeg_motion=NULL; + + event(cnt, EVENT_FILECLOSE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); + } +} + +static void event_ffmpeg_timelapseend(struct context *cnt, + int type ATTRIBUTE_UNUSED, unsigned char *dummy1 ATTRIBUTE_UNUSED, + char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, + struct tm *tm ATTRIBUTE_UNUSED) +{ + if (cnt->ffmpeg_timelapse) { + if (cnt->ffmpeg_timelapse->udata) + free(cnt->ffmpeg_timelapse->udata); + ffmpeg_close(cnt->ffmpeg_timelapse); + cnt->ffmpeg_timelapse=NULL; + + event(cnt, EVENT_FILECLOSE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL); + } +} + +#endif /* HAVE_FFMPEG */ + + +/* + * Starting point for all events + */ + +struct event_handlers { + int type; + event_handler handler; +}; + +struct event_handlers event_handlers[] = { +#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) + { + EVENT_FILECREATE, + event_sqlnewfile + }, +#endif + { + EVENT_FILECREATE, + on_picture_save_command + }, + { + EVENT_FILECREATE, + event_newfile + }, + + { + EVENT_MOTION, + event_beep + }, + { + EVENT_MOTION, + on_motion_detected_command + }, + { + EVENT_FIRSTMOTION, + on_event_start_command + }, + { + EVENT_ENDMOTION, + on_event_end_command + }, + { + EVENT_IMAGE_DETECTED, + event_image_detect + }, + { + EVENT_IMAGE_SNAPSHOT, + event_image_snapshot + }, +#ifndef WITHOUT_V4L +#ifndef __freebsd__ + { + EVENT_IMAGE | EVENT_IMAGEM, + event_vid_putpipe + }, +#endif /* __freebsd__ */ +#endif /* WITHOUT_V4L */ + { + EVENT_WEBCAM, + event_webcam_put + }, +#ifdef HAVE_FFMPEG + { + EVENT_FIRSTMOTION, + event_ffmpeg_newfile + }, + { + EVENT_IMAGE_DETECTED, + event_ffmpeg_put + }, + { + EVENT_ENDMOTION, + event_ffmpeg_closefile + }, + { + EVENT_TIMELAPSE, + event_ffmpeg_timelapse + }, + { + EVENT_TIMELAPSEEND, + event_ffmpeg_timelapseend + }, + { + EVENT_FILECLOSE, + on_movie_end_command + }, +#endif /* HAVE_FFMPEG */ + { + EVENT_STOP, + event_stop_webcam + }, + {0, NULL} +}; + + +/* The event functions are defined with the following parameters: + * - Type as defined in event.h (EVENT_...) + * - The global context struct cnt + * - image - A pointer to unsigned char as used for images + * - filename - A pointer to typically a string for a file path + * - eventdata - A void pointer that can be cast to anything. E.g. FTYPE_... + * - tm - A tm struct that carries a full time structure + * The split between unsigned images and signed filenames was introduced in 3.2.2 + * as a code reading friendly solution to avoid a stream of compiler warnings in gcc 4.0. + */ +void event(struct context *cnt, int type, unsigned char *image, char *filename, void *eventdata, struct tm *tm) +{ + int i=-1; + + while (event_handlers[++i].handler) { + if (type & event_handlers[i].type) + event_handlers[i].handler(cnt, type, image, filename, eventdata, tm); + } +} diff --git a/event.h b/event.h new file mode 100644 index 0000000..4d788fe --- /dev/null +++ b/event.h @@ -0,0 +1,37 @@ +/* + * event.h + * + * Include file for event.c + * + * Copyright Jeroen Vreeken, 2002 + * This software is distributed under the GNU Public License Version 2 + * see also the file 'COPYING'. + * + */ +#ifndef _INCLUDE_EVENT_H_ +#define _INCLUDE_EVENT_H_ + +#define EVENT_FILECREATE 1 +#define EVENT_MOTION 2 +#define EVENT_FIRSTMOTION 4 +#define EVENT_ENDMOTION 8 +#define EVENT_STOP 16 +#define EVENT_TIMELAPSE 32 +#define EVENT_TIMELAPSEEND 64 +#define EVENT_WEBCAM 128 +#define EVENT_IMAGE_DETECTED 256 +#define EVENT_IMAGEM_DETECTED 512 +#define EVENT_IMAGE_SNAPSHOT 1024 +#define EVENT_IMAGE 2048 +#define EVENT_IMAGEM 8192 +#define EVENT_FILECLOSE 16384 +#define EVENT_DEBUG 65536 +#define EVENT_CRITICAL 131072 +# + +typedef void(* event_handler)(struct context *, int, unsigned char *, char *, void *, struct tm *); + +void event(struct context *, int, unsigned char *, char *, void *, struct tm *); +const char * imageext(struct context *); + +#endif /* _INCLUDE_EVENT_H_ */ diff --git a/ffmpeg-0.4.8-macosx.patch b/ffmpeg-0.4.8-macosx.patch new file mode 100644 index 0000000..875ae43 --- /dev/null +++ b/ffmpeg-0.4.8-macosx.patch @@ -0,0 +1,60 @@ +diff -Naupr ffmpeg-0.4.8/configure ffmpeg-0.4.8-macosx/configure +--- ffmpeg-0.4.8/configure 2003-09-28 08:26:39.000000000 -0700 ++++ ffmpeg-0.4.8-macosx/configure 2005-10-06 15:49:33.000000000 -0700 +@@ -84,7 +84,7 @@ debug="yes" + extralibs="-lm" + simpleidct="yes" + bigendian="no" +-vhook="default" ++vhook="no" + dlfcn="no" + dlopen="no" + mpegaudio_hp="yes" +@@ -163,6 +163,7 @@ extralibs="-lpoll -lgnugetopt -lm" + make="gmake" + ;; + Darwin) ++SLIBSUF=".dylib" + cc="cc" + v4l="no" + audio_oss="no" +@@ -177,13 +178,13 @@ FFSLDFLAGS=-Wl,-bind_at_load + gcc_version="$($cc -v 2>&1 | grep version | cut -d ' ' -f3-)" + case "$gcc_version" in + *2.95*) +-CFLAGS="-no-cpp-precomp -pipe -fomit-frame-pointer" ++CFLAGS="$CFLAGS -no-cpp-precomp -pipe -fomit-frame-pointer" + ;; + *3.3*) +-CFLAGS="-no-cpp-precomp -pipe -fomit-frame-pointer -mdynamic-no-pic -force_cpusubtype_ALL" ++CFLAGS="$CFLAGS -no-cpp-precomp -pipe -fomit-frame-pointer -mdynamic-no-pic -force_cpusubtype_ALL" + ;; + *) +-CFLAGS="-no-cpp-precomp -pipe -fomit-frame-pointer -mdynamic-no-pic" ++CFLAGS="$CFLAGS -no-cpp-precomp -pipe -fomit-frame-pointer -mdynamic-no-pic" + ;; + esac + ;; +diff -Naupr ffmpeg-0.4.8/libavcodec/Makefile ffmpeg-0.4.8-macosx/libavcodec/Makefile +--- ffmpeg-0.4.8/libavcodec/Makefile 2003-09-28 08:26:39.000000000 -0700 ++++ ffmpeg-0.4.8-macosx/libavcodec/Makefile 2005-10-06 17:22:21.000000000 -0700 +@@ -239,6 +239,8 @@ install: + endif + + installlib: all install-headers ++ ranlib $(LIB) ++ install -d $(prefix)/lib + install -m 644 $(LIB) $(prefix)/lib + + install-headers: +diff -Naupr ffmpeg-0.4.8/libavformat/Makefile ffmpeg-0.4.8-macosx/libavformat/Makefile +--- ffmpeg-0.4.8/libavformat/Makefile 2003-09-28 08:26:40.000000000 -0700 ++++ ffmpeg-0.4.8-macosx/libavformat/Makefile 2005-10-06 15:42:53.000000000 -0700 +@@ -102,6 +102,7 @@ install: + endif + + installlib: all install-headers ++ ranlib $(LIB) + install -m 644 $(LIB) $(prefix)/lib + + install-headers: diff --git a/ffmpeg.c b/ffmpeg.c new file mode 100644 index 0000000..e6dd0cd --- /dev/null +++ b/ffmpeg.c @@ -0,0 +1,578 @@ +/********************************************************************** + * + * ffmpeg.c + * + * This software is distributed under the GNU Public License version 2 + * See also the file 'COPYING'. + * + * The contents of this file has been derived from output_example.c + * and apiexample.c from the FFmpeg distribution. + * + **********************************************************************/ + +#ifdef HAVE_FFMPEG + +#include "ffmpeg.h" +#include "motion.h" + + + +#if LIBAVCODEC_BUILD > 4680 +/* FFmpeg after build 4680 doesn't have support for mpeg1 videos with + * non-standard framerates. Previous builds contained a broken hack + * that padded with B frames to obtain the correct framerate. + */ +# define FFMPEG_NO_NONSTD_MPEG1 +# ifdef __GNUC__ +/* #warning is a non-standard gcc extension */ +# warning ************************************************** +# warning Your version of FFmpeg is newer than version 0.4.8 +# warning Newer versions of ffmpeg do not support MPEG1 with +# warning non-standard framerate. MPEG1 will be disabled for +# warning normal video output. You can still use mpeg4 and +# warning and mpeg4ms which are both better in terms of size +# warning and quality. MPEG1 is always used for timelapse. +# warning Please read the Motion Guide for more information. +# warning Note that this is not an error message! +# warning ************************************************** +# endif /* __GNUC__ */ +#endif /* LIBAVCODEC_BUILD > 4680 */ + +#if FFMPEG_VERSION_INT >= 0x000409 +/* The API for av_write_frame changed with FFmpeg version 0.4.9pre1. + * It now uses an AVPacket struct instead of direct parameters to the + * function. + */ +# define FFMPEG_AVWRITEFRAME_NEWAPI +#endif /* FFMPEG_VERSION_INT >= 0x000409 */ + +#if LIBAVFORMAT_BUILD >= 4629 +/* In this build/header version, the codec member of struct AVStream + * was changed to a pointer so changes to AVCodecContext shouldn't + * break binary compatibility with AVStream. + */ +# define AVSTREAM_CODEC_PTR(avs_ptr) (avs_ptr->codec) +#else +# define AVSTREAM_CODEC_PTR(avs_ptr) (&avs_ptr->codec) +#endif /* LIBAVFORMAT_BUILD >= 4629 */ + +/* Name of custom file protocol for appending to existing files instead + * of truncating. + */ +#define APPEND_PROTO "appfile" + +/* Some forward-declarations. */ +void ffmpeg_put_frame(struct ffmpeg *, AVFrame *); +void ffmpeg_cleanups(struct ffmpeg *); +AVFrame *ffmpeg_prepare_frame(struct ffmpeg *, unsigned char *, + unsigned char *, unsigned char *); + +/* This is the trailer used to end mpeg1 videos. */ +static unsigned char mpeg1_trailer[] = {0x00, 0x00, 0x01, 0xb7}; + +/* Append version of the file open function used in libavformat when opening + * an ordinary file. The original file open function truncates an existing + * file, but this version appends to it instead. + */ +static int file_open_append(URLContext *h, const char *filename, int flags) +{ + const char *colon; + int access_flags, fd; + + /* Skip past the protocol part of filename. */ + colon = strchr(filename, ':'); + if (colon) { + filename = colon + 1; + } + + if (flags & URL_RDWR) { + access_flags = O_CREAT | O_APPEND | O_RDWR; + } else if (flags & URL_WRONLY) { + access_flags = O_CREAT | O_APPEND | O_WRONLY; + } else { + access_flags = O_RDONLY; + } + + fd = open(filename, access_flags, 0666); + if (fd < 0) { + return -ENOENT; + } + h->priv_data = (void *)(size_t)fd; + return 0; +} + +/* URLProtocol entry for the append file protocol, which we use for mpeg1 videos + * in order to get append behavior with url_fopen. + * + * Libavformat uses protocols for achieving flexibility when handling files + * and other resources. A call to url_fopen will eventually be redirected to + * a protocol-specific open function. + * + * The remaining functions (for writing, seeking etc.) are set in ffmpeg_init. + */ +URLProtocol mpeg1_file_protocol = { + .name = APPEND_PROTO, + .url_open = file_open_append +}; + +/* We set AVOutputFormat->write_trailer to this function for mpeg1. That way, + * the mpeg1 video gets a proper trailer when it is closed. + */ +static int mpeg1_write_trailer(AVFormatContext *s) +{ + put_buffer(&s->pb, mpeg1_trailer, 4); + put_flush_packet(&s->pb); + return 0; /* success */ +} + +/* ffmpeg_init initializes for libavformat. */ +void ffmpeg_init() +{ + av_register_all(); + + /* Copy the functions to use for the append file protocol from the standard + * file protocol. + */ + mpeg1_file_protocol.url_read = file_protocol.url_read; + mpeg1_file_protocol.url_write = file_protocol.url_write; + mpeg1_file_protocol.url_seek = file_protocol.url_seek; + mpeg1_file_protocol.url_close = file_protocol.url_close; + + /* Register the append file protocol. */ + register_protocol(&mpeg1_file_protocol); +} + +/* Obtains the output format used for the specified codec. For mpeg4 codecs, + * the format is avi; for mpeg1 codec, the format is mpeg. The filename has + * to be passed, because it gets the appropriate extension appended onto it. + */ +static AVOutputFormat *get_oformat(const char *codec, char *filename) +{ + const char *ext; + AVOutputFormat *of = NULL; + + /* Here, we use guess_format to automatically setup the codec information. + * If we are using msmpeg4, manually set that codec here. + * We also dynamically add the file extension to the filename here. This was + * done to support both mpeg1 and mpeg4 codecs since they have different extensions. + */ + if ((strcmp(codec, TIMELAPSE_CODEC) == 0) +#ifndef FFMPEG_NO_NONSTD_MPEG1 + || (strcmp(codec, "mpeg1") == 0) +#endif + ) { + ext = "mpg"; + /* We use "mpeg1video" for raw mpeg1 format. Using "mpeg" would + * result in a muxed output file, which isn't appropriate here. + */ + of = guess_format("mpeg1video", NULL, NULL); + if (of) { + /* But we want the trailer to be correctly written. */ + of->write_trailer = mpeg1_write_trailer; + } +#ifdef FFMPEG_NO_NONSTD_MPEG1 + } else if (strcmp(codec, "mpeg1") == 0) { + motion_log(LOG_ERR, 0, "*** mpeg1 support for normal videos has been disabled ***"); + return NULL; +#endif + } else if (strcmp(codec, "mpeg4") == 0) { + ext = "avi"; + of = guess_format("avi", NULL, NULL); + } else if (strcmp(codec, "msmpeg4") == 0) { + ext = "avi"; + of = guess_format("avi", NULL, NULL); + if (of) { + /* Manually override the codec id. */ + of->video_codec = CODEC_ID_MSMPEG4V2; + } + } else { + motion_log(LOG_ERR, 0, "ffmpeg_video_codec option value %s is not supported", codec); + return NULL; + } + + if (!of) { + motion_log(LOG_ERR, 0, "Could not guess format for %s", codec); + return NULL; + } + + /* WARNING: potential buffer overflow */ + sprintf(filename, "%s.%s", filename, ext); + return of; +} + +/* This function opens an mpeg file using the new libavformat method. Both mpeg1 + * and mpeg4 are supported. However, if the current ffmpeg version doesn't allow + * mpeg1 with non-standard framerate, the open will fail. Timelapse is a special + * case and is tested separately. + */ +struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, + unsigned char *y, unsigned char *u, unsigned char *v, + int width, int height, int rate, int bps, int vbr) +{ + AVCodecContext *c; + AVCodec *codec; + struct ffmpeg *ffmpeg; + int is_mpeg1; + + /* Allocate space for our ffmpeg structure. This structure contains all the + * codec and image information we need to generate movies. + * FIXME when motion exits we should close the movie to ensure that + * ffmpeg is freed. + */ + ffmpeg = mymalloc(sizeof(struct ffmpeg)); + memset(ffmpeg, 0, sizeof(struct ffmpeg)); + + ffmpeg->vbr = vbr; + + /* store codec name in ffmpeg->codec, with buffer overflow check */ + snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); + + /* allocation the output media context */ + ffmpeg->oc = av_mallocz(sizeof(AVFormatContext)); + if (!ffmpeg->oc) { + motion_log(LOG_ERR, 1, "Memory error while allocating output media context"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Setup output format */ + ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); + if (!ffmpeg->oc->oformat) { + ffmpeg_cleanups(ffmpeg); + return NULL; + } + + snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); + + /* Create a new video stream and initialize the codecs */ + ffmpeg->video_st = NULL; + if (ffmpeg->oc->oformat->video_codec != CODEC_ID_NONE) { + ffmpeg->video_st = av_new_stream(ffmpeg->oc, 0); + if (!ffmpeg->video_st) { + motion_log(LOG_ERR, 1, "av_new_stream - could not alloc stream"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + } else { + /* We did not get a proper video codec. */ + motion_log(LOG_ERR, 0, "Failed to obtain a proper video codec"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); + c->codec_id = ffmpeg->oc->oformat->video_codec; + c->codec_type = CODEC_TYPE_VIDEO; + is_mpeg1 = c->codec_id == CODEC_ID_MPEG1VIDEO; + + /* Uncomment to allow non-standard framerates. */ + //c->strict_std_compliance = -1; + + /* Set default parameters */ + c->bit_rate = bps; + c->width = width; + c->height = height; +#if LIBAVCODEC_BUILD >= 4754 + /* frame rate = 1/time_base, so we set 1/rate, not rate/1 */ + c->time_base.num = 1; + c->time_base.den = rate; +#else + c->frame_rate = rate; + c->frame_rate_base = 1; +#endif /* LIBAVCODEC_BUILD >= 4754 */ + + if (vbr) + c->flags |= CODEC_FLAG_QSCALE; + + /* Set codec specific parameters. */ + /* set intra frame distance in frames depending on codec */ + c->gop_size = is_mpeg1 ? 10 : 12; + + /* some formats want stream headers to be seperate */ + if(!strcmp(ffmpeg->oc->oformat->name, "mp4") || + !strcmp(ffmpeg->oc->oformat->name, "mov") || + !strcmp(ffmpeg->oc->oformat->name, "3gp")) { + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + + /* set the output parameters (must be done even if no parameters). */ + if (av_set_parameters(ffmpeg->oc, NULL) < 0) { + motion_log(LOG_ERR, 0, "ffmpeg av_set_parameters error: Invalid output format parameters"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Dump the format settings. This shows how the various streams relate to each other */ + //dump_format(ffmpeg->oc, 0, filename, 1); + + /* Now that all the parameters are set, we can open the video + codec and allocate the necessary encode buffers */ + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + motion_log(LOG_ERR, 1, "Codec not found"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Set the picture format - need in ffmpeg starting round April-May 2005 */ + c->pix_fmt = PIX_FMT_YUV420P; + + /* open the codec */ + if (avcodec_open(c, codec) < 0) { + motion_log(LOG_ERR, 1, "avcodec_open - could not open codec"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + ffmpeg->video_outbuf = NULL; + if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { + /* allocate output buffer */ + /* XXX: API change will be done */ + ffmpeg->video_outbuf_size = 200000; + ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); + } + + /* allocate the encoded raw picture */ + ffmpeg->picture = avcodec_alloc_frame(); + if (!ffmpeg->picture) { + motion_log(LOG_ERR, 1, "avcodec_alloc_frame - could not alloc frame"); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* set variable bitrate if requested */ + if (ffmpeg->vbr) { + ffmpeg->picture->quality = ffmpeg->vbr; + } + + /* set the frame data */ + ffmpeg->picture->data[0] = y; + ffmpeg->picture->data[1] = u; + ffmpeg->picture->data[2] = v; + ffmpeg->picture->linesize[0] = ffmpeg->c->width; + ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; + ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; + + /* open the output file, if needed */ + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + char file_proto[256]; + + /* Use append file protocol for mpeg1, to get the append behavior from + * url_fopen, but no protocol (=> default) for other codecs. + */ + if(is_mpeg1) { + snprintf(file_proto, sizeof(file_proto), APPEND_PROTO ":%s", filename); + } else { + snprintf(file_proto, sizeof(file_proto), "%s", filename); + } + + if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { + /* path did not exist? */ + if (errno == ENOENT) { + /* create path for file (don't use file_proto)... */ + if (create_path(filename) == -1) { + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* and retry opening the file (use file_proto) */ + if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { + motion_log(LOG_ERR, 1, "url_fopen - error opening file %s",filename); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + /* Permission denied */ + } else if (errno == EACCES) { + motion_log(LOG_ERR, 1, + "url_fopen - error opening file %s" + " ... check access rights to target directory", filename); + /* create path for file... */ + ffmpeg_cleanups(ffmpeg); + return (NULL); + } else { + motion_log(LOG_ERR, 1, "Error opening file %s", filename); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + } + } + + /* write the stream header, if any */ + av_write_header(ffmpeg->oc); + + return ffmpeg; +} + +/* + Clean up ffmpeg struct if something was wrong +*/ +void ffmpeg_cleanups(struct ffmpeg *ffmpeg) +{ + int i; + + /* close each codec */ + if (ffmpeg->video_st) { + avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); + av_freep(&ffmpeg->picture); + av_freep(&ffmpeg->video_outbuf); + } + + /* free the streams */ + for (i = 0; i < ffmpeg->oc->nb_streams; i++) { + av_freep(&ffmpeg->oc->streams[i]); + } +/* + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + // close the output file + if (ffmpeg->oc->pb) url_fclose(&ffmpeg->oc->pb); + } +*/ + /* free the stream */ + av_free(ffmpeg->oc); + free(ffmpeg->c); + free(ffmpeg); +} + +/* Closes a video file. */ +void ffmpeg_close(struct ffmpeg *ffmpeg) +{ + int i; + + /* close each codec */ + if (ffmpeg->video_st) { + avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); + av_freep(&ffmpeg->picture); + av_freep(&ffmpeg->video_outbuf); + } + + /* write the trailer, if any */ + av_write_trailer(ffmpeg->oc); + + /* free the streams */ + for (i = 0; i < ffmpeg->oc->nb_streams; i++) { + av_freep(&ffmpeg->oc->streams[i]); + } + + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + /* close the output file */ + url_fclose(&ffmpeg->oc->pb); + } + + /* free the stream */ + av_free(ffmpeg->oc); + free(ffmpeg->c); + free(ffmpeg); +} + +/* Puts the image pointed to by ffmpeg->picture. */ +void ffmpeg_put_image(struct ffmpeg *ffmpeg) +{ + ffmpeg_put_frame(ffmpeg, ffmpeg->picture); +} + +/* Puts an arbitrary picture defined by y, u and v. */ +void ffmpeg_put_other_image(struct ffmpeg *ffmpeg, unsigned char *y, + unsigned char *u, unsigned char *v) +{ + AVFrame *picture; + /* allocate the encoded raw picture */ + picture = ffmpeg_prepare_frame(ffmpeg, y, u, v); + + if (picture) { + ffmpeg_put_frame(ffmpeg, picture); + free(picture); + } +} + +/* Encodes and writes a video frame using the av_write_frame API. This is + * a helper function for ffmpeg_put_image and ffmpeg_put_other_image. + */ +void ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) +{ + int out_size, ret; +#ifdef FFMPEG_AVWRITEFRAME_NEWAPI + AVPacket pkt; + + av_init_packet(&pkt); /* init static structure */ + pkt.stream_index = ffmpeg->video_st->index; +#endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ + + if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { + /* raw video case. The API will change slightly in the near future for that */ +#ifdef FFMPEG_AVWRITEFRAME_NEWAPI + pkt.flags |= PKT_FLAG_KEY; + pkt.data = (uint8_t *)pic; + pkt.size = sizeof(AVPicture); + ret = av_write_frame(ffmpeg->oc, &pkt); +#else + ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, + (uint8_t *)pic, sizeof(AVPicture)); +#endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ + } else { + /* encode the image */ + out_size = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), + ffmpeg->video_outbuf, + ffmpeg->video_outbuf_size, pic); + + /* if zero size, it means the image was buffered */ + if (out_size != 0) { + /* write the compressed frame in the media file */ + /* XXX: in case of B frames, the pts is not yet valid */ +#ifdef FFMPEG_AVWRITEFRAME_NEWAPI + pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; + if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) { + pkt.flags |= PKT_FLAG_KEY; + } + pkt.data = ffmpeg->video_outbuf; + pkt.size = out_size; + ret = av_write_frame(ffmpeg->oc, &pkt); +#else + ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, + ffmpeg->video_outbuf, out_size); +#endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ + } else { + ret = 0; + } + } + + if (ret != 0) { + motion_log(LOG_ERR, 1, "Error while writing video frame"); + return; + } +} + +/* Allocates and prepares a picture frame by setting up the U, Y and V pointers in + * the frame according to the passed pointers. + * + * Returns NULL If the allocation fails. + * + * The returned AVFrame pointer must be freed after use. + */ +AVFrame *ffmpeg_prepare_frame(struct ffmpeg *ffmpeg, unsigned char *y, + unsigned char *u, unsigned char *v) +{ + AVFrame *picture; + + picture = avcodec_alloc_frame(); + if (!picture) { + motion_log(LOG_ERR, 1, "Could not alloc frame"); + return NULL; + } + + /* take care of variable bitrate setting */ + if (ffmpeg->vbr) { + picture->quality = ffmpeg->vbr; + } + + /* setup pointers and line widths */ + picture->data[0] = y; + picture->data[1] = u; + picture->data[2] = v; + picture->linesize[0] = ffmpeg->c->width; + picture->linesize[1] = ffmpeg->c->width / 2; + picture->linesize[2] = ffmpeg->c->width / 2; + + return picture; +} + +#endif /* HAVE_FFMPEG */ diff --git a/ffmpeg.h b/ffmpeg.h new file mode 100644 index 0000000..0d93f0a --- /dev/null +++ b/ffmpeg.h @@ -0,0 +1,66 @@ +#ifndef _INCLUDE_FFMPEG_H_ +#define _INCLUDE_FFMPEG_H_ + +#ifdef HAVE_FFMPEG +#include +#endif + +/* Define a codec name/identifier for timelapse videos, so that we can + * differentiate between normal mpeg1 videos and timelapse videos. + */ +#define TIMELAPSE_CODEC "mpeg1_tl" + +struct ffmpeg { +#ifdef HAVE_FFMPEG + AVFormatContext *oc; + AVStream *video_st; + AVCodecContext *c; + + AVFrame *picture; /* contains default imaga pointers */ + uint8_t *video_outbuf; + int video_outbuf_size; + + void *udata; /* U & V planes for greyscale images */ + int vbr; /* variable bitrate setting */ + char codec[20]; /* codec name */ +#else + int dummy; +#endif +}; + +/* Initialize FFmpeg stuff. Needs to be called before ffmpeg_open. */ +void ffmpeg_init(void); + +/* Open an mpeg file. This is a generic interface for opening either an mpeg1 or + * an mpeg4 video. If non-standard mpeg1 isn't supported (FFmpeg build > 4680), + * calling this function with "mpeg1" as codec results in an error. To create a + * timelapse video, use TIMELAPSE_CODEC as codec name. + */ +struct ffmpeg *ffmpeg_open( + char *ffmpeg_video_codec, + char *filename, + unsigned char *y, /* YUV420 Y plane */ + unsigned char *u, /* YUV420 U plane */ + unsigned char *v, /* YUV420 V plane */ + int width, + int height, + int rate, /* framerate, fps */ + int bps, /* bitrate; bits per second */ + int vbr /* variable bitrate */ + ); + +/* Puts the image pointed to by the picture member of struct ffmpeg. */ +void ffmpeg_put_image(struct ffmpeg *); + +/* Puts the image defined by u, y and v (YUV420 format). */ +void ffmpeg_put_other_image( + struct ffmpeg *ffmpeg, + unsigned char *y, + unsigned char *u, + unsigned char *v + ); + +/* Closes the mpeg file. */ +void ffmpeg_close(struct ffmpeg *); + +#endif /* _INCLUDE_FFMPEG_H_ */ diff --git a/mmx.h b/mmx.h new file mode 100644 index 0000000..18a98b0 --- /dev/null +++ b/mmx.h @@ -0,0 +1,267 @@ +/* + * mmx.h + * Copyright (C) 1997-2001 H. Dietz and R. Fisher + */ +#ifndef I386MMX_H +#define I386MMX_H + +/* + * The type of an value that fits in an MMX register (note that long + * long constant values MUST be suffixed by LL and unsigned long long + * values by ULL, lest they be truncated by the compiler) + */ + +typedef union { + long long q; /* Quadword (64-bit) value */ + unsigned long long uq; /* Unsigned Quadword */ + int d[2]; /* 2 Doubleword (32-bit) values */ + unsigned int ud[2]; /* 2 Unsigned Doubleword */ + short w[4]; /* 4 Word (16-bit) values */ + unsigned short uw[4]; /* 4 Unsigned Word */ + char b[8]; /* 8 Byte (8-bit) values */ + unsigned char ub[8]; /* 8 Unsigned Byte */ + float s[2]; /* Single-precision (32-bit) value */ +} mmx_t; /* On an 8-byte (64-bit) boundary */ + + +#define mmx_i2r(op,imm,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "i" (imm) ) + +#define mmx_m2r(op,mem,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem)) + +#define mmx_r2m(op,reg,mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=m" (mem) \ + : /* nothing */ ) + +#define mmx_r2r(op,regs,regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + + +#define emms() __asm__ __volatile__ ("emms") + +#define movd_m2r(var,reg) mmx_m2r (movd, var, reg) +#define movd_r2m(reg,var) mmx_r2m (movd, reg, var) +#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) + +#define movq_m2r(var,reg) mmx_m2r (movq, var, reg) +#define movq_r2m(reg,var) mmx_r2m (movq, reg, var) +#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) + +#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) +#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) +#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) +#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) + +#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) +#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) + +#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) +#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) +#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) +#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) +#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) +#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) + +#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) +#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) +#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) +#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) + +#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) +#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) +#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) +#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) + +#define pand_m2r(var,reg) mmx_m2r (pand, var, reg) +#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) + +#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) +#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) + +#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) +#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) +#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) +#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) +#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) +#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) + +#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) +#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) +#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) +#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) +#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) +#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) + +#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) +#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) + +#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) +#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) + +#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) +#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) + +#define por_m2r(var,reg) mmx_m2r (por, var, reg) +#define por_r2r(regs,regd) mmx_r2r (por, regs, regd) + +#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) +#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) +#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) +#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) +#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) +#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) +#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) +#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) +#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) + +#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) +#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) +#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) +#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) +#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) +#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) + +#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) +#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) +#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) +#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) +#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) +#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) +#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) +#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) +#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) + +#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) +#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) +#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) +#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) +#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) +#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) + +#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) +#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) +#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) +#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) + +#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) +#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) +#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) +#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) + +#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) +#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) +#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) +#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) +#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) +#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) + +#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) +#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) +#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) +#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) +#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) +#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) + +#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) +#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) + + +/* 3DNOW extensions */ + +#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) +#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) + + +/* AMD MMX extensions - also available in intel SSE */ + + +#define mmx_m2ri(op,mem,reg,imm) \ + __asm__ __volatile__ (#op " %1, %0, %%" #reg \ + : /* nothing */ \ + : "X" (mem), "X" (imm)) +#define mmx_r2ri(op,regs,regd,imm) \ + __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ + : /* nothing */ \ + : "X" (imm) ) + +#define mmx_fetch(mem,hint) \ + __asm__ __volatile__ ("prefetch" #hint " %0" \ + : /* nothing */ \ + : "X" (mem)) + + +#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) + +#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) + +#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) +#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) +#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) +#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) + +#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) + +#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) + +#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) +#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) + +#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) +#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) + +#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) +#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) + +#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) +#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) + +#define pmovmskb(mmreg,reg) \ + __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) + +#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) +#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) + +#define prefetcht0(mem) mmx_fetch (mem, t0) +#define prefetcht1(mem) mmx_fetch (mem, t1) +#define prefetcht2(mem) mmx_fetch (mem, t2) +#define prefetchnta(mem) mmx_fetch (mem, nta) + +#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) +#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) + +#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) +#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) + +#define sfence() __asm__ __volatile__ ("sfence\n\t") + +/* SSE2 */ +#define pshufhw_m2r(var,reg,imm) mmx_m2ri(pshufhw, var, reg, imm) +#define pshufhw_r2r(regs,regd,imm) mmx_r2ri(pshufhw, regs, regd, imm) +#define pshuflw_m2r(var,reg,imm) mmx_m2ri(pshuflw, var, reg, imm) +#define pshuflw_r2r(regs,regd,imm) mmx_r2ri(pshuflw, regs, regd, imm) + +#define pshufd_r2r(regs,regd,imm) mmx_r2ri(pshufd, regs, regd, imm) + +#define movdqa_m2r(var,reg) mmx_m2r (movdqa, var, reg) +#define movdqa_r2m(reg,var) mmx_r2m (movdqa, reg, var) +#define movdqa_r2r(regs,regd) mmx_r2r (movdqa, regs, regd) +#define movdqu_m2r(var,reg) mmx_m2r (movdqu, var, reg) +#define movdqu_r2m(reg,var) mmx_r2m (movdqu, reg, var) +#define movdqu_r2r(regs,regd) mmx_r2r (movdqu, regs, regd) + +#define pmullw_r2m(reg,var) mmx_r2m (pmullw, reg, var) + +#define pslldq_i2r(imm,reg) mmx_i2r (pslldq, imm, reg) +#define psrldq_i2r(imm,reg) mmx_i2r (psrldq, imm, reg) + +#define punpcklqdq_r2r(regs,regd) mmx_r2r (punpcklqdq, regs, regd) +#define punpckhqdq_r2r(regs,regd) mmx_r2r (punpckhqdq, regs, regd) + + +#endif /* I386MMX_H */ diff --git a/motion-dist.conf b/motion-dist.conf new file mode 100644 index 0000000..64b32a9 --- /dev/null +++ b/motion-dist.conf @@ -0,0 +1,577 @@ +# Rename this distribution example file to motion.conf +# +# This config file was generated by motion 3.2.4 + + +############################################################ +# Daemon +############################################################ + +# Start in daemon (background) mode and release terminal (default: off) +daemon on + +############################################################ +# Basic Setup Mode +############################################################ + +# Start in Setup-Mode, daemon disabled. (default: off) +setup_mode off + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +videodevice /dev/video0 + +# Tuner device to be used for capturing using tuner as source (default /dev/tuner0) +# This is ONLY used for FreeBSD. Leave it commented out for Linux +; tunerdevice /dev/tuner0 + +# The video input to be used (default: 8) +# Should normally be set to 1 for video/TV cards, and 8 for USB cameras +input 8 + +# The video norm to use (only for video capture and TV tuner cards) +# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +norm 0 + +# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0) +frequency 0 + +# Rotate image this number of degrees. The rotation affects all saved images as +# well as mpeg movies. Valid values: 0 (default = no rotation), 90, 180 and 270. +rotate 0 + +# Image width (pixels). Valid range: Camera dependent, default: 352 +width 320 + +# Image height (pixels). Valid range: Camera dependent, default: 288 +height 240 + +# Maximum number of frames to be captured per second. +# Valid range: 2-100. Default: 100 (almost no limit). +framerate 2 + +# URL to use if you are using a network camera, size will be autodetected (incl http://) +# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined +; netcam_url value + +# Username and password for network camera (only if required). Default: not defined +# Syntax is user:password +; netcam_userpass value + +# URL to use for a netcam proxy server, if required, e.g. "http://myproxy". +# If a port number other than 80 is needed, use "http://myproxy:1234". +# Default: not defined +; netcam_proxy value + +# Let motion regulate the brightness of a video device (default: off). +# The auto_brightness feature uses the brightness option as its target value. +# If brightness is zero auto_brightness will adjust to average brightness value 128. +# Only recommended for cameras without auto brightness +auto_brightness off + +# Set the initial brightness of a video device. +# If auto_brightness is enabled, this value defines the average brightness level +# which Motion will try and adjust to. +# Valid range 0-255, default 0 = disabled +brightness 0 + +# Set the contrast of a video device. +# Valid range 0-255, default 0 = disabled +contrast 0 + +# Set the saturation of a video device. +# Valid range 0-255, default 0 = disabled +saturation 0 + +# Set the hue of a video device (NTSC feature). +# Valid range 0-255, default 0 = disabled +hue 0 + + +############################################################ +# Round Robin (multiple inputs on same video device name) +############################################################ + +# Number of frames to capture in each roundrobin step (default: 1) +roundrobin_frames 1 + +# Number of frames to skip before each roundrobin step (default: 1) +roundrobin_skip 1 + +# Try to filter out noise generated by roundrobin (default: off) +switchfilter off + + +############################################################ +# Motion Detection Settings: +############################################################ + +# Threshold for number of changed pixels in an image that +# triggers motion detection (default: 1500) +threshold 1500 + +# Automatically tune the threshold down if possible (default: off) +threshold_tune off + +# Noise threshold for the motion detection (default: 32) +noise_level 32 + +# Automatically tune the noise threshold (default: on) +noise_tune on + +# Enables motion to adjust its detection/noise level for very dark frames +# Don't use this with noise_tune on. (default: off) +night_compensate off + +# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined) +# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid. +# (l)abeling must only be used once and the 'l' must be the last letter. +# Comment out to disable +despeckle EedDl + +# PGM file to use as a sensitivity mask. +# Full path name to. (Default: not defined) +; mask_file value + +# Dynamically create a mask file during operation (default: 0) +# Adjust speed of mask changes from 0 (off) to 10 (fast) +smart_mask_speed 0 + +# Ignore sudden massive light intensity changes given as a percentage of the picture +# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled +lightswitch 0 + +# Picture frames must contain motion at least the specified number of frames +# in a row before they are detected as true motion. At the default of 1, all +# motion is detected. Valid range: 1 to thousands, recommended 1-10 +minimum_motion_frames 1 + +# Specifies the number of pre-captured (buffered) pictures from before motion +# was detected that will be output at motion detection. +# Recommended range: 0 to 5 (default: 0) +# Do not use large values! Large values will cause Motion to skip video frames and +# cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead. +pre_capture 0 + +# Number of frames to capture after motion is no longer detected (default: 0) +post_capture 0 + +# Gap is the seconds of no motion detection that triggers the end of an event +# An event is defined as a series of motion images taken within a short timeframe. +# Recommended value is 60 seconds (Default). The value 0 is allowed and disables +# events causing all Motion to be written to one single mpeg file and no pre_capture. +gap 60 + +# Minimum gap in seconds between the storing pictures while detecting motion. +# Default: 0 = as fast as possible (given by the camera framerate) +# Note: This option has nothing to do with the option 'gap' +minimum_gap 0 + +# Maximum length in seconds of an mpeg movie +# When value is exceeded a new mpeg file is created. (Default: 0 = infinite) +max_mpeg_time 0 + +# Number of frames per second to capture when not detecting +# motion (saves CPU load) (Default: 0 = disabled) +low_cpu 0 + +# Always save images even if there was no motion (default: off) +output_all off + + +############################################################ +# Image File Output +############################################################ + +# Output 'normal' pictures when motion is detected (default: on) +# Valid values: on, off, first, best +# When set to 'first', only the first picture of an event is saved. +# Picture with most motion of an event is saved when set to 'best'. +# Can be used as preview shot for the corresponding movie. +output_normal on + +# Output pictures with only the pixels moving object (ghost images) (default: off) +output_motion off + +# The quality (in percent) to be used by the jpeg compression (default: 75) +quality 75 + +# Output ppm images instead of jpeg (default: off) +ppm off + + +############################################################ +# Film (mpeg) File Output - ffmpeg based +############################################################ + +# Use ffmpeg to encode mpeg movies in realtime (default: off) +ffmpeg_cap_new on + +# Use ffmpeg to make movies with only the pixels moving +# object (ghost images) (default: off) +ffmpeg_cap_motion off + +# Use ffmpeg to encode a timelapse movie +# Default value 0 = off - else save frame every Nth second +ffmpeg_timelapse 0 + +# The file rollover mode of the timelapse video +# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual +ffmpeg_timelapse_mode daily + +# Bitrate to be used by the ffmpeg encoder (default: 400000) +# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled) +ffmpeg_bps 500000 + +# Enables and defines variable bitrate for the ffmpeg encoder. +# ffmpeg_bps is ignored if variable bitrate is enabled. +# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, +# or the range 2 - 31 where 2 means best quality and 31 is worst. +ffmpeg_variable_bitrate 0 + +# Codec to used by ffmpeg for the video compression. +# Timelapse mpegs are always made in mpeg1 format independent from this option. +# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4. +# mpeg1 - gives you files with extension .mpg +# mpeg4 or msmpeg4 - give you files with extension .avi +# msmpeg4 is recommended for use with Windows Media Player because +# it requires no installation of codec on the Windows client. +ffmpeg_video_codec mpeg4 + + +############################################################ +# Snapshots (Traditional Periodic Webcam File Output) +############################################################ + +# Make automated snapshot every N seconds (default: 0 = disabled) +snapshot_interval 0 + + +############################################################ +# Text Display +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, %T = HH:MM:SS, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, \n = new line, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event - do not use with text_event! +# You can put quotation marks around the text to allow +# leading spaces +############################################################ + +# Locate and draw a box around the moving object. +# Valid values: on, off and preview (default: off) +# Set to 'preview' will only draw a box in preview_shot pictures. +locate off + +# Draws the timestamp using same options as C function strftime(3) +# Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +# Text is placed in lower right corner +text_right %Y-%m-%d\n%T-%q + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +; text_left CAMERA %t + +# Draw the number of changed pixed on the images (default: off) +# Will normally be set to off except when you setup and adjust the motion settings +# Text is placed in upper right corner +text_changes off + +# This option defines the value of the speciel event conversion specifier %C +# You can use any conversion specifier in this option except %C. Date and time +# values are from the timestamp of the first image in the current event. +# Default: %Y%m%d%H%M%S +# The idea is that %C can be used filenames and text_left/right for creating +# a unique identifier for each event. +text_event %Y%m%d%H%M%S + +# Draw characters at twice normal size on images. (default: off) +text_double off + + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute path. (Default: current working directory) +target_dir /usr/local/apache2/htdocs/cam1 + +# File path for snapshots (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-snapshot +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot +# File extension .jpg or .ppm is automatically added so do not include this. +# Note: A symbolic link called lastsnap.jpg created in the target_dir will always +# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' +snapshot_filename %v-%Y%m%d%H%M%S-snapshot + +# File path for motion triggered images (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-%q +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q +# File extension .jpg or .ppm is automatically added so do not include this +# Set to 'preview' together with best-preview feature enables special naming +# convention for preview shots. See motion guide for details +jpeg_filename %v-%Y%m%d%H%M%S-%q + +# File path for motion triggered ffmpeg films (mpeg) relative to target_dir +# Default: %v-%Y%m%d%H%M%S +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S +# File extension .mpg or .avi is automatically added so do not include this +ffmpeg_filename %v-%Y%m%d%H%M%S + +# File path for timelapse mpegs relative to target_dir +# Default: %Y%m%d-timelapse +# Default value is near equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse +# File extension .mpg is automatically added so do not include this +timelapse_filename %Y%m%d-timelapse + + +############################################################ +# Live Webcam Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +webcam_port 8081 + +# Quality of the jpeg images produced (default: 50) +webcam_quality 50 + +# Output frames at 1 fps when no motion is detected and increase to the +# rate given by webcam_maxrate when motion is detected (default: off) +webcam_motion off + +# Maximum framerate for webcam streams (default: 1) +webcam_maxrate 1 + +# Restrict webcam connections to localhost only (default: on) +webcam_localhost on + +# Limits the number of images per connection (default: 0 = unlimited) +# Number can be defined by multiplying actual webcam rate by desired number of seconds +# Actual webcam rate is the smallest of the numbers framerate and webcam_maxrate +webcam_limit 0 + + +############################################################ +# HTTP Based Control +############################################################ + +# TCP/IP port for the http server to listen on (default: 0 = disabled) +control_port 8080 + +# Restrict control connections to localhost only (default: on) +control_localhost on + +# Output for http server, select off to choose raw text plain (default: on) +control_html_output on + +# Authentication for the http based control. Syntax username:password +# Default: not defined (Disabled) +; control_authentication username:password + + +############################################################ +# Tracking (Pan/Tilt) +############################################################ + +# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic) +# The generic type enables the definition of motion center and motion size to +# be used with the convertion specifiers for options like on_motion_detected +track_type 0 + +# Enable auto tracking (default: off) +track_auto off + +# Serial port of motor (default: none) +; track_port value + +# Motor number for x-axis (default: -1) +track_motorx -1 + +# Maximum value on x-axis (default: 0) +track_maxx 0 + +# ID of an iomojo camera if used (default: 0) +track_iomojo_id 0 + +# Angle in degrees the camera moves per step on the X-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_x 10 + +# Angle in degrees the camera moves per step on the Y-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_y 10 + +# Delay to wait for after auto-track movement as number +# of picture frames (default: 10) +track_move_wait 10 + +# Speed to set the motor to (default: 255) +track_speed 255 + +# Number of steps to make (default: 40) +track_stepsize 40 + + +############################################################ +# External Commands, Warnings and Logging: +# You can use conversion specifiers for the on_xxxx commands +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# %f = filename with full path +# %n = number indicating filetype +# Both %f and %n are only defined for on_picture_save, +# on_movie_start and on_movie_end +# Quotation marks round string are allowed. +############################################################ + +# Do not sound beeps when detecting motion (default: on) +# Note: Motion never beeps when running in daemon mode. +quiet on + +# Command to be executed when an event starts. (default: none) +# An event starts at first motion detected after a period of no motion defined by gap +; on_event_start value + +# Command to be executed when an event ends after a period of no motion +# (default: none). The period of no motion is defined by option gap. +; on_event_end value + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# To give the filename as an argument to a command append it with %f +; on_picture_save value + +# Command to be executed when a motion frame is detected (default: none) +; on_motion_detected value + +# Command to be executed when a movie file (.mpg|.avi) is created. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_start value + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_end value + + +############################################################ +# Common Options For MySQL and PostgreSQL database features. +# Options require the MySQL/PostgreSQL options to be active also. +############################################################ + +# Log to the database when creating motion triggered image file (default: on) +sql_log_image on + +# Log to the database when creating a snapshot image file (default: on) +sql_log_snapshot on + +# Log to the database when creating motion triggered mpeg file (default: off) +sql_log_mpeg off + +# Log to the database when creating timelapse mpeg file (default: off) +sql_log_timelapse off + +# SQL query string that is sent to the database +# Use same conversion specifiers has for text features +# Additional special conversion specifiers are +# %n = the number representing the file_type +# %f = filename with full path +# Default value: +# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') + + +############################################################ +# Database Options For MySQL +############################################################ + +# Mysql database to log to (default: not defined) +; mysql_db value + +# The host on which the database is located (default: not defined) +; mysql_host value + +# User account name for MySQL database (default: not defined) +; mysql_user value + +# User password for MySQL database (default: not defined) +; mysql_password value + + +############################################################ +# Database Options For PostgreSQL +############################################################ + +# PostgreSQL database to log to (default: not defined) +; pgsql_db value + +# The host on which the database is located (default: not defined) +; pgsql_host value + +# User account name for PostgreSQL database (default: not defined) +; pgsql_user value + +# User password for PostgreSQL database (default: not defined) +; pgsql_password value + +# Port on which the PostgreSQL database is located (default: 5432) +; pgsql_port 5432 + + +############################################################ +# Video Loopback Device (vloopback project) +############################################################ + +# Output images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; video_pipe value + +# Output motion images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; motion_video_pipe value + + +############################################################## +# Thread config files - One for each camera. +# Except if only one camera - You only need this config file. +# If you have more than one camera you MUST define one thread +# config file for each camera in addition to this config file. +############################################################## + +# Remember: If you have more than one camera you must have one +# thread file for each camera. E.g. 2 cameras requires 3 files: +# This motion.conf file AND thread1.conf and thread2.conf. +# Only put the options that are unique to each camera in the +# thread config files. +; thread /usr/local/etc/thread1.conf +; thread /usr/local/etc/thread2.conf +; thread /usr/local/etc/thread3.conf +; thread /usr/local/etc/thread4.conf + diff --git a/motion.1 b/motion.1 new file mode 100644 index 0000000..8793d0f --- /dev/null +++ b/motion.1 @@ -0,0 +1,793 @@ +.TH MOTION 1 2005-12-13 "Motion" "Motion Options and Config Files" +.SH NAME +motion \- Detect motion using a video4linux device +.SH SYNOPSIS +.B motion +[ -hns ] [ -c config file path] +.SH DESCRIPTION +.I Motion +uses a video4linux device to detect motion. If motion is detected both normal +and motion pictures will be taken. Motion can also take actions to notify you +if needed. Creation of automated snapshots is also possible. +.SH OPTIONS +.TP +.B \-c +Full path and filename of config file. E.g. /home/kurt/motion.conf. Default is /usr/local/etc unless specified differently when building Motion. Many RPMs and debian packages will most likely use /etc or /etc/motion as default. +.TP +.B \-h +Show help screen. +.TP +.B \-n +Run in non-daemon mode. +.TP +.B \-s +Run in setup mode. Also forces non-daemon mode +.TP +.B \-d +Run in debug mode. +.TP +.SH "CONFIG FILE OPTIONS" +These are the options that can be used in the config file. +.I They are overridden by the commandline! +All number values are integer numbers (no decimals allowed). +Boolean options can be on or off (values "1", "yes" and "on" all means true and any other value means false). +.TP +.B auto_brightness boolean +Values: on, off / Default: off +.br +Let motion regulate the brightness of a video device. Only recommended for cameras without auto brightness +.TP +.B brightness integer +Values: 0 - 255 / Default: 0 (disabled) +.br +The brightness level for the video device. +.TP +.B contrast boolean +Values: 0 - 255 / Default: 0 (disabled) +.br +The contrast level for the video device. +.TP +.B control_authentication string +Values: Max 4096 characters / Default: Not defined +.br +To protect HTTP Control by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. This option must be placed in motion.conf and not in a thread config file. +.TP +.B control_html_output boolean +Values: on, off / Default: on +.br +Enable HTML in the answer sent back to a browser connecting to the control_port. This option must be placed in motion.conf and not in a thread config file. +.TP +.B control_localhost boolean +Values: on, off / Default: on +.br +Limits the http (html) control to the localhost. This option must be placed in motion.conf and not in a thread config file. +.TP +.B control_port integer +Values: 0 - 65535 / Default: 0 (disabled) +.br +Sets the port number for the http (html using browser) based remote control. This option must be placed in motion.conf and not in a thread config file. +.TP +.B daemon boolean +Values: on, off / Default: off +.br +Start in daemon (background) mode and release terminal. This option must be placed in motion.conf and not in a thread config file. +.TP +.B despeckle string +Values: EedDl / Default: Not defined +.br +Despeckle motion image using combinations of (E/e)rode or (D/d)ilate. And ending with optional (l)abeling. +.TP +.B ffmpeg_bps integer +Values: 0 - 9999999 / Default: 400000 +.br +Bitrate of mpegs produced by ffmpeg. Bitrate is bits per second. Default: 400000 (400kbps). Higher value mans better quality and larger files. Option requires that ffmpeg libraries are installed. +.TP +.B ffmpeg_cap_motion boolean +Values: on, off / Default: off +.br +Use ffmpeg libraries to encode motion type mpeg movies where you only see the pixels that changes. +.TP +.B ffmpeg_cap_new boolean +Values: on, off / Default: off +.br +Use ffmpeg libraries to encode mpeg movies in realtime. +.TP +.B ffmpeg_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d%H%M%S +.br +File path for motion triggered ffmpeg movies (mpeg) relative to target_dir. +.TP +.B ffmpeg_timelapse boolean +Values: 0 - 2147483647 / Default: 0 (disabled) +.br +Create a timelapse movie saving a picture frame at the interval in seconds set by this parameter. Set it to 0 if not used. +.TP +.B ffmpeg_timelapse_mode discrete strings +Values: hourly, daily, weekly-sunday, weekly-monday, monthly, manual / Default: daily +.br +The file rollover mode of the timelapse video. +.TP +.B ffmpeg_variable_bitrate integer +Values: 0, 2 - 31 / Default: 0 (disabled) +.br +Enables and defines variable bitrate for the ffmpeg encoder. ffmpeg_bps is ignored if variable bitrate is enabled. Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, or the range 2 - 31 where 2 means best quality and 31 is worst. +.TP +.B ffmpeg_video_codec discrete strings +Values: mpeg1 (ffmpeg-0.4.8 only), mpeg4, msmpeg4 / Default: mpeg4 +.br +Codec to be used by ffmpeg for the video compression. Timelapse mpegs are always made in mpeg1 format independent from this option. +.TP +.B framerate integer +Values: 2 - 100 / Default: 100 (no limit) +.br +Maximum number of frames to be captured from the camera per second. +.TP +.B frequency boolean +Values: 0 - 999999 / Default: 0 (Not set) +.br +The frequency to set the tuner to (kHz). Valid range: per tuner spec, default: 0 (Don't set it) +.TP +.B gap integer +Values: 0 - 2147483647 / Default: 60 +.br +Gap is the seconds of no motion detection that triggers the end of an event. An event is defined as a series of motion images taken within a short timeframe. +.TP +.B height integer +Values: Device Dependent / Default: 288 +.br +The height of each frame in pixels. +.TP +.B hue integer +Values: 0 - 255 / Default: 0 (disabled) +.br +The hue level for the video device. +.TP +.B input integer +Values: 0 - 7, 8 = disabled / Default: 8 (disabled) +.br +Input channel to use expressed as an integer number starting from 0. Should normally be set to 1 for video/TV cards, and 8 for USB cameras. +.TP +.B jpeg_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d%H%M%S-%q +.br +File path for motion triggered images (jpeg or ppm) relative to target_dir. Value 'preview' makes a jpeg filename with the same name body as the associated saved mpeg movie file. +.TP +.B lightswitch integer +Values: 0 - 100 / Default: 0 (disabled) +.br +Ignore sudden massive light intensity changes given as a percentage of the picture area that changed intensity. +.TP +.B locate boolean +Values: on, off, preview / Default: off +.br +Locate and draw a box around the moving object. Value 'preview' makes Motion only draw a box on a saved preview jpeg image and not on the saved mpeg movie. +.TP +.B low_cpu integer +Values: 0 - 100 / Default: 0 (disabled) +.br +When this option is not zero motion will be in a low cpu mode while not detecting motion. In low cpu mode Motion reduces the framerate to the value given for this option. Value zero means disabled. +.TP +.B mask_file string +Values: Max 4095 characters / Default: Not defined +.br +PGM file to use as a sensitivity mask. This picture MUST have the same width and height as the frames being captured and be in binary format. +.TP +.B max_mpeg_time integer +Values: 0 (infinite) - 2147483647 / Default: 3600 +.br +The maximum length of an mpeg movie in seconds. Set this to zero for unlimited length. +.TP +.B minimum_gap integer +Values: 0 - 2147483647 / Default: 0 (no minimum) +.br +The minimum time between two shots in seconds. +.TP +.B minimum_motion_frames boolean +Values: 1 - 1000s / Default: 1 +.br +Picture frames must contain motion at least the specified number of frames in a row before they are detected as true motion. At the default of 1, all motion is detected. Valid range is 1 to thousands, but it is recommended to keep it within 1-10. +.TP +.B motion_video_pipe string +Values: Max 4095 characters / Default: Not defined +.br +The video4linux video loopback input device for motion images. If a particular pipe is to be used then use the device filename of this pipe, if a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. Default: not set +.TP +.B mysql_db string +Values: Max 4095 characters / Default: Not defined +.br +Name of the MySQL database. +.TP +.B mysql_host string +Values: Max 4095 characters / Default: Not defined +.br +IP address or domain name for the MySQL server. Use "localhost" if motion and MySQL runs on the same server. +.TP +.B mysql_password string +Values: Max 4095 characters / Default: Not defined +.br +The MySQL password. +.TP +.B mysql_user string +Values: Max 4095 characters / Default: Not defined +.br +The MySQL user name. +.TP +.B netcam_proxy string +Values: Max 4095 characters / Default: Not defined +.br +URL to use for a netcam proxy server, if required. The syntax is http://myproxy:portnumber +.TP +.B netcam_url string +Values: Max 4095 characters / Default: Not defined +.br +Specify an url to a downloadable jpeg file or raw mjpeg stream to use as input device. Such as an AXIS 2100 network camera. +.TP +.B netcam_userpass string +Values: Max 4095 characters / Default: Not defined +.br +For network cameras protected by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. +.TP +.B night_compensate boolean +Values: on, off / Default: off +.br +When this option is set the noise threshold will be lowered if the picture is dark. This will improve the sensitivity in dark places. However it might also increase the number of false alarms since most cameras also compensate for this with their AGC which will increase noise. +.TP +.B noise_level integer +Values: 1 - 255 / Default: 32 +.br +The noise level is used as a threshold for distinguishing between noise and motion. +.TP +.B noise_tune boolean +Values: on, off / Default: on +.br +Activates the automatic tuning of noise level. +.TP +.B norm discrete strings +Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour) / Default: 0 (PAL) +.br +Select the norm of the video device. Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +.TP +.B on_event_end string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an event ends after a period of no motion. The period of no motion is defined by option gap. You can use Conversion Specifiers and spaces as part of the command. +.TP +.B on_event_start string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an event starts. An event starts at first motion detected after a period of no motion defined by gap. You can use ConversionSpecifiers and spaces as part of the command. +.TP +.B on_motion_detected string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when a motion frame is detected. You can use Conversion Specifiers and spaces as part of the command. +.TP +.B on_movie_end string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an ffmpeg movie is closed at the end of an event. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +.TP +.B on_movie_start string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an mpeg movie is created. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +.TP +.B on_picture_save string +Values: Max 4095 characters / Default: Not defined +.br +Command to be executed when an image is saved. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +.TP +.B output_all boolean +Values: on, off / Default: off +.br +Picture are saved continuously as if motion was detected all the time. +.TP +.B output_motion boolean +Values: on, off / Default: off +.br +Output pictures with only the moving object. This feature generates the special motion type movies where you only see the pixels that changes as a graytone image. If labelling is enabled you see the largest area in blue. Smartmask is shown in red. +.TP +.B output_normal discrete strings +Values: on, off, first, best / Default: on +.br +Normal image is an image that is stored when motion is detected. It is the same image that was taken by the camera. I.e. not a motion image like defined by output_motion. Default is that normal images are stored. +.TP +.B pgsql_db string +Values: Max 4095 characters / Default: Not defined +.br +Name of the PostgreSQL database. +.TP +.B pgsql_host string +Values: Max 4095 characters / Default: Not defined +.br +IP address or domain name for the PostgreSQL server. Use "localhost" if motion and PostgreSQL runs on the same server. +.TP +.B pgsql_password string +Values: Max 4095 characters / Default: Not defined +.br +The PostgreSQL password. +.TP +.B pgsql_port integer +Values: 0 - 65535 / Default: 5432 +.br +The PostgreSQL server port number. +.TP +.B pgsql_user string +Values: Max 4095 characters / Default: Not defined +.br +The PostgreSQL user name. +.TP +.B post_capture integer +Values: 0 - 2147483647 / Default: 0 (disabled) +.br +Specifies the number of frames to be captured after motion has been detected. +.TP +.B ppm boolean +Values: on, off / Default: off +.br +Output ppm images instead of jpeg. This uses less CPU time, but causes a LOT of hard disk I/O, and it is generally slower than jpeg. +.TP +.B pre_capture integer +Values: 0 - 100s / Default: 0 (disabled) +.br +Specifies the number of previous frames to be outputted at motion detection. Recommended range: 0 to 5, default=0. Do not use large values! Large values will cause Motion to skip video frames and cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead. +.TP +.B quality integer +Values: 1 - 100 / Default: 75 +.br +The quality for the jpeg images in percent. +.TP +.B quiet boolean +Values: on, off / Default: off +.br +Be quiet, don't output beeps when detecting motion. +.TP +.B rotate discrete strings +Values: 0, 90, 180, 270 / Default: 0 +.br +Rotate image the given number of degrees. The rotation affects all saved images as well as mpeg movies. +.TP +.B roundrobin_frames integer +Values: 1 - 2147483647 / Default: 1 +.br +Specifies the number of frames to capture before switching inputs, this way also slow switching (e.g. every second) is possible. +.TP +.B roundrobin_skip integer +Values: 1 - 2147483647 / Default: 1 +.br +Specifies the number of frames to skip after a switch. (1 if you are feeling lucky, 2 if you want to be safe). +.TP +.B saturation integer +Values: 0 - 255 / Default: 0 (disabled) +.br +The colour saturation level for the video device. +.TP +.B setup_mode boolean +Values: on, off / Default: off +.br +Run Motion in setup mode. +.TP +.B smart_mask_speed integer +Values: 0 - 10 / Default: 0 (disabled) +.br +Slugginess of the smart mask. Default is 0 = DISABLED. 1 is slow, 10 is fast. +.TP +.B snapshot_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d%H%M%S-snapshot +.br +File path for snapshots (jpeg or ppm) relative to target_dir. +.TP +.B snapshot_interval integer +Values: 0 - 2147483647 / Default: 0 (disabled) +.br +Make automated snapshots every 'snapshot_interval' seconds. +.TP +.B sql_log_image boolean +Values: on, off / Default: on +.br +Log to the database when creating motion triggered image file. +.TP +.B sql_log_mpeg boolean +Values: on, off / Default: off +.br +Log to the database when creating motion triggered mpeg file. +.TP +.B sql_log_snapshot boolean +Values: on, off / Default: on +.br +Log to the database when creating a snapshot image file. +.TP +.B sql_log_timelapse boolean +Values: on, off / Default: off +.br +Log to the database when creating timelapse mpeg file +.TP +.B sql_query string +Values: Max 4095 characters / Default: insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +.br +SQL query string that is sent to the database. The values for each field are given by using convertion specifiers +.TP +.B switchfilter boolean +Values: on, off / Default: off +.br +Turns the switch filter on or off. The filter can distinguish between most switching noise and real motion. With this you can even set roundrobin_skip to 1 without generating much false detection. +.TP +.B target_dir string +Values: Max 4095 characters / Default: Not defined = current working directory +.br +Target directory for picture and movie files. +.TP +.B text_changes boolean +Values: on, off / Default: off +.br +Turns the text showing changed pixels on/off. +.TP +.B text_double boolean +Values: on, off / Default: off +.br +Draw characters at twice normal size on images. +.TP +.B text_event string +Values: Max 4095 characters / Default: %Y%m%d%H%M%S +.br +This option defines the value of the speciel event conversion specifier %C. You can use any conversion specifier in this option except %C. Date and time values are from the timestamp of the first image in the current event. +.TP +.B text_left string +Values: Max 4095 characters / Default: Not defined +.br +User defined text overlayed on each in the lower left corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > | , . : - + _ \n and conversion specifiers (codes starting by a %). +.TP +.B text_right string +Values: Max 4095 characters / Default: %Y-%m-%d\n%T +.br +User defined text overlayed on each in the lower right corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > | , . : - + _ \n and conversion specifiers (codes starting by a %). Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +.TP +.B thread string +Values: Max 4095 characters / Default: Not defined +.br +Specifies full path and filename for a thread config file. Each camera needs a thread config file containing the options that are unique to the camera. If you only have one camera you do not need thread config files. If you have two or more cameras you need one thread config file for each camera in addition to motion.conf. This option must be placed in motion.conf and not in a thread config file. +.TP +.B threshold integer +Values: 1 - 2147483647 / Default: 1500 +.br +Threshold for declaring motion. The threshold is the number of changed pixels counted after noise filtering, masking, despeckle, and labelling. +.TP +.B threshold_tune boolean +Values: on, off / Default: off +.br +Activates the automatic tuning of threshold level. +.TP +.B timelapse_filename string +Values: Max 4095 characters / Default: %v-%Y%m%d-timelapse +.br +File path for timelapse mpegs relative to target_dir (ffmpeg only). +.TP +.B track_auto boolean +Values: on, off / Default: off +.br +Enable auto tracking +.TP +.B track_iomojo_id integer +Values: 0 - 2147483647 / Default: 0 +.br +Use this option if you have an iomojo smilecam connected to the serial port instead of a general stepper motor controller. +.TP +.B track_maxx integer +Values: 0 - 2147483647 / Default: 0 +.br +The maximum position for servo x. +.TP +.B track_motorx integer +Values: -1 - 2147483647 / Default: -1 +.br +The motor number that is used for controlling the x-axis. +.TP +.B track_move_wait integer +Values: 0 - 2147483647 / Default: 10 +.br +Delay during which tracking is disabled after auto tracking has moved the camera. Delay is defined as number of picture frames. +.TP +.B track_port string +Values: Max 4095 characters / Default: Not defined +.br +This is the device name of the serial port to which the stepper motor interface is connected. +.TP +.B track_speed integer +Values: 0 - 255 / Default: 255 +.br +Speed to set the motor to. +.TP +.B track_step_angle_x integer +Values: 0-90 / Default: 10 +.br +Angle in degrees the camera moves per step on the X-axis with auto tracking. Currently only used with pwc type cameras. +.TP +.B track_step_angle_y integer +Values: 0-40 / Default: 10 +.br +Angle in degrees the camera moves per step on the Y-axis with auto tracking. Currently only used with pwc type cameras. +.TP +.B track_stepsize integer +Values: 0 - 255 / Default: 40 +.br +Number of steps to make. +.TP +.B track_type discrete strings +Values: 0 (none), 1 (stepper), 2 (iomojo), 3 (pwc), 4 (generic) / Default: 0 (None) +.br +Type of tracker. +.TP +.B tunerdevice string +Values: Max 4095 characters / Default: /dev/tuner0 +.br +The tuner device used for controlling the tuner in a tuner card. This option is only used when Motion is compiled for FreeBSD. +.TP +.B video_pipe string +Values: Max 4095 characters / Default: Not defined +.br +The video4linux video loopback input device for normal images. If a particular pipe is to be used then use the device filename of this pipe. If a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. +.TP +.B videodevice string +Values: Max 4095 characters / Default: /dev/video0 (FreeBSD: /dev/bktr0) +.br +The video device to be used for capturing. Default for Linux is /dev/video0. for FreeBSD the default is /dev/bktr0. +.TP +.B webcam_limit integer +Values: 0 - 2147483647 / Default: 0 (unlimited) +.br +Limit the number of frames to number frames. After 'webcam_limit' number of frames the connection will be closed by motion. The value 0 means unlimited. +.TP +.B webcam_localhost boolean +Values: on, off / Default: on +.br +Limits the access to the webcam to the localhost. +.TP +.B webcam_maxrate integer +Values: 1 - 100 / Default: 1 +.br +Limit the framerate of the webcam. Default is 1. Set the value to 100 for practically unlimited. +.TP +.B webcam_motion boolean +Values: on, off / Default: off +.br +If set to 'on' Motion sends slows down the webcam stream to 1 picture per second when no motion is detected. When motion is detected the stream runs as defined by webcam_maxrate. When 'off' the webcam stream always runs as defined by webcam_maxrate. +.TP +.B webcam_port integer +Values: 0 - 65535 / Default: 0 (disabled) +.br +TCP port on which motion will listen for incoming connects with its webcam server. +.TP +.B webcam_quality integer +Values: 1 - 100 / Default: 50 +.br +Quality setting in percent for the mjpeg picture frames transferred over the webcam connection. Keep it low to restrict needed bandwidth. +.TP +.B width integer +Values: Device Dependent / Default: 352 +.br +The width in pixels of each frame. Valid range is camera dependent. + +.SH SIGNALS +Motion responds to the following signals: +.TP +.B SIGHUP +The config file will be reread. +.TP +.B SIGTERM +If needed motion will create an mpeg file of the last event and exit +.TP +.B SIGUSR1 +Motion will create an mpeg file of the current event. +.SH NOTES +.TP +.B Snapshot +A snapshot is a picture taken at regular intervals independently of any movement in the picture. +.TP +.B Motion image +A "motion" image/mpeg shows the pixels that have actually changed during the last frames. These pictures are not very useful for normal presentation to the public but they are quite useful for testing and tuning and making mask files as you can see exactly where motion sees something moving. Motion is shown in greytones. If labelling is enabled the largest area is marked as blue. Smart mask is shown in read. +.TP +.B Normal image +A "normal" image is the real image taken by the camera with text overlayed. +.TP +.B Threads and config files +If Motion was invoked with command line option -c pathname Motion will expect the config file to be as specified. When you specify the config file on the command line with -c you can call it anything. +.br +If you do not specify -c or the filename you give Motion does not exist, Motion will search for the configuration file called 'motion.conf' in the following order: +.br +1. Current directory from where motion was invoked +.br +2. Then in a directory called '.motion' in the current users home directory (shell environment variable $HOME). E.g. /home/goofy/.motion/motion.conf +.br +3. The directory defined by the --sysconfdir=DIR when running .configure during installation of Motion (If this option was not defined the default is /usr/local/etc/) +.br +If you have write access to /usr/local/etc then the editor recommends having only one motion.conf file in the default /usr/local/etc/ directory. +.br +Motion has a configuration file in the distribution package called motion-dist.conf. When you run 'make install' this files gets copied to the /usr/local/etc directory. +.br +The configuration file needs to be renamed from motion-dist.conf to motion.conf. The original file is called motion-dist.conf so that your perfectly working motion.conf file does not accidentally get overwritten when you re-install or upgrade to a newer version of Motion. +.br +If you have more than one camera you should not try and invoke Motion more times. Motion is made to work with more than one camera in a very elegant way and the way to do it is to create a number of thread config files. Motion will then create an extra tread of itself for each camera. If you only have one camera you only need the motion.conf file. The minute you have two or more cameras you must have one thread config file per camera besides the motion.conf file. +.br +So if you have for example two cameras you need motion.conf and two thread config files. Total of 3 config files. +.br +An option that is common to all cameras can be placed in motion.conf. (You can also put all parameters in the thread files but that makes a lot of editing when you change a common thing). +.br +An option that is unique to a camera must be defined in each thread file. +.br +The first camera is defined in the first thread file called from motion.conf. The 2nd camera is defined in the 2nd thread file called from motion.conf etc. +.br +Any option defined in motion.conf will be used for all cameras except for the cameras in which the same option is defined in a thread config file. +.br +Motion reads its configuration parameters in the following sequence. If the same parameter exists more than one place the last one read wins. +.br +1. Motion reads the configuration file motion.conf from the beginning of the file going down line by line. +.br +2. If the option "thread" is defined in motion.conf, the thread configuration file(s) is/(are) read. +.br +3. Motion continues reading the rest of the motion.conf file. Any options from here will overrule the same option previously defines in a thread config file. +.br +4. Motion reads the command line option again overruling any previously defined options. +.br +So always call the thread config files in the end of the motion.conf file. If you define options in motion.conf AFTER the thread file calls, the same options in the thread files will never be used. So always put the thread file call at the end of motion.conf. +.br +If motion is built without specific features such as ffmpeg, mysql etc it will ignore the options that belongs to these features. You do not have to remove them or comment them out. +.br +If you run the http control command http://host:port/0/config/writeyes, motion will overwrite motion.conf and all the thread.conf files by autogenerated config files neatly formatted and only with the features included that Motion was built with. If you later re-build Motion with more features or upgrade to a new version, you can use your old config files, run the motion.conf.write command, and you will have new config files with the new options included all set to their default values. This makes upgrading very easy to do. +.TP +.B Conversion Specifiers for Advanced Filename and Text Features +The table below shows all the supported Conversion Specifiers you can use in the options text_left, text_right, snapshot_filename, jpeg_filename, ffmpeg_filename, timelapse_filename, on_event_start, on_event_end, on_picture_save, on_movie_start, on_movie_end, and on_motion_detected. +.br +In text_left and text_right you can additionally use '\n' for new line. +.TP +.B %a +The abbreviated weekday name according to the current locale. +.TP +.B %A +The full weekday name according to the current locale. +.TP +.B %b +The abbreviated month name according to the current locale. +.TP +.B %B +The full month name according to the current locale. +.TP +.B %c +The preferred date and time representation for the current locale. +.TP +.B %C +Text defined by the text_event feature +.TP +.B %d +The day of the month as a decimal number (range 01 to 31). +.TP +.B %D +Number of pixels detected as Motion. If labelling is enabled the number is the number of pixels in the largest labelled motion area. +.TP +.B %E +Modifier: use alternative format, see below. +.TP +.B %f +File name - used in the on_picture_save, on_movie_start, on_movie_end, and sql_query features. +.TP +.B %F +Equivalent to %Y-%m-%d (the ISO 8601 date format). +.TP +.B %H +The hour as a decimal number using a 24-hour clock (range 00 to 23). +.TP +.B %i +Width of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on). +.TP +.B %I +The hour as a decimal number using a 12-hour clock (range 01 to 12). +.TP +.B %j +The day of the year as a decimal number (range 001 to 366). +.TP +.B %J +Height of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on). +.TP +.B %k +The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) +.TP +.B %K +X coordinate in pixels of the center point of motion. Origin is upper left corner. +.TP +.B %l +The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) +.TP +.B %L +Y coordinate in pixels of the center point of motion. Origin is upper left corner and number is positive moving downwards (I may change this soon). +.TP +.B %m +The month as a decimal number (range 01 to 12). +.TP +.B %M +The minute as a decimal number (range 00 to 59). +.TP +.B %n +Filetype as used in the on_picture_save, on_movie_start, on_movie_end, and sql_query features. +.TP +.B %N +Noise level. +.TP +.B %o +Threshold. The number of detected pixels required to trigger motion. When threshold_tune is 'on' this can be used to show the current tuned value of threshold. +.TP +.B %p +Either 'AM' or 'PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. +.TP +.B %P +Like %p but in lowercase: `am' or `pm' or a corresponding string for the current locale. +.TP +.B %q +Picture frame number within current second. For jpeg filenames this should always be included in the filename if you save more then 1 picture per second to ensure unique filenames. It is not needed in filenames for mpegs. +.TP +.B %Q +Number of detected labels found by the despeckle feature +.TP +.B %r +The time in a.m. or p.m. notation. +.TP +.B %R +The time in 24-hour notation (%H:%M). +.TP +.B %s +The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC. +.TP +.B %S +The second as a decimal number (range 00 to 61). +.TP +.B %t +Thread number (camera number) +.TP +.B %T +The time in 24-hour notation (%H:%M:%S). +.TP +.B %u +The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w. +.TP +.B %U +The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W. +.TP +.B %v +Event number. An event is a series of motion detections happening with less than 'gap' seconds between them. +.TP +.B %V +The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W. +.TP +.B %w +The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. +.TP +.B %W +The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. +.TP +.B %x +The preferred date representation for the current locale without the time. +.TP +.B %X +The preferred time representation for the current locale without the date. +.TP +.B %y +The year as a decimal number without a century (range 00 to 99). +.TP +.B %Y +The year as a decimal number including the century. +.TP +.B %z +The time-zone as hour offset from GMT. +.TP +.B %Z +The time zone or name or abbreviation. + +.TP +.B More information +Motion homepage: http://motion.sourceforge.net/ + +Motion Guide (user and installation guide): +.br +http://www.lavrsen.dk/twiki/bin/view/Motion/MotionGuide +.SH AUTHORS +Jeroen Vreeken (pe1rxq@amsat.org), +Folkert van Heusden, +Kenneth Lavrsen (kenneth@lavrsen.dk) diff --git a/motion.c b/motion.c new file mode 100644 index 0000000..5801aee --- /dev/null +++ b/motion.c @@ -0,0 +1,2251 @@ +/* motion.c + * + * Detect changes in a video stream. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ +#include "ffmpeg.h" +#include "motion.h" + +#ifdef __freebsd__ +#include "video_freebsd.h" +#else +#include "video.h" +#endif /* __freebsd__ */ + +#include "conf.h" +#include "alg.h" +#include "track.h" +#include "event.h" +#include "picture.h" +#include "rotate.h" + +/** + * tls_key_threadnr + * + * TLS key for storing thread number in thread-local storage. + */ +pthread_key_t tls_key_threadnr; + +/** + * global_lock + * + * Protects any global variables (like 'threads_running') during updates, + * to prevent problems with multiple threads updating at the same time. + */ +//pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t global_lock; + +/** + * cnt_list + * + * List of context structures, one for each main Motion thread. + */ +struct context **cnt_list=NULL; + +/** + * threads_running + * + * Keeps track of number of Motion threads currently running. Also used + * by 'main' to know when all threads have exited. + */ +volatile int threads_running=0; + +/* + * debug_level is for developers, normally used to control which + * types of messages get output. + */ +int debug_level; + +/** + * restart + * + * Differentiates between a quit and a restart. When all threads have + * finished running, 'main' checks if 'restart' is true and if so starts + * up again (instead of just quitting). + */ +int restart=0; + +/** + * context_init + * + * Initializes a context struct with the default values for all the + * variables. + * + * Parameters: + * + * cnt - the context struct to destroy + * + * Returns: nothing + */ +static void context_init (struct context *cnt) +{ + /* + * We first clear the entire structure to zero, then fill in any + * values which have non-zero default values. Note that this + * assumes that a NULL address pointer has a value of binary 0 + * (this is also assumed at other places within the code, i.e. + * there are instances of "if (ptr)"). Just for possible future + * changes to this assumption, any pointers which are intended + * to be initialised to NULL are listed within a comment. + */ + + memset(cnt, 0, sizeof(struct context)); + cnt->noise=255; + cnt->lastrate=25; + + memcpy(&cnt->track, &track_template, sizeof(struct trackoptions)); + cnt->pipe=-1; + cnt->mpipe=-1; + +// /* Here are the pointers required to be set */ +// #ifdef HAVE_MYSQL +// cnt->database=NULL; +// #endif /* HAVE_MYSQL */ +// +// #ifdef HAVE_PGSQL +// cnt->database_pg=NULL; +// #endif /* HAVE_PGSQL */ +// +// #ifdef HAVE_FFMPEG +// cnt->ffmpeg_new=NULL; +// cnt->ffmpeg_motion=NULL; +// cnt->ffmpeg_timelapse=NULL; +// #endif /* HAVE_FFMPEG */ + +} + +/** + * context_destroy + * + * Destroys a context struct by freeing allocated memory, calling the + * appropriate cleanup functions and finally freeing the struct itself. + * + * Parameters: + * + * cnt - the context struct to destroy + * + * Returns: nothing + */ +static void context_destroy(struct context *cnt) +{ + int j; + + if (cnt->imgs.out) + free(cnt->imgs.out); + if (cnt->imgs.ref) + free(cnt->imgs.ref); + if (cnt->imgs.image_virgin) + free(cnt->imgs.image_virgin); + if (cnt->imgs.image_ring_buffer) + free(cnt->imgs.image_ring_buffer); + 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.timestamp) + free(cnt->imgs.timestamp); + if (cnt->imgs.shotstamp) + free(cnt->imgs.shotstamp); + if (cnt->imgs.preview_buffer) + free(cnt->imgs.preview_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); + + /* Free memory allocated for config parameters */ + for (j = 0; config_params[j].param_name != NULL; j++) { + if (config_params[j].copy == copy_string) { + void **val; + val = (void *)((char *)cnt+(int)config_params[j].conf_value); + if (*val) { + free(*val); + *val = NULL; + } + } + } + + free(cnt); +} + +/** + * sig_handler + * + * Our SIGNAL-Handler. We need this to handle alarms and external signals. + */ +static void sig_handler(int signo) +{ + int i; + + switch(signo) { + case SIGALRM: + /* Somebody (maybe we ourself) wants us to make a snapshot + * This feature triggers snapshots on ALL threads that have + * snapshot_interval different from 0. + */ + if (cnt_list) { + i = -1; + while (cnt_list[++i]) { + if (cnt_list[i]->conf.snapshot_interval) { + cnt_list[i]->snapshot=1; + } + } + } + break; + case SIGUSR1: + /* Ouch! We have been hit from the outside! Someone wants us to + make a movie! */ + if (cnt_list) { + i = -1; + while (cnt_list[++i]) + cnt_list[i]->makemovie=1; + } + break; + case SIGHUP: + restart = 1; + /* Fall through, as the value of 'restart' is the only difference + * between SIGHUP and the ones below. + */ + case SIGINT: + case SIGQUIT: + case SIGTERM: + /* Somebody wants us to quit! We should better finish the actual + movie and end up! */ + if (cnt_list) { + i = -1; + while (cnt_list[++i]) { + cnt_list[i]->makemovie=1; + cnt_list[i]->finish=1; + } + } + break; + case SIGSEGV: + exit(0); + } +} + +/** + * sigchild_handler + * + * This function is a POSIX compliant replacement of the commonly used + * signal(SIGCHLD, SIG_IGN). + */ +static void sigchild_handler(int signo ATTRIBUTE_UNUSED) +{ +#ifdef WNOHANG + while (waitpid(-1, NULL, WNOHANG) > 0) {}; +#endif /* WNOHANG */ + return; +} + +/** + * motion_detected + * + * Called from 'motion_loop' when motion is detected, or when to act as if + * motion was detected (e.g. in post capture). + * + * Parameters: + * + * cnt - current thread's context struct + * diffs - number of different pixels between the reference image and the + * new image (may be zero) + * dev - video device file descriptor + * devpipe - file descriptor of still image pipe + * devmpipe - file descriptor of motion pipe + * newimg - pointer to the newly captured image + */ +static void motion_detected(struct context *cnt, int diffs, int dev, unsigned char *newimg) +{ + struct config *conf = &cnt->conf; + struct images *imgs = &cnt->imgs; + struct coord *location = &cnt->location; + + cnt->lasttime = cnt->currenttime; + + /* Take action if this is a new event */ + if (cnt->event_nr != cnt->prev_event) { + int i, tmpshots; + struct tm tmptime; + cnt->preview_max = 0; + + /* Reset prev_event number to current event and save event time + * in both time_t and struct tm format. + */ + cnt->prev_event = cnt->event_nr; + cnt->eventtime = cnt->currenttime; + localtime_r(&cnt->eventtime, cnt->eventtime_tm); + + /* Since this is a new event we create the event_text_string used for + * the %C conversion specifier. We may already need it for + * on_motion_detected_commend so it must be done now. + */ + mystrftime(cnt, cnt->text_event_string, sizeof(cnt->text_event_string), + cnt->conf.text_event, cnt->eventtime_tm, NULL, 0); + + /* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */ + event(cnt, EVENT_FIRSTMOTION, newimg, NULL, NULL, cnt->currenttime_tm); + + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Motion detected - starting event %d", cnt->event_nr); + + /* pre_capture frames are written as jpegs and to the ffmpeg film + * We store the current cnt->shots temporarily until we are done with + * the pre_capture stuff + */ + + tmpshots = cnt->shots; + + for (i=cnt->precap_cur; i < cnt->precap_nr; i++) { + localtime_r(cnt->imgs.timestamp + i, &tmptime); + cnt->shots = *(cnt->imgs.shotstamp + i); + event(cnt, EVENT_IMAGE_DETECTED, + cnt->imgs.image_ring_buffer + (cnt->imgs.size * i), NULL, NULL, &tmptime); + } + + if (cnt->precap_cur) { + localtime_r(cnt->imgs.timestamp+cnt->precap_nr, &tmptime); + cnt->shots = *(cnt->imgs.shotstamp + cnt->precap_nr); + event(cnt, EVENT_IMAGE_DETECTED, + cnt->imgs.image_ring_buffer + (cnt->imgs.size * cnt->precap_nr), + NULL, NULL, &tmptime); + } + + for (i=0; i < cnt->precap_cur-1; i++) { + localtime_r(cnt->imgs.timestamp + i, &tmptime); + cnt->shots = *(cnt->imgs.shotstamp + i); + event(cnt, EVENT_IMAGE_DETECTED, + cnt->imgs.image_ring_buffer + (cnt->imgs.size * i), + NULL, NULL, &tmptime); + } + /* If output_normal=first always capture first motion frame as preview-shot */ + if (cnt->new_img == NEWIMG_FIRST){ + cnt->preview_shot = 1; + if (cnt->locate == LOCATE_PREVIEW){ + alg_draw_location(location, imgs, imgs->width, newimg, LOCATE_NORMAL); + } + } + cnt->shots = tmpshots; + } + + /* motion_detected is called with diffs = 0 during post_capture + * and if cnt->conf.output_all is enabled. We only want to draw location + * and call EVENT_MOTION when it is a picture frame with actual motion detected. + */ + if (diffs) { + if (cnt->locate == LOCATE_ON) + alg_draw_location(location, imgs, imgs->width, newimg, LOCATE_BOTH); + + /* EVENT_MOTION triggers event_beep and on_motion_detected_command */ + event(cnt, EVENT_MOTION, NULL, NULL, NULL, cnt->currenttime_tm); + } + + /* Check for most significant preview-shot when output_normal=best */ + if (cnt->new_img == NEWIMG_BEST && diffs > cnt->preview_max) { + memcpy(cnt->imgs.preview_buffer, newimg, cnt->imgs.size); + cnt->preview_max = diffs; + if (cnt->locate == LOCATE_PREVIEW){ + alg_draw_location(location, imgs, imgs->width, cnt->imgs.preview_buffer, LOCATE_NORMAL); + } + } + + if (cnt->shots < conf->frame_limit && cnt->currenttime - cnt->lastshottime >= conf->mingap ) { + cnt->lastshottime = cnt->currenttime; + + /* Output the latest picture 'image_new' or image_out for motion picture. */ + event(cnt, EVENT_IMAGE_DETECTED, newimg, NULL, NULL, cnt->currenttime_tm); + + /* If config option webcam_motion is enabled, send the latest motion detected image + * to the webcam but only if it is not the first shot within a second. This is to + * avoid double frames since we already have sent a frame to the webcam. + * We also disable this in setup_mode. + */ + if (conf->webcam_motion && !conf->setup_mode && cnt->shots != 1) + event(cnt, EVENT_WEBCAM, newimg, NULL, NULL, cnt->currenttime_tm); + cnt->preview_shot = 0; + } + + if (cnt->track.type != 0 && diffs != 0) { + cnt->moved = track_move(cnt, dev, &cnt->location, imgs, 0); + } +} + +/** + * motion_init + * + * This routine is called from motion_loop (the main thread of the program) to do + * all of the initialisation required before starting the actual run. + * + * Parameters: + * + * cnt Pointer to the motion context structure + * + * Returns: nothing + */ +static void motion_init(struct context *cnt) +{ + int i; + FILE *picture; + + /* Store thread number in TLS. */ + pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); + + cnt->diffs = 0; + cnt->currenttime_tm = mymalloc(sizeof(struct tm)); + cnt->eventtime_tm = mymalloc(sizeof(struct tm)); + cnt->smartmask_speed = 0; + + /* We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so + * that certain code below does not run until motion has been detected the first time */ + cnt->event_nr = 1; + cnt->prev_event = 0; + + motion_log(LOG_DEBUG, 0, "Thread started"); + + if (!cnt->conf.filepath) + cnt->conf.filepath = strdup("."); + + /* 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..."); + exit(1); + } + + /* We failed to get an initial image from a network 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"); + 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; + cnt->imgs.size = cnt->conf.width * cnt->conf.height * 3 / 2; + cnt->imgs.motionsize = cnt->conf.width * cnt->conf.height; + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + } + + cnt->imgs.image_ring_buffer = mymalloc(cnt->imgs.size); + memset(cnt->imgs.image_ring_buffer, 0x80, cnt->imgs.size); /* initialize to grey */ + cnt->imgs.ref = mymalloc(cnt->imgs.size); + cnt->imgs.out = mymalloc(cnt->imgs.size); + 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(int)); + cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.labels)); + cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(cnt->imgs.labelsize)); + cnt->imgs.timestamp = mymalloc(sizeof(time_t)); + cnt->imgs.shotstamp = mymalloc(sizeof(int)); + + /* Allocate a buffer for temp. usage in some places */ + /* Only despeckle for now... */ + cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width); + + /* Now is a good time to init rotation data. Since vid_start has been + * called, we know that we have imgs.width and imgs.height. When capturing + * from a V4L device, these are copied from the corresponding conf values + * in vid_start. When capturing from a netcam, they get set in netcam_start, + * which is called from vid_start. + * + * rotate_init will set cap_width and cap_height in cnt->rotate_data. + */ + 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++) { + 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) { + motion_log(LOG_ERR, 0, "Error capturing first image"); + motion_log(-1, 0, "Thread finishing..."); + exit(1); + } + } + + /* create a reference frame */ + memcpy(cnt->imgs.ref, cnt->imgs.image_virgin, cnt->imgs.size); + +#ifndef WITHOUT_V4L +#ifndef __freebsd__ + /* open video loopback devices if enabled */ + if (cnt->conf.vidpipe) { + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Opening video loopback device for normal pictures"); + /* vid_startpipe should get the output dimensions */ + 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..."); + exit(1); + } + } + if (cnt->conf.motionvidpipe) { + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Opening video loopback device for motion pictures"); + /* vid_startpipe should get the output dimensions */ + 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..."); + exit(1); + } + } +#endif /* __freebsd__ */ +#endif /*WITHOUT_V4L*/ + +#ifdef HAVE_MYSQL + if(cnt->conf.mysql_db) { + cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL)); + mysql_init(cnt->database); + if (!mysql_real_connect(cnt->database, cnt->conf.mysql_host, cnt->conf.mysql_user, + cnt->conf.mysql_password, cnt->conf.mysql_db, 0, NULL, 0)) { + 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..."); + exit(1); + } + } +#endif /* HAVE_MYSQL */ + +#ifdef HAVE_PGSQL + if (cnt->conf.pgsql_db) { + char connstring[255]; + + /* create the connection string. + Quote the values so we can have null values (blank)*/ + snprintf(connstring, 255, + "dbname='%s' host='%s' user='%s' password='%s' port='%d'", + cnt->conf.pgsql_db, /* dbname */ + (cnt->conf.pgsql_host ? cnt->conf.pgsql_host : ""), /* host (may be blank) */ + (cnt->conf.pgsql_user ? cnt->conf.pgsql_user : ""), /* user (may be blank) */ + (cnt->conf.pgsql_password ? cnt->conf.pgsql_password : ""), /* password (may be blank) */ + cnt->conf.pgsql_port + ); + + cnt->database_pg = PQconnectdb(connstring); + 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..."); + exit(1); + } + } +#endif /* HAVE_PGSQL */ + + /* Load the mask file if any */ + if (cnt->conf.mask_file) { + if ((picture = fopen(cnt->conf.mask_file, "r"))) { + /* NOTE: The mask is expected to have the output dimensions. I.e., the mask + * applies to the already rotated image, not the capture image. Thus, use + * width and height from imgs. + */ + cnt->imgs.mask = get_pgm(picture, cnt->imgs.width, cnt->imgs.height); + fclose(picture); + } else { + motion_log(LOG_ERR, 1, "Error opening mask file %s", cnt->conf.mask_file); + /* Try to write an empty mask file to make it easier + for the user to edit it */ + put_fixed_mask(cnt, cnt->conf.mask_file); + } + if (!cnt->imgs.mask) { + motion_log(LOG_ERR, 0, "Failed to read mask image. Mask feature disabled."); + } else { + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Maskfile \"%s\" loaded.",cnt->conf.mask_file); + } + } else + cnt->imgs.mask=NULL; + + /* Always initialize smart_mask - someone could turn it on later... */ + memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); + memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); + memset(cnt->imgs.smartmask_buffer, 0, cnt->imgs.motionsize*sizeof(int)); + + /* Set noise level */ + cnt->noise = cnt->conf.noise; + + /* Set threshold value */ + cnt->threshold = cnt->conf.max_changes; + + /* Initialize webcam server if webcam port is specified to not 0 */ + if (cnt->conf.webcam_port) { + if ( webcam_init(cnt) == -1 ) { + motion_log(LOG_ERR, 1, "Problem enabling stream server"); + cnt->finish = 1; + cnt->makemovie = 0; + } + 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; +} + +/** + * motion_loop + * + * Thread function for the motion handling threads. + * + */ +static void *motion_loop(void *arg) +{ + struct context *cnt = arg; + int i, j, detecting_motion = 0; + time_t lastframetime = 0; + int postcap = 0; + int frame_buffer_size; + int smartmask_ratio = 0; + int smartmask_count = 20; + int smartmask_lastrate = 0; + int olddiffs = 0; + int text_size_factor; + int passflag = 0; + long int *rolling_average_data; + long int rolling_average_limit, required_frame_time, frame_delay, delay_time_nsec; + int rolling_frame = 0; + struct timeval tv1, tv2; + unsigned long int rolling_average, elapsedtime; + unsigned long long int timenow = 0, timebefore = 0; + unsigned char *newimg = NULL; /* Pointer to where new image is stored */ + int vid_return_code = 0; /* Return code used when calling vid_next */ + +#ifdef HAVE_FFMPEG + /* Next two variables are used for timelapse feature + * time_last_frame is set to 1 so that first comming timelapse or second=0 + * is acted upon. + */ + unsigned long int time_last_frame=1, time_current_frame; +#endif /* HAVE_FFMPEG */ + + motion_init(cnt); + + /* Initialize the double sized characters if needed. */ + if(cnt->conf.text_double) + text_size_factor = 2; + else + text_size_factor = 1; + + /* Work out expected frame rate based on config setting */ + if (cnt->conf.frame_limit) + required_frame_time = 1000000L / cnt->conf.frame_limit; + else + required_frame_time = 0; + + frame_delay = required_frame_time; + + /* + * Reserve enough space for a 10 second timing history buffer. Note that, + * if there is any problem on the allocation, mymalloc does not return. + */ + rolling_average_limit = 10 * cnt->conf.frame_limit; + rolling_average_data = mymalloc(sizeof(long int) * rolling_average_limit); + + /* Preset history buffer with expected frame rate */ + for (j=0; j< rolling_average_limit; j++) + rolling_average_data[j]=required_frame_time; + + + /* MAIN MOTION LOOP BEGINS HERE */ + /* Should go on forever... unless you bought vaporware :) */ + + while (!cnt->finish || cnt->makemovie) { + + /***** MOTION LOOP - PREPARE FOR NEW FRAME SECTION *****/ + + /* Get current time and preserver last time for frame interval calc. */ + timebefore = timenow; + gettimeofday(&tv1, NULL); + timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; + + /* since we don't have sanity checks done when options are set, + * this sanity check must go in the main loop :(, before pre_captures + * are attempted. */ + if (cnt->conf.minimum_motion_frames < 1) + cnt->conf.minimum_motion_frames = 1; + if (cnt->conf.pre_capture < 0) + cnt->conf.pre_capture = 0; + + /* Check if our buffer is still the right size + * If pre_capture or minimum_motion_frames has been changed + * via the http remote control we need to re-size the ring buffer + */ + frame_buffer_size = cnt->conf.pre_capture + cnt->conf.minimum_motion_frames - 1; + if (cnt->precap_nr != frame_buffer_size) { + /* Only decrease if at last position in new buffer */ + if (frame_buffer_size > cnt->precap_nr || frame_buffer_size == cnt->precap_cur) { + unsigned char *tmp; + time_t *tmp2; + int *tmp3; + int smallest; + smallest = (cnt->precap_nr < frame_buffer_size) ? cnt->precap_nr : frame_buffer_size; + tmp=mymalloc(cnt->imgs.size*(1+frame_buffer_size)); + tmp2=mymalloc(sizeof(time_t)*(1+frame_buffer_size)); + tmp3=mymalloc(sizeof(int)*(1+frame_buffer_size)); + memcpy(tmp, cnt->imgs.image_ring_buffer, cnt->imgs.size * (1+smallest)); + memcpy(tmp2, cnt->imgs.timestamp, sizeof(time_t) * (1+smallest)); + memcpy(tmp3, cnt->imgs.shotstamp, sizeof(int) * (1+smallest)); + free(cnt->imgs.image_ring_buffer); + free(cnt->imgs.timestamp); + free(cnt->imgs.shotstamp); + cnt->imgs.image_ring_buffer = tmp; + cnt->imgs.timestamp = tmp2; + cnt->imgs.shotstamp = tmp3; + cnt->precap_nr = frame_buffer_size; + } + } + + /* 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 + */ + localtime_r(&cnt->currenttime, cnt->currenttime_tm); + + /* If we have started on a new second we reset the shots variable + * lastrate is updated to be the number of the last frame. last rate + * is used as the ffmpeg framerate when motion is detected. + */ + if (lastframetime != cnt->currenttime) { + cnt->lastrate = cnt->shots + 1; + cnt->shots = -1; + lastframetime = cnt->currenttime; + } + + /* Increase the shots variable for each frame captured within this second */ + cnt->shots++; + + /* Store time with pre_captured image*/ + *(cnt->imgs.timestamp + cnt->precap_cur) = cnt->currenttime; + + /* Store shot number with pre_captured image*/ + *(cnt->imgs.shotstamp+cnt->precap_cur) = cnt->shots; + + /* newimg now points to the current image. With precap_cur incremented it + * will be pointing to the position in the buffer for the NEXT image frame + * not the current!!! So newimg points to current frame about to be loaded + * and the cnt->precap_cur already have been incremented to point to the + * next frame. + */ + newimg = cnt->imgs.image_ring_buffer + (cnt->imgs.size * (cnt->precap_cur++)); + + /* If we are at the end of the ring buffer go to the start */ + if (cnt->precap_cur > cnt->precap_nr) + cnt->precap_cur=0; + + + /***** MOTION LOOP - RETRY INITIALIZING NETCAM SECTION *****/ + + /* If a network camera is not available we keep on retrying every 10 seconds + * until it shows up. + */ + if (cnt->video_dev == -1 && cnt->conf.netcam_url && + 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; + cnt->video_dev = vid_start(cnt); + + /* if the netcam has different dimentions 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 " + "from what is in the config file. You should fix that"); + motion_log(LOG_ERR, 0, "Restarting Motion to reinitialize all " + "image buffers to new picture dimensions"); + kill(getpid(), 1); + break; + } + } + + + /***** MOTION LOOP - IMAGE CAPTURE SECTION *****/ + + /* Fetch next frame from camera + * If vid_next returns 0 all is well and we got a new picture + * Any non zero value is an error. + * 0 = OK, valid picture + * <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, newimg); + + if (vid_return_code == 0) { + /* If all is well reset missing_frame_counter */ + if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { + /* If we previously logged starting a grey image, now log video re-start */ + motion_log(LOG_ERR, 0, "Video signal re-acquired"); + // event for re-acquired video signal can be called here + } + cnt->missing_frame_counter = 0; + + /* save the newly captured still virgin image to a buffer + * which we will not alter with text and location graphics + */ + memcpy(cnt->imgs.image_virgin, newimg, cnt->imgs.size); + + /* If the camera is a netcam we let the camera decide the pace. + * Otherwise we will keep on adding duplicate frames. + * By resetting the timer the framerate becomes maximum the rate + * of the Netcam. + */ + if (cnt->conf.netcam_url) { + gettimeofday(&tv1, NULL); + timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; + } + } 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; + } else { /* Non fatal errors */ + if (debug_level) + motion_log(-1, 0, "vid_return_code %d", vid_return_code); + + /* Netcams that change dimensions while Motion is running will + * require that Motion restarts to reinitialize all the many + * buffers inside Motion. It will be a mess to try and recover any + * other way + */ + if (vid_return_code == NETCAM_RESTART_ERROR) { + motion_log(LOG_ERR, 0, "Restarting Motion to reinitialize all " + "image buffers"); + kill(getpid(), 1); + break; + } + + /* First missed frame - store timestamp */ + if (!cnt->missing_frame_counter) + 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 + * Note: at low_cpu the timeout will be longer but we live with that + * 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)) { + memcpy(newimg, cnt->imgs.image_virgin, cnt->imgs.size); + + } else { + char tmpout[80]; + char tmpin[] = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T"; + struct tm tmptime; + localtime_r(&cnt->connectionlosttime, &tmptime); + memset(newimg, 0x80, cnt->imgs.size); + mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tmptime, NULL, 0); + draw_text(newimg, 10, 20 * text_size_factor, cnt->imgs.width, + tmpout, cnt->conf.text_double); + + /* Write error message only once */ + 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 + } + } + } + } + + + /***** MOTION LOOP - MOTION DETECTION SECTION *****/ + + /* The actual motion detection takes place in the following + * diffs is the number of pixels detected as changed + * Make a differences picture in image_out + * + * alg_diff_standard is the slower full feature motion detection algorithm + * alg_diff first calls a fast detection algorithm which only looks at a + * fraction of the pixels. If this detects possible motion alg_diff_standard + * is called. + */ + if (cnt->threshold && !cnt->pause) { + /* if we've already detected motion and we want to see if there's + * still motion, don't bother trying the fast one first. IF there's + * motion, the alg_diff will trigger alg_diff_standard + * anyway + */ + if (detecting_motion || cnt->conf.setup_mode) + cnt->diffs = alg_diff_standard(cnt, cnt->imgs.image_virgin); + else + cnt->diffs = alg_diff(cnt, cnt->imgs.image_virgin); + + /* 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. + */ + if (cnt->conf.lightswitch) { + if (alg_lightswitch(cnt, cnt->diffs)) { + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Lightswitch detected"); + if (cnt->moved < 5) + cnt->moved = 5; + cnt->diffs = 0; + } + } + + /* Switchfilter feature tries to detect a change in the video signal + * from one camera to the next. This is normally used in the Round + * Robin feature. The algoritm is not very safe. + * The algoritm takes a little time so we only call it when needed + * ie. when feature is enabled and diffs>threshold. + * We do not suspend motion detection like we did for lightswith + * because with Round Robin this is controlled by roundrobin_skip. + */ + if (cnt->conf.switchfilter && cnt->diffs > cnt->threshold) { + cnt->diffs = alg_switchfilter(cnt, cnt->diffs, newimg); + if (cnt->diffs <= cnt->threshold) { + cnt->diffs = 0; + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Switchfilter detected"); + } + } + + /* Despeckle feature + * First we run (as given by the despeckle option iterations + * of erode and dilate algoritms. + * Finally we run the labelling feature. + * All this is done in the alg_despeckle code. + */ + cnt->imgs.total_labels = 0; + cnt->imgs.largest_label = 0; + olddiffs = 0; + if (cnt->conf.despeckle && cnt->diffs > 0) { + olddiffs = cnt->diffs; + cnt->diffs = alg_despeckle(cnt, olddiffs); + } + } else if (!cnt->conf.setup_mode) + cnt->diffs = 0; + + /* Manipulate smart_mask sensitivity (only every smartmask_ratio seconds) */ + if (cnt->smartmask_speed) { + if (!--smartmask_count){ + alg_tune_smartmask(cnt); + smartmask_count = smartmask_ratio; + } + } + + /* cnt->moved is set by the tracking code when camera has been asked to move. + * When camera is moving we do not want motion to detect motion or we will + * get our camera chasing itself like crazy and we will get motion detected + * which is not really motion. So we pretend there is no motion by setting + * cnt->diffs = 0. + * We also pretend to have a moving camera when we start Motion and when light + * switch has been detected to allow camera to settle. + */ + if (cnt->moved) { + cnt->moved--; + cnt->diffs = 0; + } + + + /***** MOTION LOOP - TUNING SECTION *****/ + + /* if noise tuning was selected, do it now. but only when + * no frames have been recorded and only once per second + */ + if (cnt->conf.noise_tune && cnt->shots == 0) { + if (!detecting_motion && (cnt->diffs <= cnt->threshold)) + alg_noise_tune(cnt, cnt->imgs.image_virgin); + } + + /* if we are not noise tuning lets make sure that remote controlled + * changes of noise_level are used. + */ + if (!cnt->conf.noise_tune) + cnt->noise = cnt->conf.noise; + + /* threshold tuning if enabled + * if we are not threshold tuning lets make sure that remote controlled + * changes of threshold are used. + */ + if (cnt->conf.threshold_tune) + alg_threshold_tune(cnt, cnt->diffs, detecting_motion); + else + cnt->threshold = cnt->conf.max_changes; + + + /***** MOTION LOOP - TEXT AND GRAPHICS OVERLAY SECTION *****/ + + /* Some overlays on top of the motion image + * Note that these now modifies the cnt->imgs.out so this buffer + * can no longer be used for motion detection features until next + * picture frame is captured. + */ + + /* Fixed mask overlay */ + if (cnt->imgs.mask && (cnt->conf.motion_img || cnt->conf.ffmpeg_cap_motion || cnt->conf.setup_mode) ) + overlay_fixed_mask(cnt, cnt->imgs.out); + + /* Smartmask overlay */ + if (cnt->smartmask_speed && (cnt->conf.motion_img || cnt->conf.ffmpeg_cap_motion || cnt->conf.setup_mode) ) + overlay_smartmask(cnt, cnt->imgs.out); + + /* Largest labels overlay */ + if (cnt->imgs.largest_label && (cnt->conf.motion_img || cnt->conf.ffmpeg_cap_motion || cnt->conf.setup_mode) ) + overlay_largest_label(cnt, cnt->imgs.out); + + /* If motion is detected (cnt->diffs > cnt->threshold) and before we add text to the pictures + we find the center and size coordinates of the motion to be used for text overlays and later + for adding the locate rectangle */ + if (cnt->diffs > cnt->threshold) + alg_locate_center_size(&cnt->imgs, cnt->imgs.width, cnt->imgs.height, &cnt->location); + + /* Initialize the double sized characters if needed. */ + if(cnt->conf.text_double && text_size_factor == 1) + text_size_factor = 2; + + /* If text_double is set to off, then reset the scaling text_size_factor. */ + else if(!cnt->conf.text_double && text_size_factor == 2) { + text_size_factor = 1; + } + + /* Add changed pixels in upper right corner of the pictures */ + if (cnt->conf.text_changes) { + char tmp[15]; + sprintf(tmp, "%d", cnt->diffs); + draw_text(newimg, cnt->imgs.width - 10, 10, cnt->imgs.width, tmp, cnt->conf.text_double); + } + + /* Add changed pixels to motion-images (for webcam) in setup_mode + and always overlay smartmask (not only when motion is detected) */ + if (cnt->conf.setup_mode) { + char tmp[PATH_MAX]; + sprintf(tmp, "D:%5d L:%3d N:%3d", cnt->diffs, cnt->imgs.total_labels, cnt->noise); + draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 30 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + sprintf(tmp, "THREAD %d SETUP", cnt->threadnr); + draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + + /* Add text in lower left corner of the pictures */ + if (cnt->conf.text_left) { + char tmp[PATH_MAX]; + mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_left, cnt->currenttime_tm, NULL, 0); + draw_text(newimg, 10, cnt->imgs.height - 10 * text_size_factor, cnt->imgs.width, + tmp, cnt->conf.text_double); + } + + /* Add text in lower right corner of the pictures */ + if (cnt->conf.text_right) { + char tmp[PATH_MAX]; + mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_right, cnt->currenttime_tm, NULL, 0); + draw_text(newimg, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + + + /***** MOTION LOOP - ACTIONS AND EVENT CONTROL SECTION *****/ + + /* If motion has been detected we take action and start saving + * pictures and movies etc by calling motion_detected(). + * Is output_all enabled we always call motion_detected() + * If post_capture is enabled we also take care of this in the this + * code section. + */ + if (cnt->conf.output_all) { + detecting_motion = 1; + motion_detected(cnt, 0, cnt->video_dev, newimg); + } else if (cnt->diffs > cnt->threshold) { + /* Did we detect motion (like the cat just walked in :) )? + * If so, ensure the motion is sustained if minimum_motion_frames + * is set, and take action by calling motion_detected(). + * pre_capture is handled by motion_detected(), and we handle + * post_capture here. */ + if (!detecting_motion) + detecting_motion = 1; + + detecting_motion++; + + if (detecting_motion > cnt->conf.minimum_motion_frames) { + motion_detected(cnt, cnt->diffs, cnt->video_dev, newimg); + postcap = cnt->conf.post_capture; + } + } else if (postcap) { + motion_detected(cnt, 0, cnt->video_dev, newimg); + postcap--; + } else { + detecting_motion = 0; + } + + /* Is the mpeg movie to long? Then make movies + * First test for max mpegtime + */ + if (cnt->conf.maxmpegtime && cnt->event_nr == cnt->prev_event) + if (cnt->currenttime - cnt->eventtime >= cnt->conf.maxmpegtime) + cnt->makemovie = 1; + + /* Now test for quiet longer than 'gap' OR make movie as decided in + * previous statement. + */ + if (((cnt->currenttime - cnt->lasttime >= cnt->conf.gap) && cnt->conf.gap > 0) || cnt->makemovie) { + if (cnt->event_nr == cnt->prev_event || cnt->makemovie) { + + /* When output_normal=best save best preview_shot here at the end of event */ + if (cnt->new_img == NEWIMG_BEST && cnt->preview_max) { + preview_best(cnt); + cnt->preview_max = 0; + } + + event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, cnt->currenttime_tm); + + /* if tracking is enabled we center our camera so it does not + * point to a place where it will miss the next action + */ + if (cnt->track.type) + cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); + + if (cnt->conf.setup_mode) + motion_log(-1, 0, "End of event %d", cnt->event_nr); + + cnt->makemovie = 0; + + /* Finally we increase the event number */ + cnt->event_nr++; + + /* And we unset the text_event_string to avoid that buffered + * images get a timestamp from previous event. + */ + cnt->text_event_string[0] = '\0'; + } + } + + + /***** MOTION LOOP - REFERENCE FRAME SECTION *****/ + + /* Update reference frame */ + if (cnt->moved && (cnt->track.type || cnt->conf.lightswitch)) { + /* Prevent the motion created by moving camera or sudden light intensity + * being detected by creating a fresh reference frame + */ + memcpy(cnt->imgs.ref, cnt->imgs.image_virgin, cnt->imgs.size); + } else if (cnt->threshold) { + /* Old image slowly decays, this will make it even harder on + * a slow moving object to stay undetected + */ + unsigned char *imgs_ref_ptr = cnt->imgs.ref; + unsigned char *newimg_ptr = cnt->imgs.image_virgin; + for (i=cnt->imgs.size-1; i>=0; i--) { + *imgs_ref_ptr = (*imgs_ref_ptr + *newimg_ptr)/2; + imgs_ref_ptr++; + newimg_ptr++; + } + } + + + /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/ + + /* If setup_mode enabled output some numbers to console */ + if (cnt->conf.setup_mode){ + char msg[1024] = "\0"; + char part[100]; + + if (cnt->conf.despeckle) { + snprintf(part, 99, "Raw changes: %5d - changes after '%s': %5d", + olddiffs, cnt->conf.despeckle, cnt->diffs); + strcat(msg, part); + if (strchr(cnt->conf.despeckle, 'l')){ + sprintf(part, " - labels: %3d", cnt->imgs.total_labels); + strcat(msg, part); + } + } + else{ + sprintf(part, "Changes: %5d", cnt->diffs); + strcat(msg, part); + } + if (cnt->conf.noise_tune){ + sprintf(part, " - noise level: %2d", cnt->noise); + strcat(msg, part); + } + if (cnt->conf.threshold_tune){ + sprintf(part, " - threshold: %d", cnt->threshold); + strcat(msg, part); + } + motion_log(-1, 0, "%s", msg); + } + + + /***** MOTION LOOP - SNAPSHOT FEATURE SECTION *****/ + + /* Did we get triggered to make a snapshot from control http? Then shoot a snap + * If snapshot_interval is not zero and time since epoch MOD snapshot_interval = 0 then snap + * Note: Negative value means SIGALRM snaps are enabled + * httpd-control snaps are always enabled. + */ + if ( (cnt->conf.snapshot_interval > 0 && cnt->shots==0 && + cnt->currenttime % cnt->conf.snapshot_interval == 0) || + cnt->snapshot) { + event(cnt, EVENT_IMAGE_SNAPSHOT, newimg, NULL, NULL, cnt->currenttime_tm); + cnt->snapshot = 0; + } + + + /***** MOTION LOOP - TIMELAPSE FEATURE SECTION *****/ + +#ifdef HAVE_FFMPEG + + time_current_frame = cnt->currenttime; + + if (cnt->conf.timelapse) { + + /* Check to see if we should start a new timelapse file. We start one when + * we are on the first shot, and and the seconds are zero. We must use the seconds + * to prevent the timelapse file from getting reset multiple times during the minute. + */ + if (cnt->currenttime_tm->tm_min == 0 && + (time_current_frame % 60 < time_last_frame % 60) && + cnt->shots == 0) { + + if (strcasecmp(cnt->conf.timelapse_mode,"manual") == 0) + ;/* No action */ + + /* If we are daily, raise timelapseend event at midnight */ + else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) { + if (cnt->currenttime_tm->tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + } + + /* handle the hourly case */ + else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) { + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + } + + /* If we are weekly-sunday, raise timelapseend event at midnight on sunday */ + else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-sunday") == 0) { + if (cnt->currenttime_tm->tm_wday == 0 && cnt->currenttime_tm->tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + } + + /* If we are weekly-monday, raise timelapseend event at midnight on monday */ + else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-monday") == 0) { + if (cnt->currenttime_tm->tm_wday == 1 && cnt->currenttime_tm->tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + } + + /* If we are monthly, raise timelapseend event at midnight on first day of month */ + else if (strcasecmp(cnt->conf.timelapse_mode, "monthly") == 0) { + if (cnt->currenttime_tm->tm_mday == 1 && cnt->currenttime_tm->tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + } + + /* If invalid we report in syslog once and continue in manual mode */ + else { + motion_log(LOG_ERR, 0, "Invalid timelapse_mode argument '%s'", + cnt->conf.timelapse_mode); + motion_log(LOG_ERR, 0, "Defaulting to manual timelapse mode"); + conf_cmdparse(&cnt, (char *)"ffmpeg_timelapse_mode",(char *)"manual"); + } + } + + /* If ffmpeg timelapse is enabled and time since epoch MOD ffmpeg_timelaps = 0 + * add a timelapse frame to the timelapse mpeg. + */ + if (cnt->shots == 0 && + time_current_frame % cnt->conf.timelapse <= time_last_frame % cnt->conf.timelapse) + event(cnt, EVENT_TIMELAPSE, newimg, NULL, NULL, cnt->currenttime_tm); + } + + /* if timelapse mpeg is in progress but conf.timelapse is zero then close timelapse file + * This is an important feature that allows manual roll-over of timelapse file using xmlrpc + * via a cron job + */ + else if (cnt->ffmpeg_timelapse) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + + time_last_frame = time_current_frame; + +#endif /* HAVE_FFMPEG */ + + + /***** MOTION LOOP - VIDEO LOOPBACK SECTION *****/ + + /* feed last image and motion image to video device pipes and the webcam clients + * In setup mode we send the special setup mode image to both webcam and vloopback pipe + * In normal mode we feed the latest image to vloopback device and we send + * the image to the webcam. We always send the first image in a second to the webcam. + * Other image are sent only when the config option webcam_motion is off + * The result is that with webcam_motion on the webcam stream is normally at the minimal + * 1 frame per second but the minute motion is detected the motion_detected() function + * sends all detected pictures to the webcam except the 1st per second which is already sent. + */ + if (cnt->conf.setup_mode) { + event(cnt, EVENT_IMAGE, cnt->imgs.out, NULL, &cnt->pipe, cnt->currenttime_tm); + event(cnt, EVENT_WEBCAM, cnt->imgs.out, NULL, NULL, cnt->currenttime_tm); + } else { + event(cnt, EVENT_IMAGE, newimg, NULL, &cnt->pipe, cnt->currenttime_tm); + if (!cnt->conf.webcam_motion || cnt->shots == 1) + event(cnt, EVENT_WEBCAM, newimg, NULL, NULL, cnt->currenttime_tm); + } + + event(cnt, EVENT_IMAGEM, cnt->imgs.out, NULL, &cnt->mpipe, cnt->currenttime_tm); + + + /***** MOTION LOOP - ONCE PER SECOND PARAMETER UPDATE SECTION *****/ + + /* Check for some config parameter changes but only every second */ + if (cnt->shots == 0){ + if (strcasecmp(cnt->conf.output_normal, "on") == 0) + cnt->new_img=NEWIMG_ON; + else if (strcasecmp(cnt->conf.output_normal, "first") == 0) + cnt->new_img=NEWIMG_FIRST; + else if (strcasecmp(cnt->conf.output_normal, "best") == 0){ + cnt->new_img=NEWIMG_BEST; + /* alocate buffer here when not yet done */ + if (!cnt->imgs.preview_buffer){ + cnt->imgs.preview_buffer = mymalloc(cnt->imgs.size); + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Preview buffer allocated"); + } + } + else + cnt->new_img = NEWIMG_OFF; + + if (strcasecmp(cnt->conf.locate, "on") == 0) + cnt->locate = LOCATE_ON; + else if (strcasecmp(cnt->conf.locate, "preview") == 0) + cnt->locate = LOCATE_PREVIEW; + else + cnt->locate = LOCATE_OFF; + + /* Sanity check for smart_mask_speed, silly value disables smart mask */ + if (cnt->conf.smart_mask_speed < 0 || cnt->conf.smart_mask_speed > 10) + cnt->conf.smart_mask_speed = 0; + /* Has someone changed smart_mask_speed or framerate? */ + if (cnt->conf.smart_mask_speed != cnt->smartmask_speed || smartmask_lastrate != cnt->lastrate){ + if (cnt->conf.smart_mask_speed == 0){ + memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); + memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); + } + smartmask_lastrate = cnt->lastrate; + cnt->smartmask_speed = cnt->conf.smart_mask_speed; + /* Decay delay - based on smart_mask_speed (framerate independent) + This is always 5*smartmask_speed seconds */ + smartmask_ratio = 5 * cnt->lastrate * (11 - cnt->smartmask_speed); + } + +#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) + /* Set the sql mask file according to the SQL config options + * We update it for every frame in case the config was updated + * via remote control. + */ + cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + + cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + + cnt->conf.sql_log_mpeg * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; +#endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) */ + + } + + + /***** MOTION LOOP - FRAMERATE TIMING AND SLEEPING SECTION *****/ + + /* Work out expected frame rate based on config setting which may + have changed from http-control */ + if (cnt->conf.frame_limit) + required_frame_time = 1000000L / cnt->conf.frame_limit; + else + required_frame_time = 0; + + /* Get latest time to calculate time taken to process video data */ + gettimeofday(&tv2, NULL); + elapsedtime = (tv2.tv_usec + 1000000L * tv2.tv_sec) - timenow; + + /* Update history buffer but ignore first pass as timebefore + variable will be inaccurate + */ + if (passflag) + rolling_average_data[rolling_frame] = timenow-timebefore; + else + passflag = 1; + + rolling_frame++; + if (rolling_frame >= rolling_average_limit) + rolling_frame = 0; + + /* Calculate 10 second average and use deviation in delay calculation */ + rolling_average = 0L; + + for (j=0; j < rolling_average_limit; j++) + rolling_average += rolling_average_data[j]; + + rolling_average /= rolling_average_limit; + frame_delay = required_frame_time-elapsedtime - (rolling_average - required_frame_time); + + if (frame_delay > 0) { + /* Apply delay to meet frame time */ + if (frame_delay > required_frame_time) + frame_delay = required_frame_time; + + /* Delay time in nanoseconds for SLEEP */ + delay_time_nsec = frame_delay * 1000; + + if (delay_time_nsec > 999999999) + delay_time_nsec = 999999999; + + /* SLEEP as defined in motion.h A safe sleep using nanosleep */ + SLEEP(0, delay_time_nsec); + } + + /* This will limit the framerate to 1 frame while not detecting + motion. Using a different motion flag to allow for multiple frames per second + */ + + if (cnt->conf.low_cpu && !detecting_motion) { + /* Recalculate remaining time to delay for a total of 1/low_cpu seconds */ + if (frame_delay + elapsedtime < (1000000L / cnt->conf.low_cpu)) { + frame_delay = (1000000L / cnt->conf.low_cpu) - frame_delay - elapsedtime; + + + /* Delay time in nanoseconds for SLEEP */ + delay_time_nsec = frame_delay * 1000; + + if (delay_time_nsec > 999999999) + delay_time_nsec = 999999999; + + /* SLEEP as defined in motion.h A safe sleep using nanosleep */ + SLEEP(0, delay_time_nsec); + + /* Correct frame times to ensure required_frame_time is maintained + * This is done by taking the time NOW and subtract the time + * required_frame_time. This way we pretend that timenow was set + * one frame ago. + */ + gettimeofday(&tv1, NULL); + timenow = tv1.tv_usec + 1000000L * tv1.tv_sec - required_frame_time; + } + } + } + + /* 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; + } + if (rolling_average_data) + free(rolling_average_data); + + 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); + + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + + pthread_exit(NULL); +} + +/** + * become_daemon + * + * Turns Motion into a daemon through forking. The parent process (i.e. the + * one initially calling this function) will exit inside this function, while + * control will be returned to the child process. Standard input/output are + * released properly, and the current directory is set to / in order to not + * lock up any file system. + * + * Parameters: + * + * cnt - current thread's context struct + * + * Returns: nothing + */ +static void become_daemon(void) +{ + int i; + struct sigaction sig_ign_action; + + /* Setup sig_ign_action */ +#ifdef SA_RESTART + sig_ign_action.sa_flags = SA_RESTART; +#else + sig_ign_action.sa_flags = 0; +#endif + sig_ign_action.sa_handler = SIG_IGN; + sigemptyset(&sig_ign_action.sa_mask); + + if (fork()) { + motion_log(-1, 0, "Motion going to daemon mode"); + exit(0); + } + + /* changing dir to root enables people to unmount a disk + without having to stop Motion */ + if (chdir("/")) { + motion_log(LOG_ERR, 1, "Could not change directory"); + } + +#ifdef __freebsd__ + setpgrp(0, getpid()); +#else + setpgrp(); +#endif /* __freebsd__ */ + + if ((i = open("/dev/tty", O_RDWR)) >= 0) { + ioctl(i, TIOCNOTTY, NULL); + close(i); + } + + setsid(); + i = open("/dev/null", O_RDONLY); + + if(i != -1) { + dup2(i, STDIN_FILENO); + close(i); + } + + i = open("/dev/null", O_WRONLY); + + if(i != -1) { + dup2(i, STDOUT_FILENO); + dup2(i, STDERR_FILENO); + close(i); + } + + sigaction(SIGTTOU, &sig_ign_action, NULL); + sigaction(SIGTTIN, &sig_ign_action, NULL); + sigaction(SIGTSTP, &sig_ign_action, NULL); +} + +/** + * cntlist_create + * + * Sets up the 'cnt_list' variable by allocating room for (and actually + * allocating) one context struct. Also loads the configuration from + * the config file(s). + * + * Parameters: + * argc - size of argv + * argv - command-line options, passed initially from 'main' + * + * Returns: nothing + */ +static void cntlist_create(int argc, char *argv[]) +{ + /* cnt_list is an array of pointers to the context structures cnt for each thread. + * First we reserve room for a pointer to thread 0's context structure + * and a NULL pointer which indicates that end of the array of pointers to + * thread context structures. + */ + cnt_list = mymalloc(sizeof(struct context *)*2); + + /* Now we reserve room for thread 0's context structure and let cnt_list[0] point to it */ + cnt_list[0] = mymalloc(sizeof(struct context)); + + /* Populate context structure with start/default values */ + context_init(cnt_list[0]); + + /* cnt_list[1] pointing to zero indicates no more thread context structures - they get added later */ + cnt_list[1] = NULL; + + /* Command line arguments are being pointed to from cnt_list[0] and we call conf_load which loads + * the config options from motion.conf, thread config files and the command line. + */ + cnt_list[0]->conf.argv = argv; + cnt_list[0]->conf.argc = argc; + cnt_list = conf_load(cnt_list); +} + +/** + * motion_shutdown + * + * Responsible for performing cleanup when Motion is shut down or restarted, + * including freeing memory for all the context structs as well as for the + * context struct list itself. + * + * Parameters: none + * + * Returns: nothing + */ +static void motion_shutdown(void) +{ + int i = -1; + while (cnt_list[++i]){ + context_destroy(cnt_list[i]); + } + free(cnt_list); +#ifndef WITHOUT_V4L + vid_close(); + vid_cleanup(); +#endif +} + +/** + * motion_startup + * + * Responsible for initializing stuff when Motion starts up or is restarted, + * including daemon initialization and creating the context struct list. + * + * Parameters: + * + * daemonize - non-zero to do daemon init (if the config parameters says so), + * or 0 to skip it + * argc - size of argv + * argv - command-line options, passed initially from 'main' + * + * Returns: nothing + */ +static void motion_startup(int daemonize, int argc, char *argv[]) +{ + /* Initialize our global mutex */ + pthread_mutex_init(&global_lock, NULL); + + /* Create the list of context structures and load the + * configuration. + */ + cntlist_create(argc, argv); + + initialize_chars(); + + if (daemonize) { + /* If daemon mode is requested, and we're not going into setup mode, + * become daemon. + */ + if (cnt_list[0]->daemon && cnt_list[0]->conf.setup_mode == 0) { + become_daemon(); + motion_log(LOG_INFO, 0, "Motion running as daemon process"); + if (cnt_list[0]->conf.low_cpu) { + motion_log(LOG_INFO, 0, "Capturing %d frames/s when idle", + cnt_list[0]->conf.low_cpu); + } + } + } + +#ifndef WITHOUT_V4L + vid_init(); +#endif +} + +/** + * setup_signals + * + * Attaches handlers to a number of signals that Motion need to catch. + * + * Parameters: sigaction structs for signals in general and SIGCHLD. + * + * Returns: nothing + */ +static void setup_signals(struct sigaction *sig_handler_action, struct sigaction *sigchild_action) +{ +#ifdef SA_NOCLDWAIT + sigchild_action->sa_flags = SA_NOCLDWAIT; +#else + sigchild_action->sa_flags = 0; +#endif + sigchild_action->sa_handler = sigchild_handler; + sigemptyset(&sigchild_action->sa_mask); +#ifdef SA_RESTART + sig_handler_action->sa_flags = SA_RESTART; +#else + sig_handler_action->sa_flags = 0; +#endif + sig_handler_action->sa_handler = sig_handler; + sigemptyset(&sig_handler_action->sa_mask); + + /* Enable automatic zombie reaping */ + sigaction(SIGCHLD, sigchild_action, NULL); + sigaction(SIGPIPE, sigchild_action, NULL); + sigaction(SIGALRM, sig_handler_action, NULL); + sigaction(SIGHUP, sig_handler_action, NULL); + sigaction(SIGINT, sig_handler_action, NULL); + sigaction(SIGQUIT, sig_handler_action, NULL); + sigaction(SIGTERM, sig_handler_action, NULL); + sigaction(SIGUSR1, sig_handler_action, NULL); +} + +/** + * main + * + * Main entry point of Motion. Launches all the motion threads and contains + * the logic for starting up, restarting and cleaning up everything. + * + * Parameters: + * + * argc - size of argv + * argv - command-line options + * + * Returns: Motion exit status = 0 always + */ +int main (int argc, char **argv) +{ + int i, j; + int webcam_port; + pthread_attr_t thread_attr; + pthread_t thread_id; + + /* Setup signals and do some initialization. 1 in the call to + * 'motion_startup' means that Motion will become a daemon if so has been + * requested, and argc and argc are necessary for reading the command + * line options. + */ + struct sigaction sig_handler_action; + struct sigaction sigchild_action; + setup_signals(&sig_handler_action, &sigchild_action); + + motion_startup(1, argc, argv); + +#ifdef HAVE_FFMPEG + /* FFMpeg initialization is only performed if FFMpeg support was found + * and not disabled during the configure phase. + */ + ffmpeg_init(); +#endif /* HAVE_FFMPEG */ + + /* In setup mode, Motion is very communicative towards the user, which + * allows the user to experiment with the config parameters in order to + * optimize motion detection and stuff. + */ + if(cnt_list[0]->conf.setup_mode) + motion_log(-1, 0, "Motion running in setup mode."); + + /* Create and a thread attribute for the threads we spawn later on. + * PTHREAD_CREATE_DETACHED means to create threads detached, i.e. + * their termination cannot be synchronized through 'pthread_join'. + */ + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + + /* Create the TLS key for thread number. */ + pthread_key_create(&tls_key_threadnr, NULL); + + do { + if (restart) { + /* Handle the restart situation. Currently the approach is to + * cleanup everything, and then initialize everything again + * (including re-reading the config file(s)). + */ + motion_shutdown(); + restart = 0; /* only one reset for now */ +#ifndef WITHOUT_V4L + SLEEP(5,0); // maybe some cameras needs less time +#endif + 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++) { + if (cnt_list[0]->conf.setup_mode) + motion_log(-1, 0, "Thread device: %s input %d", + 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 + ); + + /* 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); + cnt_list[i]->threadnr = ++threads_running; + pthread_mutex_unlock(&global_lock); + + if ( strcmp(cnt_list[i]->conf_filename,"") ) + motion_log(LOG_INFO, 0, "Thread is from %s", cnt_list[i]->conf_filename ); + + /* Create the actual thread. Use 'motion_loop' as the thread + * function. + */ + pthread_create(&thread_id, &thread_attr, &motion_loop, cnt_list[i]); + } + + /* 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.control_port) + pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list); + + if (cnt_list[0]->conf.setup_mode) + motion_log(-1, 0,"Waiting for threads to finish, pid: %d", getpid()); + + /* 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) { + SLEEP(1,0); + } + + if (cnt_list[0]->conf.setup_mode) + motion_log(LOG_DEBUG, 0, "Threads finished"); + + /* Rest for a while if we're supposed to restart. */ + if (restart) + SLEEP(2,0); + + } while (restart); /* loop if we're supposed to restart */ + + motion_log(LOG_INFO, 0, "Motion terminating"); + + /* Perform final cleanup. */ + pthread_key_delete(tls_key_threadnr); + pthread_attr_destroy(&thread_attr); + pthread_mutex_destroy(&global_lock); + motion_shutdown(); + + return 0; +} + +/** + * mymalloc + * + * Allocates some memory and checks if that succeeded or not. If it failed, + * do some errorlogging and bail out. + * + * NOTE: Kenneth Lavrsen changed printing of size_t types so instead of using + * conversion specifier %zd I changed it to %llu and casted the size_t + * variable to unsigned long long. The reason for this nonsense is that older + * versions of gcc like 2.95 uses %Zd and does not understand %zd. So to avoid + * this mess I used a more generic way. Long long should have enough bits for + * 64-bit machines with large memory areas. + * + * Parameters: + * + * nbytes - no. of bytes to allocate + * + * Returns: a pointer to the allocated memory + */ +void * mymalloc(size_t nbytes) +{ + void *dummy = malloc(nbytes); + if (!dummy) { + motion_log(LOG_EMERG, 1, "Could not allocate %llu bytes of memory!", (unsigned long long)nbytes); + exit(1); + } + + return dummy; +} + +/** + * myrealloc + * + * Re-allocate (i.e., resize) some memory and check if that succeeded or not. + * If it failed, do some errorlogging and bail out. If the new memory size + * is 0, the memory is freed. + * + * Parameters: + * + * ptr - pointer to the memory to resize/reallocate + * size - new memory size + * desc - name of the calling function + * + * Returns: a pointer to the reallocated memory, or NULL if the memory was + * freed + */ +void *myrealloc(void *ptr, size_t size, const char *desc) +{ + void *dummy = NULL; + + if (size == 0) { + free(ptr); + motion_log(LOG_WARNING, 0, + "Warning! Function %s tries to resize memoryblock at %p to 0 bytes!", + desc, ptr); + } else { + dummy = realloc(ptr, size); + if (!dummy) { + motion_log(LOG_EMERG, 0, + "Could not resize memory-block at offset %p to %llu bytes (function %s)!", + ptr, (unsigned long long)size, desc); + exit(1); + } + } + + return dummy; +} + +/** + * create_path + * + * This function creates a whole path, like mkdir -p. Example paths: + * this/is/an/example/ + * /this/is/an/example/ + * Warning: a path *must* end with a slash! + * + * Parameters: + * + * cnt - current thread's context structure (for logging) + * path - the path to create + * + * Returns: 0 on success, -1 on failure + */ +int create_path(const char *path) +{ + char *start; + mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + + if (path[0] == '/') + start = strchr(path + 1, '/'); + else + start = strchr(path, '/'); + + while(start) { + char *buffer = strdup(path); + buffer[start-path] = 0x00; + + if (mkdir(buffer, mode) == -1 && errno != EEXIST) { + motion_log(LOG_ERR, 1, "Problem creating directory %s", buffer); + free(buffer); + return -1; + } + + free(buffer); + + start = strchr(start + 1, '/'); + } + + return 0; +} + +/** + * myfopen + * + * This function opens a file, if that failed because of an ENOENT error + * (which is: path does not exist), the path is created and then things are + * tried again. This is faster then trying to create that path over and over + * again. If someone removes the path after it was created, myfopen will + * recreate the path automatically. + * + * Parameters: + * + * path - path to the file to open + * mode - open mode + * + * Returns: the file stream object + */ +FILE * myfopen(const char *path, const char *mode) +{ + /* first, just try to open the file */ + FILE *dummy = fopen(path, mode); + + /* could not open file... */ + if (!dummy) { + /* path did not exist? */ + if (errno == ENOENT) { + //DEBUG CODE syslog(LOG_DEBUG, "Could not open file %s directly; path did not exist. Creating path & retrying.", path); + + /* create path for file... */ + if (create_path(path) == -1) + return NULL; + + /* and retry opening the file */ + dummy = fopen(path, mode); + if (dummy) + return dummy; + } + + /* two possibilities + * 1: there was an other error while trying to open the file for the first time + * 2: could still not open the file after the path was created + */ + motion_log(LOG_ERR, 1, "Error opening file %s with mode %s", path, mode); + + return NULL; + } + + return dummy; +} + +/** + * mystrftime + * + * Motion-specific variant of strftime(3) that supports additional format + * specifiers in the format string. + * + * Parameters: + * + * cnt - current thread's context structure + * s - destination string + * max - max number of bytes to write + * userformat - format string + * tm - time information + * filename - string containing full path of filename + * set this to NULL if not relevant + * sqltype - Filetype as used in SQL feature, set to 0 if not relevant + * + * Returns: number of bytes written to the string s + */ +size_t mystrftime(struct context *cnt, char *s, size_t max, const char *userformat, + const struct tm *tm, const char *filename, int sqltype) +{ + char formatstring[PATH_MAX] = ""; + char tempstring[255] = ""; + char *format, *tempstr; + const char *pos_userformat; + + format = formatstring; + + /* if mystrftime is called with userformat = NULL we return a zero length string */ + if (userformat == NULL) { + *s = '\0'; + return 0; + } + + for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) { + + if (*pos_userformat == '%') { + /* Reset 'tempstr' to point to the beginning of 'tempstring', + * otherwise we will eat up tempstring if there are many + * format specifiers. + */ + tempstr = tempstring; + tempstr[0] = '\0'; + switch (*++pos_userformat) { + case '\0': // end of string + --pos_userformat; + break; + + case 'v': // event + sprintf(tempstr, "%02d", cnt->event_nr); + break; + + case 'q': // shots + sprintf(tempstr, "%02d", cnt->shots); + break; + + case 'D': // diffs + sprintf(tempstr, "%d", cnt->diffs); + break; + + case 'N': // noise + sprintf(tempstr, "%d", cnt->noise); + break; + + case 'i': // motion width + sprintf(tempstr, "%d", cnt->location.width); + break; + + case 'J': // motion height + sprintf(tempstr, "%d", cnt->location.height); + break; + + case 'K': // motion center x + sprintf(tempstr, "%d", cnt->location.x); + break; + + case 'L': // motion center y + sprintf(tempstr, "%d", cnt->location.y); + break; + + case 'o': // threshold + sprintf(tempstr, "%d", cnt->threshold); + break; + + case 'Q': // number of labels + sprintf(tempstr, "%d", cnt->imgs.total_labels); + break; + case 't': // thread number + sprintf(tempstr, "%d",(int)(unsigned long) + pthread_getspecific(tls_key_threadnr)); + break; + case 'C': // text_event + if (cnt->text_event_string && cnt->text_event_string[0]) + sprintf(tempstr, "%s", cnt->text_event_string); + else + ++pos_userformat; + break; + case 'f': // filename + if (filename) + sprintf(tempstr, "%s", filename); + else + ++pos_userformat; + break; + case 'n': // sqltype + if (sqltype) + sprintf(tempstr, "%d", sqltype); + else + ++pos_userformat; + break; + default: // Any other code is copied with the %-sign + *format++ = '%'; + *format++ = *pos_userformat; + continue; + } + + /* If a format specifier was found and used, copy the result from + * 'tempstr' to 'format'. + */ + if (tempstr[0]) { + while ((*format = *tempstr++) != '\0') + ++format; + continue; + } + } + + /* For any other character than % we just simply copy the character */ + *format++ = *pos_userformat; + } + + *format = '\0'; + format = formatstring; + + return strftime(s, max, format, tm); +} + +/** + * motion_log + * + * This routine is used for printing all informational, debug or error + * messages produced by any of the other motion functions. It always + * produces a message of the form "[n] {message}", and (if the param + * 'errno_flag' is set) follows the message with the associated error + * message from the library. + * + * Parameters: + * + * level logging level for the 'syslog' function + * (-1 implies no syslog message should be produced) + * errno_flag if set, the log message should be followed by the + * error message. + * fmt the format string for producing the message + * ap variable-length argument list + * + * Returns: + * Nothing + */ +void motion_log(int level, int errno_flag, const char *fmt, ...) +{ + int errno_save, n; + char buf[1024]; +#ifndef __freebsd__ + char msg_buf[100]; +#endif + va_list ap; + int threadnr; + + /* If pthread_getspecific fails (e.g., because the thread's TLS doesn't + * contain anything for thread number, it returns NULL which casts to zero, + * which is nice because that's what we want in that case. + */ + threadnr = (unsigned long)pthread_getspecific(tls_key_threadnr); + + /* + * First we save the current 'error' value. This is required because + * the subsequent calls to vsnprintf could conceivably change it! + */ + errno_save = errno; + + /* Prefix the message with the thread number */ + n = snprintf(buf, sizeof(buf), "[%d] ", threadnr); + + /* Next add the user's message */ + va_start(ap, fmt); + n += vsnprintf(buf + n, sizeof(buf) - n, fmt, ap); + + /* If errno_flag is set, add on the library error message */ + if (errno_flag) { + strcat(buf, ": "); + n += 2; + /* + * this is bad - apparently gcc/libc wants to use the non-standard GNU + * version of strerror_r, which doesn't actually put the message into + * my buffer :-(. I have put in a 'hack' to get around this. + */ +#ifdef __freebsd__ + strerror_r(errno_save, buf + n, sizeof(buf) - n); /* 2 for the ': ' */ +#else + strcat(buf, strerror_r(errno_save, msg_buf, sizeof(msg_buf))); +#endif + } + /* If 'level' is not negative, send the message to the syslog */ + if (level >= 0) + syslog(level, buf); + + /* For printing to stderr we need to add a newline */ + strcat(buf, "\n"); + fputs(buf, stderr); + fflush(stderr); + + /* Clean up the argument list routine */ + va_end(ap); +} + diff --git a/motion.h b/motion.h new file mode 100644 index 0000000..84b88c7 --- /dev/null +++ b/motion.h @@ -0,0 +1,342 @@ +/* motion.h + * + * Include file for motion.c + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#ifndef _INCLUDE_MOTION_H +#define _INCLUDE_MOTION_H + +/* Includes */ + +#ifdef HAVE_MYSQL +#include +#endif + +#include +#include +#include +#define __USE_GNU +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _LINUX_TIME_H 1 +#if (!defined(WITHOUT_V4L)) && (!defined (__freebsd__)) +#include +#endif + +#include + +#ifdef HAVE_PGSQL +#include +#endif + +#include "conf.h" +#include "webcam.h" +#include "webhttpd.h" + +/** + * ATTRIBUTE_UNUSED: + * + * Macro used to signal to GCC unused function parameters + */ +#ifdef __GNUC__ +#ifdef HAVE_ANSIDECL_H +#include +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif +#else +#define ATTRIBUTE_UNUSED +#endif + +/* The macro below defines a version of sleep using nanosleep + * If a signal such as SIG_CHLD interrupts the sleep we just continue sleeping + */ +#define SLEEP(seconds, nanoseconds) { \ + struct timespec tv; \ + tv.tv_sec = (seconds); \ + tv.tv_nsec = (nanoseconds); \ + while (nanosleep(&tv, &tv) == -1); \ + } + + +#if defined(WITHOUT_V4L) || defined(__freebsd__) + +#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ +#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ +#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ +#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ +#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ +#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ +#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ +#define VIDEO_PALETTE_YUYV 8 +#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ +#define VIDEO_PALETTE_YUV420 10 +#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ +#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ +#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ +#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ +#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ +#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ +#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ +#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ +#endif + +/* Default picture settings */ +#define DEF_WIDTH 352 +#define DEF_HEIGHT 288 +#define DEF_QUALITY 75 +#define DEF_CHANGES 1500 + +#define DEF_MAXFRAMERATE 100 +#define DEF_NOISELEVEL 32 + +/* Minimum time between two 'actions' (email, sms, external) */ +#define DEF_GAP 60 /* 1 minutes */ +#define DEF_MAXMPEGTIME 3600 /* 60 minutes */ + +#define DEF_FFMPEG_BPS 400000 +#define DEF_FFMPEG_VBR 0 +#define DEF_FFMPEG_CODEC "mpeg4" + +#define THRESHOLD_TUNE_LENGTH 256 + +#define MISSING_FRAMES_TIMEOUT 30 /* When failing to get picture frame from camera + * we reuse the previous frame until + * MISSING_FRAMES_TIMEOUT seconds has passed + * and then we show a grey image instead + */ + +#define DEF_MAXSTREAMS 10 /* Maximum number of webcam clients per camera */ +#define DEF_MAXWEBQUEUE 10 /* Maximum number of webcam client in queue */ + +#define DEF_TIMESTAMP "%Y-%m-%d\\n%T" +#define DEF_EVENTSTAMP "%Y%m%d%H%M%S" + +#define DEF_SNAPPATH "%v-%Y%m%d%H%M%S-snapshot" +#define DEF_JPEGPATH "%v-%Y%m%d%H%M%S-%q" +#define DEF_MPEGPATH "%v-%Y%m%d%H%M%S" +#define DEF_TIMEPATH "%Y%m%d-timelapse" + +#define DEF_TIMELAPSE_MODE "daily" + +/* Do not break this line into two or more. Must be ONE line */ +#define DEF_SQL_QUERY "sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')" + +/* Filetype defines */ +#define FTYPE_IMAGE 1 +#define FTYPE_IMAGE_SNAPSHOT 2 +#define FTYPE_IMAGE_MOTION 4 +#define FTYPE_MPEG 8 +#define FTYPE_MPEG_MOTION 16 +#define FTYPE_MPEG_TIMELAPSE 32 + +#define FTYPE_MPEG_ANY (FTYPE_MPEG | FTYPE_MPEG_MOTION | FTYPE_MPEG_TIMELAPSE) +#define FTYPE_IMAGE_ANY (FTYPE_IMAGE | FTYPE_IMAGE_SNAPSHOT | FTYPE_IMAGE_MOTION) + +#define NEWIMG_OFF 0 +#define NEWIMG_ON 1 +#define NEWIMG_FIRST 2 +#define NEWIMG_BEST 3 + +#define LOCATE_OFF 0 +#define LOCATE_ON 1 +#define LOCATE_PREVIEW 2 + +#define LOCATE_NORMAL 0 +#define LOCATE_BOTH 1 + +/* DIFFERENCES BETWEEN imgs.width, conf.width AND rotate_data.cap_width + * (and the corresponding height values, of course) + * =========================================================================== + * Location Purpose + * + * conf The values in conf reflect width and height set in the + * configuration file. These can be set via xmlrpc, but they are + * not used internally by Motion, so it won't break anything. + * These values are transferred to imgs in vid_start. + * + * imgs The values in imgs are the actual output dimensions. Normally + * the output dimensions are the same as the capture dimensions, + * but for 90 or 270 degrees rotation, they are not. E.g., if + * you capture at 320x240, and rotate 90 degrees, the output + * dimensions are 240x320. + * These values are set from the conf values in vid_start, or + * from the first JPEG image in netcam_start. For 90 or 270 + * degrees rotation, they are swapped in rotate_init. + * + * rotate_data The values in rotate_data are named cap_width and cap_height, + * and contain the capture dimensions. The difference between + * capture and output dimensions is explained above. + * These values are set in rotate_init. + */ + +/* date/time drawing, draw.c */ +int draw_text (unsigned char *image, int startx, int starty, int width, char *text, int factor); +int initialize_chars(void); + +struct images { + unsigned char *image_ring_buffer; /* The base address of the image ring buffer */ + unsigned char *ref; /* The reference frame */ + unsigned char *out; /* Picture buffer for motion images */ + unsigned char *image_virgin; /* Last picture frame with no text or locate overlay */ + unsigned char *preview_buffer; /* Picture buffer for best image when enables */ + unsigned char *mask; /* Buffer for the mask file */ + unsigned char *smartmask; + unsigned char *smartmask_final; + unsigned char *common_buffer; + int *smartmask_buffer; + int *labels; + int *labelsize; + int width; + int height; + int type; + int size; + int motionsize; + int total_labels; + int labelsize_max; + int labelgroup_max; + int labels_above; + int largest_label; + time_t *timestamp; + int *shotstamp; +}; + +/* Contains data for image rotation, see rotate.c. */ +struct rotdata { + /* Temporary buffer for 90 and 270 degrees rotation. */ + unsigned char *temp_buf; + /* Degrees to rotate; copied from conf.rotate_deg. This is the value + * that is actually used. The value of conf.rotate_deg cannot be used + * because it can be changed by motion-control, and changing rotation + * while Motion is running just causes problems. + */ + int degrees; + /* Capture width and height - different from output width and height if + * rotating 90 or 270 degrees. */ + int cap_width; + int cap_height; +}; + +#include "track.h" + +#include "netcam.h" + + + +/* + these used to be global variables but now each thread will have its + own context + */ +struct context { + char conf_filename[PATH_MAX]; + int threadnr; + int daemon; + + struct config conf; + struct images imgs; + struct trackoptions track; + struct netcam_context *netcam; + int precap_nr; + int precap_cur; + int new_img; + int preview_shot; + int preview_max; /* Stores max diff number seen in an event of output_normal==best */ + int locate; + struct coord location; /* coordinates for center and size of last motion detection*/ + struct rotdata rotate_data; /* rotation data is thread-specific */ + + int diffs; + int noise; + int threshold; + int diffs_last[THRESHOLD_TUNE_LENGTH]; + int smartmask_speed; + + int snapshot; + int makemovie; + int finish; + + int event_nr; + int prev_event; + char text_event_string[255]; /* The text for conv. spec. %C - we assume 255 characters is fine */ + + int shots; + struct tm *currenttime_tm; + struct tm *eventtime_tm; + + time_t currenttime; + time_t lasttime; + time_t eventtime; + time_t lastshottime; + time_t connectionlosttime; /* timestamp from connection lost */ + + int lastrate; + int moved; + int switched; + int pause; + int missing_frame_counter; /* counts failed attempts to fetch picture frame from camera */ + +#ifdef __freebsd__ + int tuner_dev; +#endif + int video_dev; + int pipe; + int mpipe; + + struct webcam webcam; + int stream_count; + +#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) + int sql_mask; +#endif + +#ifdef HAVE_MYSQL + MYSQL *database; +#endif + +#ifdef HAVE_PGSQL + PGconn *database_pg; +#endif + +#ifdef HAVE_FFMPEG + struct ffmpeg *ffmpeg_new; + struct ffmpeg *ffmpeg_motion; + struct ffmpeg *ffmpeg_timelapse; + struct ffmpeg *ffmpeg_smartmask; + char newfilename[PATH_MAX]; + char motionfilename[PATH_MAX]; + char timelapsefilename[PATH_MAX]; +#endif +}; + +extern pthread_mutex_t global_lock; +extern volatile int threads_running; +extern int debug_level; + +/* TLS keys below */ +extern pthread_key_t tls_key_threadnr; /* key for thread number */ + +int http_bindsock(int, int); +void * mymalloc(size_t); +void * myrealloc(void *, size_t, const char *); +FILE * myfopen(const char *, const char *); +size_t mystrftime(struct context *, char *, size_t, const char *, const struct tm *, const char *, int); +int create_path(const char *); +void motion_log(int, int, const char *, ...); +#endif /* _INCLUDE_MOTION_H */ diff --git a/motion.init-Debian.in b/motion.init-Debian.in new file mode 100644 index 0000000..0883ab0 --- /dev/null +++ b/motion.init-Debian.in @@ -0,0 +1,72 @@ +#! /bin/bash +# +# motion Start the motion detection . +# + +NAME=motion +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DAEMON=@BIN_PATH@/motion +PIDFILE=/var/run/$NAME.pid + + +trap "" 1 +export LANG=C +export PATH + +test -f $DAEMON || exit 0 + + +case "$1" in + start) + echo "Starting motion detection : $NAME" + start-stop-daemon --start --make-pidfile --pidfile $PIDFILE --exec $DAEMON + echo `ps axf | grep -v grep | grep $DAEMON | head -n1 | awk '{print $1}'` > $PIDFILE + ;; + + stop) + echo "Stopping motion detection : $NAME" + start-stop-daemon --stop --pidfile $PIDFILE --oknodo --exec $DAEMON --retry 30 + rm -rf $PIDFILE 2> /dev/null + ;; + + status) + echo "Status motion detection : $NAME" + if (test -f $PIDFILE); then + echo -n "Running process for $NAME : " + pidof $NAME + else + echo "Stopped" + fi + ;; + + reload-config) + echo "Reloading $NAME configuration" + start-stop-daemon --stop --pidfile $PIDFILE --signal HUP --exec $DAEMON + ;; + + restart-motion) + echo "Restarting $NAME" + start-stop-daemon --stop --pidfile $PIDFILE --oknodo --exec $DAEMON --retry 30 + rm -rf $PIDFILE 2> /dev/null + start-stop-daemon --start --make-pidfile --pidfile $PIDFILE --exec $DAEMON + echo `ps axf | grep -v grep | grep $DAEMON | head -n1 | awk '{print $1}'` > $PIDFILE + ;; + + restart) + $0 restart-motion + exit $? + ;; + + *) + echo "Usage: /etc/init.d/$NAME {start|stop|status|reload-config|restart}" + exit 1 + ;; +esac + +if [ $? == 0 ]; then + echo . + exit 0 +else + echo failed + exit 1 +fi diff --git a/motion.init-FreeBSD.sh.in b/motion.init-FreeBSD.sh.in new file mode 100644 index 0000000..89021ee --- /dev/null +++ b/motion.init-FreeBSD.sh.in @@ -0,0 +1,30 @@ +#!/bin/sh + +NAME=motion +DAEMON=@BIN_PATH@/motion +PIDFILE=/var/run/$NAME.pid + +case "$1" in +start) + if [ -x $DAEMON ] ; then + $DAEMON && > /dev/null && echo -n ' Starting motion' + echo `ps axf | grep -v grep | grep $DAEMON | head -n1 | awk '{print $1}'` > $PIDFILE + else + echo "Daemon not found" + echo + fi + ;; +stop) + if [ -r $PIDFILE ] ; then + kill -15 `cat $PIDFILE` && rm $PIDFILE > /dev/null && echo -n ' Stopping motion' + else + echo "pidfile doesn't exist or motion stopped" + echo + fi + ;; +*) + echo "Usage: `basename $0` {start|stop}" >&2 + ;; +esac + +exit 0 diff --git a/motion.init-RH.in b/motion.init-RH.in new file mode 100644 index 0000000..6c4fef6 --- /dev/null +++ b/motion.init-RH.in @@ -0,0 +1,84 @@ +#!/bin/bash +# +# Startup script for the Motion Detection System +# +# chkconfig: - 85 15 +# description: Motion Detection System. It is used to detect \ +# movement based on compare images. +# processname: motion +# pidfile: /var/run/motion.pid +# config: /etc/motion.conf + +# Source function library. +. /etc/rc.d/init.d/functions + + + +motion=${MOTION-@BIN_PATH@/motion} +prog=motion +PIDFILE=/var/run/motion.pid +RETVAL=0 + + +start() { + echo -n $"Starting $prog: " + daemon $motion + RETVAL=$? + echo `ps axf | grep -v grep | grep $motion | head -n1 | awk '{print $1}'` > $PIDFILE + echo + [ $RETVAL = 0 ] && touch /var/lock/subsys/motion + return $RETVAL +} +stop() { + echo -n $"Stopping $prog: " + killproc $motion + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f /var/lock/subsys/motion /var/run/motion.pid +} +stopsafe() { + echo -n $"Stopping $prog: ( for restarting ) " + killproc $motion + RETVAL=$? + sleep 10s + echo + [ $RETVAL = 0 ] && rm -f /var/lock/subsys/motion /var/run/motion.pid +} +reload() { + echo -n $"Reloading $prog: " + killproc $motion -HUP + RETVAL=$? + echo +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status $motion + RETVAL=$? + ;; + restart) + stopsafe + start + ;; + condrestart) + if [ -f /var/run/motion.pid ] ; then + stop + start + fi + ;; + reload) + reload + ;; + *) + echo $"Usage: $prog {start|stop|restart|condrestart|reload|status}" + exit 1 +esac + +exit $RETVAL diff --git a/motion.spec b/motion.spec new file mode 100644 index 0000000..a2b2d5c --- /dev/null +++ b/motion.spec @@ -0,0 +1,63 @@ +Name: motion +Version: 3.2.5 +Release: 1 +Summary: MOTION, a Video-surveilance-system + +Group: Applications/Multimedia +License: GPL +URL: http://motion.sourceforge.net/ +Source0: http://prdownloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildRequires: libjpeg-devel ffmpeg-devel +BuildRequires: postgresql-devel mysql-devel + +%description +Motion is a software motion detector. It grabs images from video4linux +devices and/or from webcams (such as the axis network cameras). Motion +is the perfect tool for keeping an eye on your property keeping only +those images that are interesting. Motion is strictly command line +driven and can run as a daemon with a rather small footprint. It is +built with MySQL and PostgreSQL support and mpegs generated by ffmpeg +and http remote control. + +%prep + +%setup -q + +%build + +%configure --sysconfdir=%{_sysconfdir}/%{name} \ + --without-optimizecpu \ + --without-libjpeg-mmx + +make %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT motion.init +make install DESTDIR=$RPM_BUILD_ROOT + +(cd $RPM_BUILD_ROOT%{_sysconfdir}/%{name} ; mv motion-dist.conf motion.conf) + +sed -e 's#MOTION-/usr/#MOTION-#g' motion.init-RH > motion.init +install -D -m 0755 motion.init $RPM_BUILD_ROOT%{_initrddir}/%{name} + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr (-,root,root,-) +%doc CHANGELOG COPYING CREDITS INSTALL README motion_guide.html +%doc motion-dist.conf thread1.conf thread2.conf thread3.conf thread4.conf +%dir %{_sysconfdir}/%{name} +%config %{_sysconfdir}/%{name}/motion.conf +%{_bindir}/motion +%{_mandir}/man1/motion.1* +%{_initrddir}/%{name} + + +%changelog +* Sun Sep 18 2005 Kenneth Lavrsen - 3.2.4-1 +- Generic version of livna spec file replacing the old less optimal specfile. + +* Thu Sep 15 2005 Dams - 3.2.3-0.lvn.1 +- Initial released based upon upstream spec file diff --git a/motion.spec.in b/motion.spec.in new file mode 100644 index 0000000..49fd3c1 --- /dev/null +++ b/motion.spec.in @@ -0,0 +1,63 @@ +Name: motion +Version: @VERSION@ +Release: 1 +Summary: MOTION, a Video-surveilance-system + +Group: Applications/Multimedia +License: GPL +URL: http://motion.sourceforge.net/ +Source0: http://prdownloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildRequires: libjpeg-devel ffmpeg-devel +BuildRequires: postgresql-devel mysql-devel + +%description +Motion is a software motion detector. It grabs images from video4linux +devices and/or from webcams (such as the axis network cameras). Motion +is the perfect tool for keeping an eye on your property keeping only +those images that are interesting. Motion is strictly command line +driven and can run as a daemon with a rather small footprint. It is +built with MySQL and PostgreSQL support and mpegs generated by ffmpeg +and http remote control. + +%prep + +%setup -q + +%build + +%configure --sysconfdir=%{_sysconfdir}/%{name} \ + --without-optimizecpu \ + --without-libjpeg-mmx + +make %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT motion.init +make install DESTDIR=$RPM_BUILD_ROOT + +(cd $RPM_BUILD_ROOT%{_sysconfdir}/%{name} ; mv motion-dist.conf motion.conf) + +sed -e 's#MOTION-/usr/#MOTION-#g' motion.init-RH > motion.init +install -D -m 0755 motion.init $RPM_BUILD_ROOT%{_initrddir}/%{name} + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr (-,root,root,-) +%doc CHANGELOG COPYING CREDITS INSTALL README motion_guide.html +%doc motion-dist.conf thread1.conf thread2.conf thread3.conf thread4.conf +%dir %{_sysconfdir}/%{name} +%config %{_sysconfdir}/%{name}/motion.conf +%{_bindir}/motion +%{_mandir}/man1/motion.1* +%{_initrddir}/%{name} + + +%changelog +* Sun Sep 18 2005 Kenneth Lavrsen - 3.2.4-1 +- Generic version of livna spec file replacing the old less optimal specfile. + +* Thu Sep 15 2005 Dams - 3.2.3-0.lvn.1 +- Initial released based upon upstream spec file diff --git a/motion_guide.html b/motion_guide.html new file mode 100644 index 0000000..875f6cd --- /dev/null +++ b/motion_guide.html @@ -0,0 +1,4664 @@ +

Motion Guide - One Large Document.

+This version of the Guide is made for inclusion in the Motion download package for off line reading. +

+If you read this document from the distribution package of Motion or from some not up to date mirror you should know that the URL for the always up to date version is http://www.lavrsen.dk/twiki/bin/view/Motion/MotionGuide. If you are already on the new TWiki based Motion site clicking the link just mentioned will lead you to the index page for the Motion Guide documents. +


+This topic consists of the following subtopics: +MotionOverview, KnownProblems, InstallOverview, PrepareInstall, ConfigureScript, MakeInstall, UpgradingFromOlderVersion, RunningMotionConfigFiles, CommandLineOptions, ConfigFileOptions, SignalsKill, ErrorLogging, CaptureDeviceOptions, MotionDetectionSettings, ImageFileOutput, TuningMotion, MpegFilmsFFmpeg, SnapshotsWebCam, TextFeatures, AdvancedFilenames, ConversionSpecifiers, WebcamServer, RemoteControlHttp, ExternalCommands, TrackingControl, UsingDatabases, LoopbackDevice. +
+

+

+ +
+

+

Motion Guide - Installation

+

+

Motion Overview

+

+

What is Motion?

+Motion is a program that monitors the video signal from one or more cameras and is able to detect if a significant part of the picture has changed. Or in other words, it can detect motion. +

+The program is written in C and is made for the Linux operating system. +

+Motion is a command line based tool. It has absolutely no graphical user interface. Everything is setup either via the command line or via a set of configuration files (simple ASCII files that can be edited by any ASCII editor). +

+The output from motion can be: +

+

    +
  • jpg files +
  • +
  • ppm format files +
  • +
  • mpeg video sequences +
  • +
+

+

How do I get Motion and what does it cost?

+Motion is an open source type of project. It does not cost anything. Motion is published under the GNU GENERAL PUBLIC LICENSE (GPL) version 2 or later. It may be a bit difficult to understand all the details of the license text (especially if your first language is not English). It means that you can get the program, install it and use it freely. You do not have to pay anything and you do not have to register anywhere or ask the author or publisher for permission. The GPL gives you both rights and some very reasonable duties when it comes to copying, distribution and modification of the program. So in very general terms you do not have to worry about licensing as a normal hobby user. If you want to use Motion in a commercial product, if you want to distribute either modified or original versions of Motion - for free or for a fee, you should read the license carefully. For more information about free software and the GPL, I encourage you to study the very interesting documents about the subject available the of the Free Software Foundation pages about the Philosophy of the GNU Project. +

+

Maintenance and Support

+Both Motion and the Motion Guide are written by people that do all this as a hobby and without asking for any payments or donations. We have a life other than developing Motion and its documentation. This means that bugfixes and updates to this guide are done as our time and families allow it. You are however encouraged to participate and contribute in a very active mailing list. It is a list with a very "positive attitude" and with many contributors that propose features, post patches, discuss problems and patiently answer newbie questions with a very positive spirit. Expect 1-10 emails per day. +

+To get motion direct your browser to the Motion Homepage. +

+Under the download page you will find a series of stable versions and a series of development versions. +

+Motion was originally written by Jeroen Vreeken who still actively participates in the development of Motion and later Folkert van Heusden continued as a lead programmer with Kenneth Lavrsen responsible for Motion Guide, website and releases on Sourceforge. +

+From version 3.1.12 Motion is now project managed entirely by Kenneth Lavrsen, and the project now shift towards being developed by many contributers. +

+For support we encourage you to join the mailing list instead of writing to Jeroen, Folkert or Kenneth directly. We are all very active on the mailing list and by using the mailing list much more users will have benefit of the answers. Newbies and stupid questions are welcome on the list. Contributions in the form of patches are also very welcome on the mailing list. +

+

Which version to download and use?

+Versions 3.2.X are the current version. There is at the moment no development branch. The versions 3.1.X ended at 3.1.20 and there will be no more 3.1.X releases. If you use use a version older than 3.2.X you are encouraged to update. +

+Since 3.1.13 quite many options have been renamed to make setting up Motion easier. From 3.1.17-18 some unfinished features have been removed. The Berkeley mpeg feature is now removed because the ffmpeg feature is now mature and much better working. At version 3.1.18 a new network camera feature was introduced replacing the old cURL based netcam code and introducing support of mjpeg streaming cameras. However this new code was quite difficult to get stable. During the development of 3.2.2 the network camera code was totally rewritten again learning from our experience and now finally it seems to be stable. +

+Since 3.2.3 Debian users can find binary packages on the Motion Sourceforge file download page. You can find Debian versions of Motion in different Debian repositories but they are all out of date and hardly ever get updated. +

+

What features does Motion have?

+See more description at the Motion Homepage. +
    +
  • Taking snapshots of movement +
  • +
  • Watch multiple video devices at the same time +
  • +
  • Watch multiple inputs on one capture card at the same time +
  • +
  • Live streaming webcam (using multipart/x-mixed-replace) +
  • +
  • Real time creation of mpeg movies using libraries from ffmpeg +
  • +
  • Take automated snapshots on regular intervals +
  • +
  • Take automated snapshots at irregular intervals using cron +
  • +
  • Executing external program when detecting movement +
  • +
  • Execute external program at the beginning of an event of several motion detections. +
  • +
  • Execute external program at the end of an event of several motion detections. +
  • +
  • Execute external program when a picture is saved. +
  • +
  • Execute external program when a movie mpeg is created (opened) +
  • +
  • Execite external program when a movie mpeg ends (closed) +
  • +
  • Motion tracking +
  • +
  • Feed events to an MySQL or PostgreSQL database. +
  • +
  • Feed video back to a video4linux loopback for real time viewing +
  • +
  • Web interface using Motion Related Projects such as motion.cgi, Kenneths Webcam Package, Kevins Webpage, X-Motion and many more. +
  • +
  • User configurable and user defined on screen display. +
  • +
  • Control via simple web interface. +
  • +
  • Automatic noise and threshold control +
  • +
  • Ability to control the pan/tilt of a Logitech Sphere (or Orbit) camera +
  • +
  • Highly configurable display of text on images. +
  • +
  • High configurable definition of path and file names of the stored images and films. +
  • +
+

+You can find more information and links at the Motion Homepage. +

+

FreeBSD

+

+Motion is originally developed for Linux and it is still mainly developed and supported for this platform. From version 3.1.15 an experimental port has been made by Angel Carpintero. Not all features of Motion are supported at this time and it still needs a lot of test time on different hardware. Angel is very interested in feedback. Join the Motion Mailing List and give your feedback there. Patches for bugfixes and for enabling the missing features are very welcome. The rest of this guide is still mainly targeted for Linux users. Follow this topic to Install FreeBSD. +

+

MacOSX

+

+From Motion version 3.2.4 it is now also possible to build and install Motion under MacOSX. Feature set it the same as for FreeBSD. See the MacOSX topic for specific help how to install Motion and its dependencies on MacOSX. Again this port has been contributed by Angel Carpintero. +

+

Documentation

+You have the following sources of information: +

+

+

+

Supported Hardware

+Input devices: Here we are thinking about the cameras. +

+Motion supports video input from two kinds of sources. +

+Standard video4linux devices (e.g. /dev/video0). Motion has no drivers for cameras. Installing the camera itself is outside the scope of this document. But here are some nice links. +

+Network cameras (which are actually cameras with a built in web server that can be connected directory to your network). + +

+

+

Known Problems

+See also the Frequently Asked Questions and Bug Reports for known open bugs. +

+Kernel 2.6 and pwc. Note that for kernel 2.6 there is a new release of the Philips WebCam (pwc) drivers 10.0.X. It is recommended to install this. At the time of this being written the 2.6.12+ kernels have a version of pwc built-in but it is a cripled version which can only support very small picture size. You can however download the latest source code of the pwc driver (at this time 10.0.9) and build it without having to rebuild your kernel. But you will need to have either the kernel sources or a special kernel-header package installed to compile it. See Installation of PWC page which is also hosted in this wiki. +

+If you use use a Logitech Quickcam Orbit or Sphere using the driver pwc/pwcx and kernel 2.6.X you should replace the file in the Motion sources called pwc-ioctl.h with the one that comes with the your pwc version. Motion is shipped with 3 versions of pwc-ioctl.h-VERSION. Rename the one that fits your major pwc version number best to pwc-ioctl.h (after renaming the current to something else). There has been some small adjustments in the API that requires that you have the right header file. +

+Camera picture dimensions must be multiple of 16 +Dimentions of camera image must have both height and width that are a multiple of 16. Thís is normally not a problem. All standard sizes like 640, 480, 352, 320, 288, 240, ...etc are multiples of 16. But if you intend to monitor a network camera which is saving jpeg images you may have to pay attention to the dimensions of the picture. +

+error message: warning, clipping 1 dct coefficients to -127..127 +When using the msmpeg4 codec with certain ffmpeg versions you get this error message displayed "[msmpeg4v2 @ 0xb7a890]warning, clipping 1 dct coefficients to -127..127". We have not yet found out why but it seems to not cause any problems. +

+

+

+

How do I install Motion?

+Motion is mainly distributed as source files that you must compile yourself. There is also an RPM made on Fedora Core 3. And Debian packages are available for selected versions. +

+The short overview of the steps to install Motion from sources. +

    +
  • Preparation: Motion uses a number of shared libraries that must be installed on your computer before you can build Motion. The needed shared libraries depends on the features you wish to use. Features network camera, ffmpeg, MySQL and PostgreSQL needs specific shared libraries installed. See preparation section for more information. +
  • +
+

+

    +
  • Download the motion source files (distributed as tar'ed and compressed files). Place the file in a place of your own choice. +
  • +
+

+

    +
  • Untar and uncompress the file to the place you want the program installed. Editor recommends placing the motion source file directory in /usr/local. If you do not have write access to the /usr/local directory (you are under the mercy of an ignorant system administrator with a severe case of paranoia) - you can install the program in a directory in your home directory. You will then need to read the next section about how to configure before you compile the program. Below is shown the exact commands using version 3.2.X installed in /usr/local as an example (replace /path/to with the actual placement of the tar.gz file). +
  • +
+
    +
    +cd /usr/local
    +tar -xvzf /path/to/motion-3.2.X.tar.gz
    +
    +
+
    +
  • You will now have created a directory called motion-3.2.X. You can rename it to motion (mv motion-3.1.X motion). I recommend creating a symbolic link to the current version. This way you can more easily experiment with different version simply by changing the link. +
  • +
+
    +
    +ln -s motion-3.2.X motion
    +
    +
+
    +
  • Now change to the new directory +
  • +
+
    +
    +cd motion
    +
    +
+
    +
  • Run configure. You can start with the defaults. If you need to modify the installation parameters you can read the next section. +
  • +
+
    +
    +./configure
    +
    +
+
    +
  • Build the code +
  • +
+
    +
    +make
    +
    +
+
    +
  • Install the code, manual page, etc +
  • +
+
    +
    +make install
    +
    +
+
    +
  • In /etc/motion/etc you will find a file called motion-dist.conf. If it is the first time you install Motion - rename this file to motion.conf and edit as a minimum the settings: videodevice, input, norm, frequency, width, height and target_dir. That should get you going. +
  • +
+

+

    +
  • Run the program. To enable more features you must modify the config file. +
  • +
+
    +
    +motion
    +
    +
+

+

+

Preparation For Install

+

+Note: If you're using SuSE 9.2, you might want to ADDITIONALLY have a look at Compiling on SuSE 9.2. As mentioned on that page as well, you will still need to read the instructions here as well. +

+Before you start you may need to install a number of shared libraries that Motion uses. If they are missing the feature will simply normally not be included. Most of these libraries can be found on the CDs of your distribution. A few will have to be downloaded from the Internet. Note that when you install software using pre-compiled binaries (Redhat type RPMs, Debian debs etc) you normally only get what is needed to run the programs themselves. In order to compile other programs from source that uses these pre-compiled libraries you also need to installed the development packages. These are normally called the same name as the package suffixed by -devel or -dev. These development packages contains the header files (xxx.h) that Motion needs to build with the shared libraries. If you build a library from sources you already have these header files. It is recommended to simply install the pre-compiled binary packages and their development brothers. +

+This is a list of shared libraries used by Motion and the RPM packages that provides them. +

+Motion will always need these libraries to be built and work + + + + + +
Library RPM Packages Debian Packages
libm, libresolv, libdl, libpthread, libc, ld-linux, libcrypt, and libnsl glibc and glibc-devel libc6 , libc6-dev ,libglib1.2
libjpeg libjpeg and libjpeg-devel libjpeg62 and libjpeg62-dev ( optional libjpeg-mmx-dev )
libz zlib and zlib-devel zlib1g and zlib1g-dev
+

+For generating mpeg films with ffmpeg you need this library:
+(See also the section Generating MPEG films with ffmpeg for how to install ffmpeg and libavframe/libavcodec)
+Motion must be installed with revision 0.4.8 or 0.4.9pre1 of ffmpeg. Motion will also work with later CVS snapshots of ffmpeg but the API of the ffmpeg libraries changes all the time and without warning. If you have problems compiling Motion or with running an RPM of Motion you may try with an older CVS snapshot of ffmpeg. The Motion developers will like to know when ffmpeg changes and breaks Motion so we can fix it. Please file a bug report then with the exact date of the ffmpeg CVS version you have trouble with. +

+ + + +
Library RPM Packages Debian Packages
libavcodec, libavframe ffmpeg and ffmpeg-devel or install from source ffmpeg ,libavcodec1,libavcodec1-dev (*)
+

+Debian has not provided deb packages for ffmpeg due patent issues. However this is about to change so checkout for availability of newer versions of debian ffmpeg debs. You can build yourself from source or from Christian Marillat website or apt repository. +

+deb ftp://ftp.nerim.net/debian-marillat/ stable main # ( woody )
+deb ftp://ftp.nerim.net/debian-marillat/ testing main # ( sarge )
+deb ftp://ftp.nerim.net/debian-marillat/ unstable main # ( sid )
+
+Add the suitable line to your /etc/apt/sources.list and run this: +
+apt-get update ; apt-get -y install ffmpeg libavcodec1 libavcodec1-dev
+
+

+For logging in MySQL you need this library: + + + +
Library RPM Packages Debian Packages
libmysqlclient mysql and mysql-devel libmysqlclient10 and libmysqlclient10-dev
+

+For logging in PostgreSQL you need this library: + + + +
Library RPM Packages Debian Packages
libpq postgresql-libs and postgresql-devel postgresql-dev and libpgsql2
+

+

+

Configure Script

+Configure is script that you run to setup the build environment for the C-compiler. It generates the "Makefile" which the program "make" uses to compile and install the software. +

+To run configure your current directory must be the motion directory. You type +

+./configure +

+You can add the parameter ./configure --help to get help on the different switches. +

+This is walk through of the options. +

+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Option Description
Defaults for the options
are specified in brackets [ ]
Editors comment
-h, --help display this help and exit  
--help=short display options specific to this package This command shows the options special to motion. Recommended
--help=recursive display the short help of all the included packages  
-V, --version display version information and exit Gives no useful information
-q, --quiet, --silent do not print `checking...' messages Not very useful. Output to screen is only a few lines anyway.
--cache-file=FILE cache test results in FILE. [disabled] No function
-C, --config-cach alias for `--cache-file=config.cache' No function
-n, --no-create do not create output files Used for testing if other switches produce error - without writing anything to the disk
--srcdir=DIR find the sources in DIR. [configure dir or `..'] DIR is a directory path. Editor recommends having the current directory being the motion installation directory and not using this switch. Then it defaults to the same directory as where the configure script is which is the current directory.
Installation directories:    
--prefix=PREFIX install architecture-independent files in PREFIX
[/usr/local]
The default /usr/local means that the executable binary "motion" is installed in /usr/local/bin, the manual page in /usr/local/man/man1, the document files in /usr/local/docs/motion-version, configuration file in /usr/local/etc, and some examples config files in /usr/local/examples/motion-versionEditor recommends keeping this default setting.
If you are experimenting with many parallel versions it may be interesting to set the PREFIX to e.g. /usr/local/motion and then add /usr/local/motion/bin to your search path (or simply cd /usr/local/motion/bin before execution).
This way you can change version just by changing the symbolic link in /usr/local/motion as suggested earlier in this guide.
If you are installing the software on a machine where you have no access to the /usr/local but have write access to a home directory, then you should change this to point to a directory within your home tree.
Example: --prefix=$HOME
--exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
[PREFIX]
If you set this it only defines an alternative installation directory for the executable binary.
Note: The executable binary will be placed in a directory "bin" below the directory specified by this option
Editor recommends leaving this as default (i.e. not setting it).
--bindir=DIR user executables [EPREFIX/bin] With this option you can control exactly in which directory the executable binary is installed. The previous option automatically adds the bin directory. Here you are in fill control.
--sbindir=DIR System admin executables [EPREFIX/sbin] Not used by motion. Ignore it.
--libexecdir=DIR program executables [EPREFIX/libexec] Not used by motion. Ignore it.
--datadir=DIR read-only architecture-independent data [PREFIX/share] Not used by motion. Ignore it.
--sysconfdir=DIR read-only single-machine data [PREFIX/etc] This is where motion both installs the default configuration file and also where it later searches for it.
Motion searches for the configuration file "motion.conf" in the following order:

    1. Current directory from where motion was invoked
    2. $HOME/.motion
    3. The sysconfig directory set by this switch. If not defined the default is /usr/local/etc/

Editor recommends leaving this at default. Be careful if you run "make install" again. This will overwrite the motion.conf file that you have edited and experimented with for hours. Make sure to keep a copy in a safe place. Alternatively, copy the working file to the motion base install directory. Then make install will simply copy the same file back again.
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] Not used by motion. Ignore it.
--localstatedir=DIR modifiable single-machine data [PREFIX/var] Not used by motion. Ignore it.
--libdir=DIR object code libraries [EPREFIX/lib] Not used by motion. Ignore it.
--includedir=DIR C header files [PREFIX/include] Not used by motion. Ignore it.
--oldincludedir=DIR C header files for non-gcc [/usr/include] Not used by motion. Ignore it.
--infodir=DIR info documentation [PREFIX/info] Not used by motion. Ignore it.
--mandir=DIR man documentation [PREFIX/man] Editor recommends the default.
Optional Packages:    
--with-ffmpeg=DIR Specify the path for the directory prefix in which the library and headers are installed.
If not specified configure will search in /usr/ and /usr/local/
DIR is the directory PREFIX in which the ffmpeg shared libraries and their headers are installed.
If you install ffmpeg from sources and use the default directories or if ffmpeg is installed as a binary package (RPM or deb) you do not need to specify the directory prefix. Configure will find the libraries automatically. If you installed ffmpeg from sources and specified a different --prefix when building ffmpeg you must use the same value for the DIR ( --with-ffmpeg=DIR).
For more information on FFmpeg see the FFmpeg project home page.
FFmpeg is a package that enables streamed video mpeg signal from your web camera to a browser.
Editor recommends installing ffmpeg from source and in the directory /usr/local/ffmpeg and build ffmpeg with ./configure --enable-shared.
This places libraries in /usr/local/lib and headers in /usr/local/include.
--without-ffmpeg Do not compile with ffmpeg Use this if you do not want to compile with ffmpeg. If ffmpeg is not installed you do not need to specify that Motion must build without ffmpeg.
--with-mysql=DIR normally, configure will scan all possible default installation paths for mysql. When its fail, use this command to tell configure where mysql installation root directory is. DIR is the installation directory of mysql. E.g. /usr/local/mysql
Default is that make searches in the normal installation directories of most distributions.
--without-mysql Do not compile with MySQL support Use this if you do not want to include MySQL support in the package.
This can also be useful if you get compilation errors related to MySQL and you actually do not need the feature anyway.
--with-pgsql=DIR Include PostgreSQL support. DIR is the PostgreSQL base install directory, defaults to /usr/local/pgsql.
Set DIR to "shared" to build as a dynamic library, or "shared,DIR" to build as a dynamic library and still specify DIR.
Default is that make searches in the normal installation directories of most distributions.
See section later about PostgreSQL about potential problem during compilation. There is an easy workaround for it.
--without-pgsql Do not compile with PostgreSQL support Use this if you do not want to include PostgreSQL support in the package.
This can also be useful if you get compilation errors related to PostgreSQL and you actually do not need the feature anyway.
--without-v4l Exclude using v4l (video4linux) subsystem. Makes Motion so it only supports network cameras. Can be used if you do not need V4L support and maybe lack some of the libraries for it.
--without-optimizecpu Exclude autodetecting platform and cpu type. This will disable the compilation of gcc optimizing code by platform and cpu. Use this if the optimization causes problems. Typically if you build on some non X386 compatible CPU.
Developers options    
--with-developer-flags Add additional warning flags for the compiler. This option is for developers only. It produces a flood of warnings that helps the developer to write more robust code. These warnings are normally harmless but can sometimes be a latent defect.
For more information about these flags, see CompileWithDeveloperFlags
+ +
+

+

+

Make

+When you run make, all the C-source files are automatically compiled and linked. Just look out for error messages. +

+Make uses a file called "Makefile" which is generated by the "configure" script you just ran. If you have special needs you can manually edit this file. Next time you run configure a new Makefile will be generated and your changes are lost. +

+ALERT! Attention! +

+If you have run make before, you should run a make clean before running make again. This cleans out all the object files that were generated the previous time you ran make. If you do not run make clean first before you rebuild Motion you may not get the additional feature included. For example: If you built Motion without ffmpeg support and then add it later - and rebuild Motion without running make clean first - the ffmpeg feature does not get compiled into the Motion binary. +

+First time you build motion run ./configure, make, make install. If you need to build it again (to run with different configure options) run ./configure, make clean, make, make install. +

+

Make Install

+make install simply copies all the nice files that were generated during the compilation/linking that make did. +

+Makes the directories (if they do not already exist)(path shown are the defaults): /usr/local/bin, usr/local/man/man1, /usr/local/etc, /usr/local/share/doc/motion-3.2.X, and /usr/local/share/doc/examples/motion-3.2.X. +

+Copies the following files from the base motion directory (assuming the default PREFIX /usr/local was used when running configure - otherwise adjust to the actuals you chose) +

    +
  • Executable binary "motion" to /usr/local/bin +
  • +
  • Manual page "motion.1" to /usr/local/man/man1 +
  • +
  • Document files "CHANGELOG, COPYING, CREDITS, INSTALL, and README to /usr/local/share/doc/motion-3.2.X +
  • +
  • Example configuration files "*.conf" to /usr/local/share/doc/examples/motion-3.2.X +
  • +
  • Configuration file "motion-dist.conf" to /usr/local/etc +
  • +
+Note that the any existing files are overwritten. The default config file motion-dist.conf is named like this so that you do not get your working motion.conf file overwritten when you upgrade Motion. +

+

Un-install

+From the motion base installation directory you simply run make uninstall +

+And delete the base installation directory in /usr/local and any link pointing to it. If you have forgotten where you installed it or someone else did it for you, simply search for the files and directories starting with motion. If the filenames and the directories match the names described in the "Make Install" section of this document, you can safely delete them. +

+

Additional Make Options

+The make command can be run with several options. make, make install and make uninstall has already been described above. +

+

+
make clean
deletes all the binary files (object files) and the motion binary generated by make. It also deletes temporary files and any jpg files that motion has saved in the motion source directory. It is very important to always run make clean before you run make if you change the configuration (like adding features such as ffmpeg) and rebuild motion. +
+
+

+

+
make distclean
deletes the files: config.status, config.log, config.cache, Makefile, and motion.spec. +
+
+

+

+
make updateguide
fetches a fresh new copy of this guide and place it in your motion source directory. Note that the pictures are not downloaded. +
+
+

+

+
make dist
performs make clean, make distclean and make updateguide in one single operation. +
+
+

+

+

Upgrading From Older Version

+If you are upgrading from motion 3.0.X or from an older version of 3.1.X you should note that many options have been removed from version 3.1.13 and forward and many new have arrived. You still have most of the old features. The options have been changed for two reasons. New more flexible features and to simplify getting started with Motion. With 3.2.1 the changes are significant. +You should also note these major differences. +
    +
  • The use of thread files has completely changed. Read the section "The Config Files" carefully. +
  • +
  • The mask file format has changed. Read the section about "Mask File" +
  • +
  • Pre_capture feature introduced in 3.1.12 +
  • +
  • Advanced filename feature enables very flexible filename definitions (3.1.13) +
  • +
  • onffmpegclose options enables running external scripts when mpeg file is closed (3.1.13) +
  • +
  • despeckle feature improves motion detection and noise immunity (3.1.13) +
  • +
  • Minimum_motion_frames feature prevents short noise events from being saved (3.1.14) +
  • +
  • If you use the database features you need to note that from version 3.1.15 and forward the fields have been redefined. Removed are second, minute, hour, day, month and year. Instead these six have been replaced by a real timestamp field called time_stamp. The relatively new field 'type' has been renamed to 'file_type' to avoid reserved SQL words. A new field 'text_left' has been added which stores the text given by the config option text_left. And last a field called 'camera' has been added which stores the thread number. +
  • +
  • From 3.1.15 the ffmpeg feature now also supports mpeg4 and msmpeg4. The build process of Motion now use ffmpeg libraries as shared libraries. The --with-libavcodec has been replaced by a --with-ffmpeg which only needed to specify if you are installing ffmpeg from sources in a non-standard location. If you have installed ffmpeg from sources already you will need to rebuild by running (from within the ffmpeg source file root) ./configure --enable-shared followed by make and make install. If you had installed ffmpeg from a binary RPM or deb you probably don't have to do anything. +
  • +
  • Rotate feature was introduced in 3.1.15 +
  • +
  • Berkeley mpeg feature has been removed in 3.1.18 (use ffmpeg - it is much better) +
  • +
  • Incomplete prediction feature was removed in 3.1.18. (lack of interest in finishing it) +
  • +
  • Smart Mask feature introduced in 3.1.18 +
  • +
  • output_normal can now also have the value "first" which means only save first jpg from each event (3.1.18) +
  • +
  • ffmpeg-0.4.9 is now supported. Motion detection mpegs can no longer be saved as mpeg1 (ffmpeg does not support non-standard framerates in 0.4.9) (3.1.18) +
  • +
  • Motion now supports most (not all) mjpeg streaming cameras (3.1.18). +
  • +
  • output_normal can now have values "first" or "best". It is used when you need to present a link to an mpeg movie shown as a single jpeg image. "First" saves the first picture frame in the new event. "Best" saves the picture frame with most motion content (most changed pixels) when the event is over. "on" still saves all motion detection picture frames plus pre and post captured images. With "best" you can set jpeg_filename = "preview" and it gets the same filename as the mpeg file but with extension .jpg. Option "locate" can also take the value "preview" which makes it only draw a rectangel on the jpeg but not on the mpeg movie. (3.2.1) +
  • +
  • The xmlrpc remote control interface is replaced by a much nicer http remote control interface. (3.2.1) +
  • +
  • All the options that calls external programs have been made much more generic. New onxxxx options have been added. Execute, sms and mail have been replaced by the generic on_event_start. (3.2.1) +
  • +
  • New setup mode makes setting all the detection options much easier. +
  • +
  • netcam now also supports proxies (3.2.2) and ftp (3.2.4) +
  • +
  • text on the pictures can be set to double size (3.2.2) +
  • +
  • Tracking with Logitech Sphere/Orbit improved (3.2.4) +
  • +
  • SQL database feature is now fully configurable so you can control which fields you have in the database. +
  • +
  • Many new conversion specifiers have been added which can be used both in filenames, commands, text, and SQL database features (3.2.2-3.2.4) +
  • +
+

+The table below shows the new options in the left column, and obsolete options in the right column. If the there are options on both sides in a row it means that the options in the left column replaced the options in the right column. +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
New Options Obsolete Options
text_left (3.1.13)
text_right (3.1.13)
text_changes (3.1.13)
drawtext_user (3.1.13)
drawtext_shots (3.1.13)
drawtext_changes (3.1.13)
jpeg_filename (3.1.13)
ffmpeg_filename (3.1.13)
snapshot_filename (3.1.13)
timelapse_filename (3.1.13)
predict_filename (3.1.13)
(predict_filename removed in 3.1.18)
oldlayout (3.1.13)
snapshots_overwrite (3.1.13)
snapshot_interval (3.1.13) snapshots (3.1.13)
  realmotion (3.1.13)
despeckle (3.1.13)  
pre_capture (3.1.12)  
ffmpeg_timelapse (v. 3.1.14) ffmpeg_timelaps (renamed v 3.1.14)
ffmpeg_timelapse_mode (3.1.14)  
sql_log_image (3.1.14)
sql_log_snapshot (3.1.14)
sql_log_mpeg (3.1.14)
sql_log_timelapse (3.1.14)
sql_log_prediction (3.1.14)
 
minimum_motion_frames (3.1.14)  
rotate (3.1.15)  
ffmpeg_variable_bitrate (3.1.15)
ffmpeg_video_codec (3.1.15)
 
  berkeley_single_directory (3.1.18)
mpeg_encode (3.1.18)
mpeg_encode_bin (3.1.18)
adjust_rate off (3.1.18)
jpg_cleanup (3.1.18)
  predict_filename (3.1.18)
predict_enable (3.1.18)
predict_threshold (3.1.18)
predict_description (3.1.18)
sql_log_prediction (3.1.18)
brightness (3.1.18)
contrast (3.1.18)
saturation (3.1.18)
hue (3.1.18)
 
smart_mask_speed (3.1.18)  
output_normal
valid values are now "on", "off", "first" (3.1.18) and "best" (3.2.1)
 
setup_mode (3.2.1) always_changes (3.2.1)
locate
valid values are now "on", "off", "preview" (3.2.1)
 
jpeg_filename
Besides normal path names the value "preview" has speciel meaning together with output_normal = "best" (3.2.1)
 
control_html_output (3.2.1)  
on_event_start (3.2.1)
execute (3.2.1)
sms (3.2.1)
mail (3.2.1)
on_event_end (3.2.1)
 
on_motion_detected (3.2.1)
 
on_picture_save (3.2.1)
onsave (3.2.1)
on_movie_start (3.2.1)
on_movie_end (3.2.1)
onmpeg (3.2.1)
onffmpegclose (introduced 3.1.13)(renamed to on_movie_end 3.2.1)
netcam_proxy (3.2.2)  
text_double (3.2.2)  
webcam_motion
Feature has been heavily improved so it is actually usefull now (3.2.2).
 
netcam_url
Now also supports fetching single frame jpeg pictures via ftp using ftp:// syntax (3.2.4)
 
track_step_angle_x (3.2.4)
track_step_angle_y (3.2.4)
Add better configuration of auto tracking with a Logitech Sphere/Orbit camera.
 
track_move_wait (3.2.4)
track_auto (3.2.4)
Adds better configuration of auto tracking feature
 
sql_query (3.2.4)
Adds full flexibility of defining fields when using the SQL database features.
 
+ +
+

+

+

Running Motion

+

+

Important Definitions

+Motion is invoked from the command line. It has no GUI. Everything is controlled from config files. From version 3.2 the command line is only used to define location of config file and a few special runtime modes (setup and non-daemon). +

+A few important definitions. +

    +
  • A snapshot is a picture taken at regular intervals independently of any movement in the picture. +
  • +
  • A "motion" image/mpeg shows the pixels that have actually changed during the last frames. These pictures are not very useful for normal presentation to the public but they are quite useful for testing and tuning and making mask files as you can see exactly where motion sees something moving. Motion is shown in greytones. If labelling is enabled the largest area is marked as blue. Smart mask is shown in read. +
  • +
  • A "normal" image is the real image taken by the camera with text overlayed. +
  • +
+

+ +

The Config Files

+

+If Motion was invoked with command line option -c pathname Motion will expect the config file to be as specified. When you specify the config file on the command line with -c you can call it anything. +

+If you do not specify -c or the filename you give Motion does not exist, Motion will search for the configuration file called 'motion.conf' in the following order: +

+

    +
  1. Current directory from where motion was invoked +
  2. +
  3. Then in a directory called '.motion' in the current users home directory (shell environment variable $HOME). E.g. /home/goofy/.motion/motion.conf +
  4. +
  5. The directory defined by the --sysconfdir=DIR when running .configure during installation of Motion
    (If this option was not defined the default is /usr/local/etc/) +
  6. +
+If you have write access to /usr/local/etc then the editor recommends having only one motion.conf file in the default /usr/local/etc/ directory. +

+Motion has a configuration file in the distribution package called motion-dist.conf. When you run 'make install' this files gets copied to the /usr/local/etc directory. +

+The configuration file needs to be renamed from motion-dist.conf to motion.conf. The original file is called motion-dist.conf so that your perfectly working motion.conf file does not accidentally get overwritten when you re-install or upgrade to a newer version of Motion. +

+If you have more than one camera you should not try and invoke Motion more times. Motion is made to work with more than one camera in a very elegant way and the way to do it is to create a number of thread config files. Motion will then create an extra tread of itself for each camera. If you only have one camera you only need the motion.conf file. The minute you have two or more cameras you must have one thread config file per camera besides the motion.conf file. +

+So if you have for example two cameras you need motion.conf and two thread config files. Total of 3 config files. +

+An option that is common to all cameras can be placed in motion.conf. (You can also put all parameters in the thread files but that makes a lot of editing when you change a common thing). +

+An option that is unique to a camera must be defined in each thread file. +

+It is often seen that people copy the entire motion.conf into the thread config files and change a few options. This works but it not recommended because it is more difficult to maintain and overview. Keep all the common options in motion.conf and the few unique only in the thread config files +

+The first camera is defined in the first thread file called from motion.conf. The 2nd camera is defined in the 2nd thread file called from motion.conf etc. +

+Any option defined in motion.conf will be used for all cameras except for the cameras in which the same option is defined in a thread config file. +

+Motion reads its configuration parameters in the following sequence. If the same parameter exists more than one place the last one read wins. +

    +
  1. Motion reads the configuration file motion.conf from the beginning of the file going down line by line. +
  2. +
  3. If the option "thread" is defined in motion.conf, the thread configuration file(s) is/(are) read. +
  4. +
  5. Motion continues reading the rest of the motion.conf file. Any options from here will overrule the same option previously defines in a thread config file. +
  6. +
  7. Motion reads the command line option again overruling any previously defined options. +
  8. +
+So always call the thread config files in the end of the motion.conf file. If you define options in motion.conf AFTER the thread file calls, the same options in the thread files will never be used. So always put the thread file call at the end of motion.conf. +

+Nearly all config options can be unique for a specific camera and placed in a thread config file. There are a few options that must be in motion.conf and cannot be in a thread config file: control_authentication, control_html_output, control_localhost, control_port, daemon, and thread. +

+If motion is built without specific features such as ffmpeg, mysql etc it will ignore the options that belongs to these features. You do not have to remove them or comment them out. +

+If you run the http control command http://host:port/0/config/writeyes, motion will overwrite motion.conf and all the thread.conf files by autogenerated config files neatly formatted and only with the features included that Motion was built with. If you later re-build Motion with more features or upgrade to a new version, you can use your old config files, run the motion.conf.write command, and you will have new config files with the new options included all set to their default values. This makes upgrading very easy to do. +

+

+

Command Line Options

+

+ALERT! In Motion 3.2.1 and forward most command line options have been removed and replaced them by an option to specify location to motion.conf and a few options related to setting up motion. There are now only 4 command line options left and they are basically all new. +

+SYNOPSIS +

+motion [ -hns ] [ -c config file path]
+
+

+ +

+ + + + + + + + + +
Option Description Editors comment
-n Run in non-daemon mode. Instead of running Motion in the background Motion runs in the terminal window writing messages when things happen. If you have problems getting Motion to start or work, run Motion in this mode to get more messages that can help you solve the problem.
-s Run in setup mode. Also forces non-daemon mode
-c config file path Full path and filename of config file. E.g. /home/kurt/motion.conf. Default is /usr/local/etc unless specified differently when building Motion. Many RPMs and debian packages will most likely use /etc or /etc/motion as default
-h Show help screen.  
-d level Debugging mode This mode is used for developers to enable debug messages. Normal users will not need to use this mode unless a developer request to get additional information in the attempt to resolve a bug. Mainly the netcam code has debugging features. The level defines how much debugging info you get. A high number displays all debugging.
+ +
+

+

+

Config File Options

+These are the options that can be used in the config file. +

+All number values are integer numbers (no decimals allowed). Boolean options can be on or off. +

+Some configuration options are only used if Motion is built on a system that has the matching software libraries installed (MySQL, PostgreSQL and FFMPEG). +

+MySQL +

    +
  • mysql_db, mysql_host, mysql_user, mysql_password +
  • +
+

+PostgreSQL +

    +
  • pgsql_db, pgsql_host, pgsql_user, pgsql_password, pgsql_port +
  • +
+

+FFMPEG (libavcodec) +

    +
  • ffmpeg_cap_new, ffmpeg_cap_motion, ffmpeg_filename, ffmpeg_timelapse, ffmpeg_timelapse_mode, ffmpeg_bps, ffmpeg_variable_bitrate, ffmpeg_video_codec +
  • +
+

+

Options in Alphabetical Order.

+

+The table below lists all the Motion options in alphabetical order. Click on the option name to see a longer description of each. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Option Range/Values
Default
Description
auto_brightness Values: on, off
Default: off
Let motion regulate the brightness of a video device. Only recommended for cameras without auto brightness
brightness Values: 0 - 255
Default: 0 (disabled)
The brightness level for the video device.
contrast Values: 0 - 255
Default: 0 (disabled)
The contrast level for the video device.
control_authentication Values: Max 4096 characters
Default: Not defined
To protect HTTP Control by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. This option must be placed in motion.conf and not in a thread config file.
control_html_output Values: on, off
Default: on
Enable HTML in the answer sent back to a browser connecting to the control_port. This option must be placed in motion.conf and not in a thread config file.
control_localhost Values: on, off
Default: on
Limits the http (html) control to the localhost. This option must be placed in motion.conf and not in a thread config file.
control_port Values: 0 - 65535
Default: 0 (disabled)
Sets the port number for the http (html using browser) based remote control. This option must be placed in motion.conf and not in a thread config file.
daemon Values: on, off
Default: off
Start in daemon (background) mode and release terminal. This option must be placed in motion.conf and not in a thread config file.
despeckle Values: EedDl
Default: Not defined
Despeckle motion image using combinations of (E/e)rode or (D/d)ilate. And ending with optional (l)abeling.
ffmpeg_bps Values: 0 - 9999999
Default: 400000
Bitrate of mpegs produced by ffmpeg. Bitrate is bits per second. Default: 400000 (400kbps). Higher value mans better quality and larger files. Option requires that ffmpeg libraries are installed.
ffmpeg_cap_motion Values: on, off
Default: off
Use ffmpeg libraries to encode motion type mpeg movies where you only see the pixels that changes.
ffmpeg_cap_new Values: on, off
Default: off
Use ffmpeg libraries to encode mpeg movies in realtime.
ffmpeg_filename Values: Max 4095 characters
Default: %v-%Y%m%d%H%M%S
File path for motion triggered ffmpeg movies (mpeg) relative to target_dir.
ffmpeg_timelapse Values: 0 - 2147483647
Default: 0 (disabled)
Create a timelapse movie saving a picture frame at the interval in seconds set by this parameter. Set it to 0 if not used.
ffmpeg_timelapse_mode Values: hourly, daily, weekly-sunday, weekly-monday, monthly, manual
Default: daily
The file rollover mode of the timelapse video.
ffmpeg_variable_bitrate Values: 0, 2 - 31
Default: 0 (disabled)
Enables and defines variable bitrate for the ffmpeg encoder. ffmpeg_bps is ignored if variable bitrate is enabled. Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, or the range 2 - 31 where 2 means best quality and 31 is worst.
ffmpeg_video_codec Values: mpeg1 (ffmpeg-0.4.8 only), mpeg4, msmpeg4
Default: mpeg4
Codec to be used by ffmpeg for the video compression. Timelapse mpegs are always made in mpeg1 format independent from this option.
framerate Values: 2 - 100
Default: 100 (no limit)
Maximum number of frames to be captured from the camera per second.
frequency Values: 0 - 999999
Default: 0 (Not set)
The frequency to set the tuner to (kHz). Valid range: per tuner spec, default: 0 (Don't set it)
gap Values: 0 - 2147483647
Default: 60
Gap is the seconds of no motion detection that triggers the end of an event. An event is defined as a series of motion images taken within a short timeframe.
height Values: Device Dependent
Default: 288
The height of each frame in pixels.
hue Values: 0 - 255
Default: 0 (disabled)
The hue level for the video device.
input Values: 0 - 7, 8 = disabled
Default: 8 (disabled)
Input channel to use expressed as an integer number starting from 0. Should normally be set to 1 for video/TV cards, and 8 for USB cameras.
jpeg_filename Values: Max 4095 characters
Default: %v-%Y%m%d%H%M%S-%q
File path for motion triggered images (jpeg or ppm) relative to target_dir. Value 'preview' makes a jpeg filename with the same name body as the associated saved mpeg movie file.
lightswitch Values: 0 - 100
Default: 0 (disabled)
Ignore sudden massive light intensity changes given as a percentage of the picture area that changed intensity.
locate Values: on, off, preview
Default: off
Locate and draw a box around the moving object. Value 'preview' makes Motion only draw a box on a saved preview jpeg image and not on the saved mpeg movie.
low_cpu Values: 0 - 100
Default: 0 (disabled)
When this option is not zero motion will be in a low cpu mode while not detecting motion. In low cpu mode Motion reduces the framerate to the value given for this option. Value zero means disabled.
mask_file Values: Max 4095 characters
Default: Not defined
PGM file to use as a sensitivity mask. This picture MUST have the same width and height as the frames being captured and be in binary format.
max_mpeg_time Values: 0 (infinite) - 2147483647
Default: 3600
The maximum length of an mpeg movie in seconds. Set this to zero for unlimited length.
minimum_gap Values: 0 - 2147483647
Default: 0 (no minimum)
The minimum time between two shots in seconds.
minimum_motion_frames Values: 1 - 1000s
Default: 1
Picture frames must contain motion at least the specified number of frames in a row before they are detected as true motion. At the default of 1, all motion is detected. Valid range is 1 to thousands, but it is recommended to keep it within 1-10.
motion_video_pipe Values: Max 4095 characters
Default: Not defined
The video4linux video loopback input device for motion images. If a particular pipe is to be used then use the device filename of this pipe, if a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. Default: not set
mysql_db Values: Max 4095 characters
Default: Not defined
Name of the MySQL database.
mysql_host Values: Max 4095 characters
Default: Not defined
IP address or domain name for the MySQL server. Use "localhost" if motion and MySQL runs on the same server.
mysql_password Values: Max 4095 characters
Default: Not defined
The MySQL password.
mysql_user Values: Max 4095 characters
Default: Not defined
The MySQL user name.
netcam_proxy Values: Max 4095 characters
Default: Not defined
URL to use for a netcam proxy server, if required. The syntax is http://myproxy:portnumber
netcam_url Values: Max 4095 characters
Default: Not defined
Specify an url to a downloadable jpeg file or raw mjpeg stream to use as input device. Such as an AXIS 2100 network camera.
netcam_userpass Values: Max 4095 characters
Default: Not defined
For network cameras protected by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication.
night_compensate Values: on, off
Default: off
When this option is set the noise threshold will be lowered if the picture is dark. This will improve the sensitivity in dark places. However it might also increase the number of false alarms since most cameras also compensate for this with their AGC which will increase noise.
noise_level Values: 1 - 255
Default: 32
The noise level is used as a threshold for distinguishing between noise and motion.
noise_tune Values: on, off
Default: on
Activates the automatic tuning of noise level.
norm Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour)
Default: 0 (PAL)
Select the norm of the video device. Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL)
on_event_end Values: Max 4095 characters
Default: Not defined
Command to be executed when an event ends after a period of no motion. The period of no motion is defined by option gap. You can use Conversion Specifiers and spaces as part of the command.
on_event_start Values: Max 4095 characters
Default: Not defined
Command to be executed when an event starts. An event starts at first motion detected after a period of no motion defined by gap. You can use ConversionSpecifiers and spaces as part of the command.
on_motion_detected Values: Max 4095 characters
Default: Not defined
Command to be executed when a motion frame is detected. You can use Conversion Specifiers and spaces as part of the command.
on_movie_end Values: Max 4095 characters
Default: Not defined
Command to be executed when an ffmpeg movie is closed at the end of an event. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command.
on_movie_start Values: Max 4095 characters
Default: Not defined
Command to be executed when an mpeg movie is created. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command.
on_picture_save Values: Max 4095 characters
Default: Not defined
Command to be executed when an image is saved. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command.
output_all Values: on, off
Default: off
Picture are saved continuously as if motion was detected all the time.
output_motion Values: on, off
Default: off
Output pictures with only the moving object. This feature generates the special motion type movies where you only see the pixels that changes as a graytone image. If labelling is enabled you see the largest area in blue. Smartmask is shown in red.
output_normal Values: on, off, first, best
Default: on
Normal image is an image that is stored when motion is detected. It is the same image that was taken by the camera. I.e. not a motion image like defined by output_motion. Default is that normal images are stored.
pgsql_db Values: Max 4095 characters
Default: Not defined
Name of the PostgreSQL database.
pgsql_host Values: Max 4095 characters
Default: Not defined
IP address or domain name for the PostgreSQL server. Use "localhost" if motion and PostgreSQL runs on the same server.
pgsql_password Values: Max 4095 characters
Default: Not defined
The PostgreSQL password.
pgsql_port Values: 0 - 65535
Default: 5432
The PostgreSQL server port number.
pgsql_user Values: Max 4095 characters
Default: Not defined
The PostgreSQL user name.
post_capture Values: 0 - 2147483647
Default: 0 (disabled)
Specifies the number of frames to be captured after motion has been detected.
ppm Values: on, off
Default: off
Output ppm images instead of jpeg. This uses less CPU time, but causes a LOT of hard disk I/O, and it is generally slower than jpeg.
pre_capture Values: 0 - 100s
Default: 0 (disabled)
Specifies the number of previous frames to be outputted at motion detection. Recommended range: 0 to 5, default=0. Do not use large values! Large values will cause Motion to skip video frames and cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead.
quality Values: 1 - 100
Default: 75
The quality for the jpeg images in percent.
quiet Values: on, off
Default: off
Be quiet, don't output beeps when detecting motion.
rotate Values: 0, 90, 180, 270
Default: 0
Rotate image the given number of degrees. The rotation affects all saved images as well as mpeg movies.
roundrobin_frames Values: 1 - 2147483647
Default: 1
Specifies the number of frames to capture before switching inputs, this way also slow switching (e.g. every second) is possible.
roundrobin_skip Values: 1 - 2147483647
Default: 1
Specifies the number of frames to skip after a switch. (1 if you are feeling lucky, 2 if you want to be safe).
saturation Values: 0 - 255
Default: 0 (disabled)
The colour saturation level for the video device.
setup_mode Values: on, off
Default: off
Run Motion in setup mode.
smart_mask_speed Values: 0 - 10
Default: 0 (disabled)
Slugginess of the smart mask. Default is 0 = DISABLED. 1 is slow, 10 is fast.
snapshot_filename Values: Max 4095 characters
Default: %v-%Y%m%d%H%M%S-snapshot
File path for snapshots (jpeg or ppm) relative to target_dir.
snapshot_interval Values: 0 - 2147483647
Default: 0 (disabled)
Make automated snapshots every 'snapshot_interval' seconds.
sql_log_image Values: on, off
Default: on
Log to the database when creating motion triggered image file.
sql_log_mpeg Values: on, off
Default: off
Log to the database when creating motion triggered mpeg file.
sql_log_snapshot Values: on, off
Default: on
Log to the database when creating a snapshot image file.
sql_log_timelapse Values: on, off
Default: off
Log to the database when creating timelapse mpeg file
sql_query Values: Max 4095 characters
Default: insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')
SQL query string that is sent to the database. The values for each field are given by using convertion specifiers
switchfilter Values: on, off
Default: off
Turns the switch filter on or off. The filter can distinguish between most switching noise and real motion. With this you can even set roundrobin_skip to 1 without generating much false detection.
target_dir Values: Max 4095 characters
Default: Not defined = current working directory
Target directory for picture and movie files.
text_changes Values: on, off
Default: off
Turns the text showing changed pixels on/off.
text_double Values: on, off
Default: off
Draw characters at twice normal size on images.
text_event Values: Max 4095 characters
Default: %Y%m%d%H%M%S
This option defines the value of the speciel event conversion specifier %C. You can use any conversion specifier in this option except %C. Date and time values are from the timestamp of the first image in the current event.
text_left Values: Max 4095 characters
Default: Not defined
User defined text overlayed on each in the lower left corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > ¦ , . : - + _ \n and conversion specifiers (codes starting by a %).
text_right Values: Max 4095 characters
Default: %Y-%m-%d\n%T
User defined text overlayed on each in the lower right corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > ¦ , . : - + _ \n and conversion specifiers (codes starting by a %). Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock
thread Values: Max 4095 characters
Default: Not defined
Specifies full path and filename for a thread config file. Each camera needs a thread config file containing the options that are unique to the camera. If you only have one camera you do not need thread config files. If you have two or more cameras you need one thread config file for each camera in addition to motion.conf. This option must be placed in motion.conf and not in a thread config file.
threshold Values: 1 - 2147483647
Default: 1500
Threshold for declaring motion. The threshold is the number of changed pixels counted after noise filtering, masking, despeckle, and labelling.
threshold_tune Values: on, off
Default: off
Activates the automatic tuning of threshold level.
timelapse_filename Values: Max 4095 characters
Default: %v-%Y%m%d-timelapse
File path for timelapse mpegs relative to target_dir (ffmpeg only).
track_auto Values: on, off
Default: off
Enable auto tracking
track_iomojo_id Values: 0 - 2147483647
Default: 0
Use this option if you have an iomojo smilecam connected to the serial port instead of a general stepper motor controller.
track_maxx Values: 0 - 2147483647
Default: 0
The maximum position for servo x.
track_motorx Values: -1 - 2147483647
Default: -1
The motor number that is used for controlling the x-axis.
track_move_wait Values: 0 - 2147483647
Default: 10
Delay during which tracking is disabled after auto tracking has moved the camera. Delay is defined as number of picture frames.
track_port Values: Max 4095 characters
Default: Not defined
This is the device name of the serial port to which the stepper motor interface is connected.
track_speed Values: 0 - 255
Default: 255
Speed to set the motor to.
track_step_angle_x Values: 0-90
Default: 10
Angle in degrees the camera moves per step on the X-axis with auto tracking. Currently only used with pwc type cameras.
track_step_angle_y Values: 0-40
Default: 10
Angle in degrees the camera moves per step on the Y-axis with auto tracking. Currently only used with pwc type cameras.
track_stepsize Values: 0 - 255
Default: 40
Number of steps to make.
track_type Values: 0 (none), 1 (stepper), 2 (iomojo), 3 (pwc), 4 (generic)
Default: 0 (None)
Type of tracker.
tunerdevice Values: Max 4095 characters
Default: /dev/tuner0
The tuner device used for controlling the tuner in a tuner card. This option is only used when Motion is compiled for FreeBSD.
video_pipe Values: Max 4095 characters
Default: Not defined
The video4linux video loopback input device for normal images. If a particular pipe is to be used then use the device filename of this pipe. If a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe.
videodevice Values: Max 4095 characters
Default: /dev/video0 (FreeBSD: /dev/bktr0)
The video device to be used for capturing. Default for Linux is /dev/video0. for FreeBSD the default is /dev/bktr0.
webcam_limit Values: 0 - 2147483647
Default: 0 (unlimited)
Limit the number of frames to number frames. After 'webcam_limit' number of frames the connection will be closed by motion. The value 0 means unlimited.
webcam_localhost Values: on, off
Default: on
Limits the access to the webcam to the localhost.
webcam_maxrate Values: 1 - 100
Default: 1
Limit the framerate of the webcam. Default is 1. Set the value to 100 for practically unlimited.
webcam_motion Values: on, off
Default: off
If set to 'on' Motion sends slows down the webcam stream to 1 picture per second when no motion is detected. When motion is detected the stream runs as defined by webcam_maxrate. When 'off' the webcam stream always runs as defined by webcam_maxrate.
webcam_port Values: 0 - 65535
Default: 0 (disabled)
TCP port on which motion will listen for incoming connects with its webcam server.
webcam_quality Values: 1 - 100
Default: 50
Quality setting in percent for the mjpeg picture frames transferred over the webcam connection. Keep it low to restrict needed bandwidth.
width Values: Device Dependent
Default: 352
The width in pixels of each frame. Valid range is camera dependent.
+

+

+

+

+

Signals (sent with e.g. kill command)

+A signal can be sent from the command line by typing e.g. kill -s SIGHUP pid, where the last parameter is the process ID which you get by typing ps -ef ¦ grep motion. The PID is the first on the list which is the parent process for the threads. +Motion responds to the following signals: +

+ + + + + +
Signal Description Editors comment
SIGHUP The config file will be reread. This is a very useful signal when you experiment with settings in the config file.
SIGTERM If needed motion will create an mpeg file of the last event and exit  
SIGUSR1 Motion will create an mpeg file of the current event.  
+

+

+

Error Logging

+Motion reports errors to the console when it runs in non-daemon mode. And it outputs even more information when run in setup mode. +

+Error logging has been implemented so that errors during daemon (background) mode are logged in the syslog. +

+The syslog is in most Linux systems the file /var/log/messages (e.g. RedHat/Fedora) or /var/log/syslog and /var/log/user.log (e.g. Debian). +

+

+

Motion Guide - Basic Features

+

+

Capture Device Options - The Basic Setup

+Before you can start using motion you need to know some basics about your camera. +Either you have a camera connected directly to your computer. In this case it is a video4linux type of camera. Or you connect to a network camera using a normal web URL. +

+

video4linux (V4L) devices

+You need to install your camera with the right driver. It is out of scope of this document to tell you how to do this and it depends on which type of camera. +

+Once installed the camera(s) will have the device names /dev/video0, /dev/video1, /dev/video2... +

+FreeBSD has a different naming of devices. When you build Motion for FreeBSD the default device name is /dev/bktr0. Under FreeBSD a TV card has a special device for controlling the tuner (e.g. /dev/tuner0). The option tunerdevice is only valid when Motion is built and running under FreeBSD. For Linux do not include this option in the config file (remove or comment out). +

+USB cameras take a lot of bandwidth. A USB camera connected to a USB 1.1 port or hub consumes all the bandwidth. Even with a small framesize and low framerate you should not expect to have more than one camera per USB 1.1 controller. If you need more than 1 USB camera add extra USB PCI cards to your computer. There exists cards that have 4 inputs each with their own controller and with full bandwidth. Many 4-input cards only have 1 controller. USB cameras do not have the feature of selecting input channels. To disable the input selection the option input must be set to the value 8 for USB cameras. +

+Composite video cards are normally made with a chip called BT878 (older cards have a BT848). They all use the Linux driver called 'bttv'. +

+There are cards with more then one video input but still only one BT878 chip. They have a video multiplexer which input is selected with the config option input. Input channel numbers start at 0 (which is why the value 8 and not 0 disables input selection). There are video capture cards available with 4 or 8 inputs but only one chip. They present themselves as one single video device and you select input using the 'input' option. If you define e.g. 4 thread config files with the same videodevice name but different input numbers Motion automatically goes into round robin mode. See the round robin section for more information. Many TV tuner cards have the input channels: TV Tuner = 0, Standard composite video = 1, S-VHS = 3. Other have TV=0, composite video 1= 1, composite video = 2, S-VHS = 3. For video capture cards input 1 is normally the composite video input. +

+Some capture cards are specially made for surveillance with for example 4 inputs. Others have a TV tuner, a composite input (phono socket) and perhaps also a S-VHS input. For all these cards the inputs are numbered. The numbering varies from card to card so the easiest is to experiment for 5 minutes with a program that can show the videostream. Use a program such as Camstream or xawtv to experiment with the values. +

+If you use the TV tuner input you also need to set the frequency of the TV channel using the option frequency. Otherwise set 'frequency' to 0. +

+Finally you need to set the TV norm. Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default is 0 (PAL). If your camera is a PAL black and white you may get a better result with norm=3 (PAL no colour). +

+If the netcam_url option is defined all the video4linux options are ignored so make sure the netcam_url option is commented out if you do not need it. +

+These are the parameters used for video4linux devices +

+

+

+

auto_brightness

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Let motion regulate the brightness of a video device. Only recommended for cameras without auto brightness +

+Motion will try to adjust the brightness of the video device if the images captured are too dark or too light. This option will be most useful for video devices like web cams, which sometimes don't have such an option in hardware. +

+The auto_brightness feature will adjust the brightness of the device up or down until the value defined by the option brightness is reached (1 = dark, 255 = bright). If brightness is zero auto_brightness will try to adjust to the average brightness level 128. +

+You need to know if the camera supports auto brightness. Most cameras have auto everything. If your video device already does this for you this option might cause oscillations. If you do not know assume that it has and do not use the Motion auto brightness feature. At least not to start with. +

+

+

+

+

+

brightness

+
    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 255 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+The brightness level for the video device. +

+Value 0 means that Motion does not set the brightness value but leaves it unchanged. +

+If this setting is used in conjunction with the auto_brightness feature then this setting is the average brightness level in the range 1 (dark) to 255 (bright) that the auto_brightness feature will try to achieve by adjusting the device brightness up and down. +

+

+

+

+

contrast

+
    +
  • Type: Boolean +
  • +
  • Range / Valid values: 0 - 255 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+The contrast level for the video device. +

+Disabled (Value 0) means that Motion does not set the contrast value. +

+

+

+

+

framerate

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 2 - 100 +
  • +
  • Default: 100 (no limit) +
  • +
  • Option Topic +
  • +
+

+Maximum number of frames to be captured from the camera per second. +

+The faster you fetch pictures from the camera the more CPU load you get and the more pictures get included when Motion is detected. +

+Motion will stop storing pictures if the framerate is set to less than 2. +

+Set this parameter to the maximum number of images per second that you want to store either as images or as mpeg films. +

+To set intervals longer than one second use the 'minimum_gap' option instead. +

+

+

+

+

frequency

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: 0 - 999999 +
  • +
  • Default: 0 (Not set) +
  • +
  • Option Topic +
  • +
+

+The frequency to set the tuner to (kHz). Valid range: per tuner spec, default: 0 (Don't set it) +

+This option is only relevant if you have a TV tuner card where you can select the tuner frequency. Your tuner card must support this feature. +

+

+

+

+

height

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: Device Dependent +
  • +
  • Default: 288 +
  • +
  • Option Topic +
  • +
+

+The height of each frame in pixels. +

+The height of the image in pixels. Motion does not scale so should be set to the actual size of the v4l device. In case of a net camera motion sets the height to the height of the first image read. +

+Motion actually set the size of the image coming from the video4linux device. +

+Your camera or capture/TV card will not support any picture size. You must know which frame size (width and height) the camera supports. If you do not know start with width 320 and height 240 which most cameras and capture cards supports. +

+For some device drivers like pwc (driver for Philips USB cameras) setting the size to a non-standard value makes the driver create an image of the nearest smaller size and create a gray band around the image to fit the size given by motion. Note that it is the driver and not motion that generates the gray band. Motion will try to detect motion in the entire image including the gray band. +

+Motion requires that dimensions of camera image must have both height and width that are a multiple of 16. Thís is normally not a problem. All standard sizes like 640, 480, 352, 320, 288, 240, ...etc are multiples of 16. +

+

+

+

+

hue

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 255 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+The hue level for the video device. +

+Normally only relevant for NTSC cameras. +

+

+

+

+

input

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 7, 8 = disabled +
  • +
  • Default: 8 (disabled) +
  • +
  • Option Topic +
  • +
+

+Input channel to use expressed as an integer number starting from 0. Should normally be set to 1 for video/TV cards, and 8 for USB cameras. +

+This parameter is really used only with video capture cards that has more than one input. +

+However if you set the input number to e.g. 1 for a USB camera (ov511 or pwc driver) motion writes an error message back. If you set it to 8 it does not give you any warning. +

+If you have a video capture card you can define the channel to tune to using this option. If you are using a USB device, network camera or a capture card without tuner you should set the value to the default 8. +

+Many TV tuner cards have the input channels: TV Tuner = 0, Standard composite video = 1, S-VHS = 3. Other have TV=0, composite video 1= 1, composite video = 2, S-VHS = 3. It is recommended to set the parameter to 8 for USB cameras as your first try. For video capture cards input 1 is normally the composite video input. +

+

+

+

+

norm

+

+

    +
  • Type: Discrete Strings +
  • +
  • Range / Valid values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour) +
  • +
  • Default: 0 (PAL) +
  • +
  • Option Topic +
  • +
+

+Select the norm of the video device. Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +

+This value is only used for capture cards using the BTTV driver. +

+

+

+

+

rotate

+

+

    +
  • Type: Discrete Strings +
  • +
  • Range / Valid values: 0, 90, 180, 270 +
  • +
  • Default: +
  • +
  • Option Topic +
  • +
+

+Rotate image the given number of degrees. The rotation affects all saved images as well as mpeg movies. +

+The rotation feature is used when the camera is hanging upside down (180 degrees) or if you choose a picture format in portrait instead of the normal landscape (90 or 270 degrees). +

+Note that the CPU load increases when using this feature with a value other than 0. Also note that Motion automatically swaps width and height if you rotate 90 or 270 degrees, so you don't have to touch these options. +

+

+

+

+

saturation

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 255 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+The colour saturation level for the video device. +

+

+

+

+

+

+

tunerdevice

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: /dev/tuner0 +
  • +
  • Option Topic +
  • +
+

+The tuner device used for controlling the tuner in a tuner card. This option is only used when Motion is compiled for FreeBSD. +

+Make sure to remove or comment out this option when running Motion under Linux. +

+

+

+

+

videodevice

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: /dev/video0 (FreeBSD: /dev/bktr0) +
  • +
  • Option Topic +
  • +
+

+The video device to be used for capturing. Default for Linux is /dev/video0. for FreeBSD the default is /dev/bktr0. +

+This is the video4linux device name. Ignore this for net cameras. +

+

+

+

+

width

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: Device Dependent +
  • +
  • Default: 352 +
  • +
  • Option Topic +
  • +
+

+The width in pixels of each frame. Valid range is camera dependent. +

+Motion does not scale so should be set to the actual size of the v4l device. +

+In case of a net camera motion sets the height to the height of the first image read. +

+Motion actually set the size of the image coming from the video4linux device. +

+Your camera or capture/TV card will not support any picture size. You must know which frame size (width and height) the camera supports. If you do not know start with width 320 and height 240 which most cameras and capture cards supports. +

+For some device drivers like pwc (driver for Philips USB cameras) setting the size to a non-standard value makes the driver create an image of the nearest smaller size and create a gray band around the image to fit the size given by motion. Note that it is the driver and not motion that generates the gray band. Motion will try to detect motion in the entire image including the gray band. +

+Motion requires that dimensions of camera image must have both height and width that are a multiple of 16. Thís is normally not a problem. All standard sizes like 640, 480, 352, 320, 288, 240, ...etc are multiples of 16. +

+

+

+

Network Cameras

+Motion can connect to a network camera through a normal TCP socket. All you need to give it is the URL. The URL given must return either one single jpeg picture or an mjpeg stream. For the time being Motion cannot connect to a video stream such a mpeg, mpeg4, divx. The URL must return one single jpeg image or an mjpeg stream! You can connect through a proxy server. +

+Also watch out that you do not use a URL that create an HTML page with an embedded jpg. What must be returned is the jpg picture itself or the raw mjpeg stream. +

+When the netcam_url is defined all the video4linux options above are ignored!! +

+If the connection to a network camera is lost - Motion will reuse the last good image for approx 30 seconds. AFter 30 seconds the image is replaced by a grey image with a text telling that the signal is lost and when the connection was lost. This text and its date format is not configurable and there are no plans to make it configurable in order to keep the number config options under control. +

+Note that Motion requires that dimensions of camera image must have both height and width that are a multiple of 16. Thís is normally not a problem. All standard sizes like 640, 480, 352, 320, 288, 240, ...etc are multiples of 16. But if you intend to monitor a network camera which is saving jpeg images you may have to pay attention to the dimensions of the picture. +

+The network camera feature has been completely re-written in Motion 3.2.2. We believe the netcam feature is much more stable now that it was in previous versions. Motion tries to reconnect to the camera if the connection is lost. There is no official standard for mjpeg and we know that there are probably still some cameras that are not yet supported. If you run into a problem please file a Bug Report with as much information about the format as possible. A binary raw dump of the first 2-3 frames with headers and boundary strings is very useful. You can see how to make it on the special topic NetcamMjpegStreamDumps. When you have the file you can upload it to the same topic. +

+

+

+

netcam_proxy

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+URL to use for a netcam proxy server, if required. The syntax is http://myproxy:portnumber +

+Use this if you need to connect to a network camera through a proxy server. +

+Example of syntax: "http://myproxy.mydomain.com:1024 +

+If the proxy port number is 80 you can ommit the port number. Then the syntax is use "http://myproxy.mydomain.com" . +

+Leave this option undefined if you do not use a proxy server. +

+

+

+

+

netcam_url

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Specify an url to a downloadable jpeg file or raw mjpeg stream to use as input device. Such as an AXIS 2100 network camera. +

+Example of URL: http://www.gate.com/pe1rxq/jeroen.jpg. +

+Motion can connect to a network camera through a normal TCP socket. All you need to give it is the URL. The URL given must return either one single jpeg picture or an mjpeg stream. For the time being Motion cannot connect to a video stream such a mpeg, mpeg4, divx. The URL must return one single jpeg image or an mjpeg stream! +

+Also watch out that you do not use a URL that create an HTML page with an embedded jpg. What must be returned is the jpg picture itself or the raw mjpeg stream. +

+When the netcam_url is defined all the video4linux options are ignored!! +

+Motion can also fetch jpeg pictures via ftp. You then use the ftp:// syntax instead. +

+

+

+

+

netcam_userpass

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+For network cameras protected by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. +

+To use no authentication simply remove this option from the config file comment it out with "#" or ";" in front. +

+

+

+

+ +

Round Robin feature

+This feature is automatically activated where multiple threads are sharing the same video device (for example /dev/video0). Each thread can then set different input channels to change camera with the input option or by tuning the tuner with frequency option. +

+ALERT! Round Robin is not relevant for Network cameras or standard USB web cameras. The Round Robin feature is used with video capture cards which have multiple inputs per video chip. +

+ALERT! Note that round robin is not the ideal way to run multiple cameras. When the capture card changes input it takes a little while before the decoder chip has syncronized to the new camera. You can improve this if you have expensive cameras with a syncronize input. Only one camera can be decoded at a time so if you have 4 cameras connected 3 of the camera threads will need to wait for their turn. The fact that cameras have to take turns and the fact that you have to skip a few frames after each turn dramatically lowers the possible framerate. You can get a high framerate by viewing each camera for a long time. But then you may miss the action on one of the inactive cameras. If you can affort it avoid Round Robin and buy the more expensive type of capture cards that has one decoder chip per input. If you only need 2 or 3 cameras you can also simply put 2 or 3 cheap TV cards in the computer. Linux has no problem working with multiple TV cards. +

+

    +
  • If multiple threads use the same video device, they each can capture roundrobin_frames number of frames before having to share the device with the other threads. +
  • +
  • When another thread wants to watch another input or frequency or size the first roundrobin_skip number of frames are skipped to allow the device to settle. +
  • +
  • The last option switch_filter is supposed to prevent the change of camera from being detected as Motion. Unfortunately it seems that many have problems detecting motion at all when this option is enabled. We are working on improving it. You are probably better off turning this option off. +
  • +
+

+These are the special Round Robin options +

+

+

+

roundrobin_frames

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 2147483647 +
  • +
  • Default: 1 +
  • +
  • Option Topic +
  • +
+

+Specifies the number of frames to capture before switching inputs, this way also slow switching (e.g. every second) is possible. +

+The Round Robin feature is automatically activated where multiple threads are sharing the same video device. Each thread can then set different input channels or frequencies to change camera. +

+If multiple threads use the same video device, they each can capture roundrobin_frames number of frames before having to share the device with the other threads. +

+

+

+

+

roundrobin_skip

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 2147483647 +
  • +
  • Default: 1 +
  • +
  • Option Topic +
  • +
+

+Specifies the number of frames to skip after a switch. (1 if you are feeling lucky, 2 if you want to be safe). +

+The Round Robin feature is automatically activated where multiple threads are sharing the same video device. Each thread can then set different input channels or frequencies to change camera. +

+When another thread wants to watch another input or frequency or size the first roundrobin_skip number of frames are skipped to allow the device to settle. +

+

+

+

+

switchfilter

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Turns the switch filter on or off. The filter can distinguish between most switching noise and real motion. With this you can even set roundrobin_skip to 1 without generating much false detection. +

+This is a round robin related feature used when you have a capture card with multiple inputs (controlled by the 'input' option) on the same videodevice. +

+ALERT! This feature was seriously broken until Motion 3.2.4 +

+

+

+

+

+

Motion Detection Settings

+These are the options that controls the detection of motion. Further details follows after. +

+

+

+

despeckle

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: EedDl +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Despeckle motion image using combinations of (E/e)rode or (D/d)ilate. And ending with optional (l)abeling. +

+A way of tuning (by removing or enhancing) noise in the motion image. Options for the despeckle feature are any of 'e', 'E', 'd' or 'D'. This can be combined by a trailing 'l' (letter l) which enables the labeling feature. Default: Not Defined (Don't despeckle and label). +

+Wind blowing grass and trees around or poor light conditions can cause a lot of dots (or noise) to appear in the motion image (See the section on Tuning Motion). This feature removes (or enhances!) this noise and so improves the reliability of motion. +

+The 'e' option removes diamonds, 'E' removes squares and alternating eE will remove circles. Each e/E you add will shrink the noise by a pixel all the way around. So 'despeckle Ee' will remove circles of radius 2. However, this will also shrink the detection by 2 and will affect the threshold. So to remove noise and then restore the detected motion to its original size try 'despeckle EedD'. +

+After the despeckle feature is done you can let the labeling feature search for areas of connected pixels and "label" each area. The program will now trigger motion based on the number of changed pixels in the largest area. In other words, the largest labeled area has to be above the threshold to trigger a motion detected. +

+The value EedDl is a good starting point. The possible combinations are endless and it requires many experiments to find the best combination. Just remember that the labeling feature only works as intended if it runs after the despeckle feature. Ie. the letter 'l' must be the last letter and only one 'l'. +

+If you have very few problems with false detections leave this option either blank or at EedD which will remove most of the single pixel noise. +A very detailed technical explanation of the despeckle part can be found at the webpage of the author of this feature Ian McConnell's Webcam: Motion Web Page +

+

+

+

+

gap

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 60 +
  • +
  • Option Topic +
  • +
+

+Gap is the seconds of no motion detection that triggers the end of an event. An event is defined as a series of motion images taken within a short timeframe. +

+Recommended value is 60 seconds (Default). The value 0 is allowed and disables events causing all Motion to be written to one single mpeg file and no pre_capture. You can force an event to end and a new to begin using the http control 'http://host:port/thread_number/action/makemovie'. +

+An event is defined as a series of motion images taken within a short timeframe. E.g. a person walking through the room is an event that may have caused 10 single jpg images to be stored. This option defines how long a pause between detected motions that is needed to be defined as a new event. +

+The gap timer starts after the last motion is detected and post_capture images have been saved and appended to open movie mpeg files. +

+Any motion detected before the gap timer times out resets the gap timer so it starts counting over again. +

+Detailed Description +

+The option 'gap' is important. It defines how long a period of no motion detected it takes before we say an event is over. An event is defined as a series of motion images taken within a short timeframe. E.g. a person walking through the room is an event that may have caused 10 single jpg images to be stored. Motion detected includes post_captured frames set by the 'post_capture' option. The 'gap' option defines how long a pause between detected motions that is needed to be defined as a new event. A good starting value is 60 seconds. +

+The way 'gap' works in more technical terms is: +

    +
  • Gap is a timer that timeout 'gap' seconds after the last video frame with motion is detected. +
  • +
  • If 'post_capture' is activated then the gap timer starts counting after the last image of the post_capture buffer has been saved. +
  • +
  • The gap timer is reset and starts all over each time new motion is detected, so you will not miss any action by having a short 'gap' value. It will just create more events (e.g. more mpegs files) +
  • +
+

+The gap value impacts many functions in Motion. +

    +
  • When the gap timer runs out the event number is increased by one next time motion is detected. When you use the %v conversion specifier in filenames or text features this means that the number in filename or text increased by one. +
  • +
  • The pre_capture feature only works at the beginning of an event. So if you have a very large 'gap' value pre_capture is not working very often. +
  • +
  • When you make mpegs using the ffmpeg features a new mpeg file is started at the beginning of an event when the first motion is detected. When 'gap' seconds has passed without motion (and post_captured frames saved) the mpeg files is completed and closed. +
  • +
  • Do not use large gap values to generate one large mpeg4 file. If Motion stops working this mpeg4 file never gets properly completed and closed and you will not be able to view it. +
  • +
  • Some of the tracking features sets the camera back to the center position when an event is over. +
  • +
+

+Note that 'gap' and 'minimum_gap' have nothing to do with each other. +

+

+

+

+

lightswitch

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 100 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Ignore sudden massive light intensity changes given as a percentage of the picture area that changed intensity. +

+Experiment to see what works best for your application. +

+Note: From version 3.1.17 (snap release 2 and on) this option has changed from a boolean (on or off) to a number in percent between 0 and 100. Zero means the option is disabled. +

+The value defines the picture areas in percent that will trigger the lightswitch condition. When lightswitch is detected motion detection is disabled for 5 picture frames. This is to avoid false detection when light conditions change and when a camera changes sensitivity at low light. +

+

+

+

+

low_cpu

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 100 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+When this option is not zero motion will be in a low cpu mode while not detecting motion. In low cpu mode Motion reduces the framerate to the value given for this option. Value zero means disabled. +

+This is smart for running a server that also does other tasks such as running Apache, MySQL etc. Motion grabs this lower number of frames per second until it detects motion. Then it speeds up to normal speed and take pictures as set by the option "framerate". +

+

+

+

+

mask_file

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+PGM file to use as a sensitivity mask. This picture MUST have the same width and height as the frames being captured and be in binary format. +

+Full path of the PGM (portable gray map) mask file (binary format). +

+If you have one or more areas of the camera image in which you do NOT want motion detected (e.g. a tree that moves in the wind or a corner of the picture where you can see cars/pedestrians passing by) you need a mask file. This file is a picture that you create in your favorite photo editing program. The areas that you want detected must be white. The error that you want ignored must be black. The pgm image must be the same size (number of pixels high and wide) as the pictures that are taken by the camera (video4linux device). +

+You can adjust sensitivity by using gray tones. +

+If you do not have a mask file disable this option by not having it in the config file or comment it out ("#"or ";" as first character in line). +If you are using the rotate option, note that the mask is applied after the rotation. +

+Detailed Description +

+The mask file must be a pgm format image file (portable gray map). Note that you must choose the BINARY format. +

+The feature is simple. Create an image of exact the same size as the ones you get from your video device (camera). Make a purely white picture and paint the areas that you want to mask out black. You can also make gray areas where you want to lower the sensitivity to motion. Normally you will stick to pure black and white. +

+One easy method for generating the mask file is as follows. +

+You can just take a motion captured picture, edit it with black and white for the mask and save it as a pgm file. +If you cannot save in this format save as a grayscale jpg and then you can convert it to pgm format with +

+djpeg -grayscale -pnm [inputfile] > mask.pgm +

+(assuming you have djpeg installed - part of the jpeg lib package). +

+Note that the mask file option masks off the detection of motion. The entire picture is still shown on the picture. This means that you cannot use the feature to mask off an area that you do not want people to see. +

+Below are an example of a webcam picture and a mask file to prevent the detection cars in the street. +

+Normal picture. Notice the street is visible through the hedge. +

+normal.jpg +

+Mask file (converted to png format so it can be shown by your web browser) +

+ + +
mask1.png
+

+

+

+

+

max_mpeg_time

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 (infinite) - 2147483647 +
  • +
  • Default: 3600 +
  • +
  • Option Topic +
  • +
+

+The maximum length of an mpeg movie in seconds. Set this to zero for unlimited length. +

+

+

+ +

+

+

+

+

minimum_gap

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 0 (no minimum) +
  • +
  • Option Topic +
  • +
+

+The minimum time between two shots in seconds. +

+This is the minimum gap between the storing pictures while detecting motion. +

+The value zero means that pictures can be stored almost at the framerate of the camera. Normally you will set this to 0 +

+This option has nothing to do with the 'gap' option. +

+

+

+

+

minimum_motion_frames

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: 1 - 1000s +
  • +
  • Default: 1 +
  • +
  • Option Topic +
  • +
+

+Picture frames must contain motion at least the specified number of frames in a row before they are detected as true motion. At the default of 1, all motion is detected. Valid range is 1 to thousands, but it is recommended to keep it within 1-10. +

+Note that the picture frames are buffered by Motion and once motion is detected also the first frames containing motion are saved so you will not miss anything. +

+The feature is used when you get many false detections when the camera changes light sensitivity or light changes. +

+Experiment for best setting. Even though Motion accepts large values you should set this to a relatively low number (below 10). For each step larger than 1 Motion reserves space in RAM for the picture frame buffer. If you have a large value Motion will miss many frames from the camera while it is processing the all the pictures in the buffer. +

+

+

+

+

night_compensate

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+When this option is set the noise threshold will be lowered if the picture is dark. This will improve the sensitivity in dark places. However it might also increase the number of false alarms since most cameras also compensate for this with their AGC which will increase noise. +

+It has normally been the advice not to use this with 'noise_tune' turned on. However the latest experience is that with the new improved noise_tune algorithm it actually works fine in combination with 'night_compensate'. +

+

+

+

+

noise_level

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 255 +
  • +
  • Default: 32 +
  • +
  • Option Topic +
  • +
+

+The noise level is used as a threshold for distinguishing between noise and motion. +

+This is different from the threshold parameter. This is changes at pixel level. The purpose is to eliminate the changes generated by electric noise in the camera. Especially in complete darkness you can see the noise as small grey dots that come randomly in the picture. This noise can create false motion detection. What this parameter means is that the intensity of a pixel must change more than +/- the noise threshold parameter to be counted. +

+

+

+

+

noise_tune

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Activates the automatic tuning of noise level. +

+This feature makes Motion continuously adjust the noise threshold for distinguishing between noise and motion. The 'noise_level' setting is ignored when activating this feature. This is a new feature and new algorithm. It may give different results depending on camera and light conditions. Report your experience with it on the Motion mailing list. If it does not work well, deactivate the 'noise_tune' option and use the manual setting of 'noise_level' instead. +

+

+

+

+

output_all

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Picture are saved continuously as if motion was detected all the time. +

+This feature is not meant to be the normal mode of operation. Especially not if you have the output_normal or output_motion features enabled since it will keep on saving pictures on the disk and you will soon run out of disk space. So be careful with this command. +

+If your frame rate is 10 pictures per second motion will save 10 new picture pr second until the disk is full. +

+It does all the normal actions that are done when motion is detected. It saves pictures on the harddisk, execute external scripts, etc as fast as the frame rate of the camera. So it is probably a good idea to run with a low framerate when using this feature and to not use activate all the features that saves files on the disk. +

+The idea of this feature is that you can turn the feature on and off for a short period of time to test or to generate continuous mpeg films when needed. +

+

+

+

+

post_capture

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Specifies the number of frames to be captured after motion has been detected. +

+The purpose of this is mainly to create smooth video clips each time motion is detected. Use it to you personal taste (and disk space).. +

+This option is the preferred way to create continuous movies. Post_capture does not consume extra RAM and it does not create pauses in the movie even with large values. +

+If you only store mpegs movies and do not have output_normal on, then the recommended post_capture value is what is equivalent to 1-5 seconds of movie. +

+

+

+

+

pre_capture

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 100s +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Specifies the number of previous frames to be outputted at motion detection. Recommended range: 0 to 5, default=0. Do not use large values! Large values will cause Motion to skip video frames and cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead. +

+Motion buffers the number of picture frames defined by 'pre_capture'. When motion is detected the pictures in the buffer are included in the video clip generated by ffmpeg. The effect is that it seems the program knew in advance that the event was going to take place and started the recording before it actually happened. This is a nice feature that give more complete video clips of an event. +

+If pre_capture is set to 0 the feature is disabled. Keep this value below 5. +

+The recommended value would be approx 0.5 second of video so the value should be defined so it fits the framerate and the desired pre-capture time. E.g. 0.5 second at 20 frames pr second would mean a value of 5. You should never use a value larger than 10. +

+You can in theory have up to 100s of pre-captured frames but naturally this makes motion leave a larger footprint in the memory of the computer. More important Motion is processing all the buffered images including saving jpegs, encoding mpegs, writing to databases and executing external programs after the first image is detected as Motion. +

+Motion will not grab another image until this is done. This means that even moderate values for pre_capture combined with high framerates will mean that you will miss quite many frames of Motion. It is therefore recommended to use relative small values for pre_capture. Depending on your chosen framerate and depending on the features enabled values from 1-5 are sensible. +

+If you wish to create smooth mpegs during events using large pre_capture values will do the opposite! It will create a long pause where a lot of action is missed. +

+To get a smooth mpeg use a large value for post_capture which does not cost any performance hit or RAM space. +

+

+

+

+

smart_mask_speed

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 10 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Slugginess of the smart mask. Default is 0 = DISABLED. 1 is slow, 10 is fast. +

+Smartmask is a dynamic, self-learning mask. Smartmask will disable sensitivity in areas with frequent motion (like trees in the wind). Sensitivity is turned on again after some time of no more motion in this area. The built mask is a bit larger at the borders than the actual motion was. This way smartmask works more reliable when sudden moves occur under windy conditions. +

+smart_mask_speed - tunes the slugginess of the mask. It accepts values from 0 (turned off) to 10 (fast). Fast means here that the mask is built quick, but it is also not staying very long with no more motion. Slow means that it takes a while until the mask is built but it also stays longer. A good start value for smart_mask_speed is 5. This setting is independent from the framerate. The attack and decay time is constant over all available framerates. +

+When smartmask is enabled and motion is also configured to either write motion-images or motion-mpegs, the current smartmask is copied as an overlay into the black/white motion-pictures/mpegs in red colour. Same thing happens to the webcam stream when Motion runs in setup_mode. That way you can easily adjust smart_mask_speed. +

+Detailed Description +

+The mask_file option provides a static mask to turn off sensitivity in certain areas. This is very usefull to mask a street with cars passing by all day long etc... +

+But imagine a scenario with large bushes and big trees where all the leaves are moving in the wind also triggering motion from time to time even with despeckle turned on. Of course you can also define a static mask here, but what if the bushes are growing during spring and summer? Well, you have to adapt the mask from time to time. What if the camera position moves slightly? What if someone grows new plants in your garden? You always have to setup a new static mask. +

+The answer to this problem is the smart mask feature introduced in Motion 3.1.18. A dynamic, self-learing mask. +

+Smart mask will disable sensitivity in areas with frequent motion (like trees in the wind). Sensitivity is turned on again after some time of no more motion in this area. The built mask is a bit larger at the borders than the actual motion. This way smartmask works more reliably when sudden moves occur under windy conditions. +

+

+

+

+

+

threshold

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 2147483647 +
  • +
  • Default: 1500 +
  • +
  • Option Topic +
  • +
+

+Threshold for declaring motion. The threshold is the number of changed pixels counted after noise filtering, masking, despeckle, and labelling. +

+The 'threshold' option is the most important detection setting. When motion runs it compares the current image frame with the previous and counts the number of changed pixels after having processed the image with noise filtering, masking, despeckle and labeling. If more pixels than defined by 'threshold' have changed we assume that we have detected motion. Set the threshold as low as possible so that you get the motion you want detected but large enough so that you do not get detections from noise and plants moving. Note that the larger your frames are, the more pixels you have. So for large picture frame sizes you need a higher threshold. +

+Use the -s (setup mode) command line option and/or the text_changes config file option to experiment to find the right threshold value. If you do not get small movements detected (see the mouse on the kitchen floor) lower the value. If motion detects too many birds or moving trees, increase the number. Practical values would be from a few hundred to 2000 indoors and 1000-10000 outdoors. +

+

+

+

+

threshold_tune

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Activates the automatic tuning of threshold level. +

+This feature makes Motion continuously adjust the threshold for declaring motion. +

+The threshold setting is ignored when activating this feature. It may give different results depending on your camera, light conditions, indoor/outdoor, the motion to be detected etc. If it does not work well, deactivate the 'threshold_tune' option and use the manual setting of threshold instead. +

+

+

+

+

+

Image File Output

+The following options controls how Motion generates images when detection motion. +

+

+

+

output_motion

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Output pictures with only the moving object. This feature generates the special motion type movies where you only see the pixels that changes as a graytone image. If labelling is enabled you see the largest area in blue. Smartmask is shown in red. +

+Motion images shows the motion content of the pictures. This is good for tuning and testing but probably not very interesting for the general public. +

+Default is not to store motion images. Motion pictures are stored the same place and with the same filename as normal motion triggered pictures except they have an "m" appended at the end of the filename before the .jpg or .ppm. E.g. the name can be 01-20020424232936-00m.jpg. +

+

+

+

+

output_normal

+

+

    +
  • Type: Discrete Strings +
  • +
  • Range / Valid values: on, off, first, best +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Normal image is an image that is stored when motion is detected. It is the same image that was taken by the camera. I.e. not a motion image like defined by output_motion. Default is that normal images are stored. +

+If you set the value to 'first' Motion saves only the first motion detected picture per event. +

+If you set it to "best" Motion saves the picture with most changed pixels during the event. This is useful if you store mpegs on a webserver and want to present a jpeg to show the content of the mpeg on a webpage. "best" requires a little more CPU power and resources compared to "first". +

+

+

+

+

ppm

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Output ppm images instead of jpeg. This uses less CPU time, but causes a LOT of hard disk I/O, and it is generally slower than jpeg. +

+The recommendation is to always use jpg except if you have a specific need to store high quality pictures without any quality loss. For web cameras you should always choose jpg. Note that the built in webcam server requires that this parameter is set to off. +

+

+

+

+

quality

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 100 +
  • +
  • Default: 75 +
  • +
  • Option Topic +
  • +
+

+The quality for the jpeg images in percent. +

+100 means hardly compressed. A small number means a much smaller file size but also a less nice quality image to look at. 50 is a good compromise for most. +

+

+

+

+

+

+

Tuning Motion

+

+Motion 3.2 introduces a new feature Setup Mode. This is a great new feature with really make tuning all the settings of Motion much more easy and transparent. In setup mode two things happen: +

+

    +
  1. With 'motion -s' Motion runs in console mode instead of daemon. It outputs a lot of useful information for each frame from the camera. Each message is prefixed by [number] where number is the camera number (thread number). +
  2. +
  3. When you look at the mjpeg webcam stream you see a black image with numbers. What you see is the number of changed pixels, number of labeled areas and noise setting. When something moves you see the pixels detected as Motion in black and white. The largest labelled area (assuming despeckle is enabled and with the 'l' at the end) is blue. It is only the blue areas which is counted as Motion. If smartmask is enabled you see this as red areas. +
  4. +
+

+Here is a suggestion how to initially setup Motion. +

+

    +
  • Disable despeckle (comment it out in motion.conf). +
  • +
  • Disable smartmask +
  • +
  • Enable both http control and webcam by setting port numbers. Example 8080 for control and 8081 for webcam. +
  • +
  • Start Motion in setup mode +
  • +
  • View the webcam stream. Either with Cambozola or with Firefox. http://localhost:8081/ Firefox often needs to reload the page before it works. Bug in Firefox. Internet Explorer cannot show the stream unless you make a webpage on your Apache with Cambozola applet. +
  • +
  • Open new browser window and connect to the http interface. http://localhost:8080/ . You can now control and change almost anything while Motion is running. You cannot resize the image. That was too hard to code. To disable a feature enter a space. +
  • +
  • Start by experimenting with noise level. Do this both during daylight and during darkness. You will be surprised to see how much noise a camera makes during night. Try using the automatic noise feature. It should work for most. +
  • +
  • Now try the despeckle feature. Enable it using the recommended default EedDl. If this is not enough experiment. Remember that the l must be the last letter. It is fun to play with. +
  • +
  • Set the threshold to what you want to trigger Motion. +
  • +
+

+In normal mode you can use the same setting with two browser windows and experiment with settings of the camera if needed. +

+From the web interface you can ask Motion to write all your changes back to the config files (motion.conf and thread config files). It will even tidy them up for you so they look nice. +

+There are two sets of options to adjust. +

+

+


+

+Normal picture frame +

+outputnormal1.jpg +

+


+Motion type picture frame with despeckle. Note that the largest area is blue and only this is counted as Motion. +

+The Motion image shows how Motion maintains a "reference frame" which is not just the last picture frame but a matematical calculation of the past images. This enlarges real Motion and ensures that it is not easy to sneak in slowly. +

+outputmotion1.jpg +


+

+

+

Generating MPEG films with ffmpeg

+The ffmpeg option can generate mpeg films very fast and "on the fly". This means that the mpeg film is growing each time motion is detected. +

+Some people on the Motion mailing list have had trouble building the ffmpeg package because they did not have the NASM assembler package installed. So pay attention to this if you run into problems. +

+ffmpeg exists as binary packages for most distributions including RPMs and debian packages. +

+Ffmpeg is a strange project. The developers never release anything. The official releases are totally out of date now. They want you to take your chance and checkout a version from their CVS server and hope you are lucky and getting a version that works. See ffmpeg project page. Sad that such an important project is being run by a bunch of spoiled kids (oppinion of KennethLavrsen and many frustrated OSS project managers). +

+In order to help people finding a version of ffmpeg that works we have started testing the Motion package with a selection of binaries and a CVS snapshot. The CVS source snapshot of ffmpeg which is certified with Motion is available on the Related projects file area on the Motion Sourceforge project +

+Motion works with the following versions of ffmpeg: +

    +
  • ffmpeg-0.4.8. With this release Motion supports mpeg1, mpeg4 and msmpeg4. Lately newer distributions have problems building this 2003 release of ffmpeg so many of you no longer have this option. +
  • +
  • ffmpeg-0.4.9pre1. Is supported starting from Motion version 3.1.18. With this release Motion supports mpeg4 and msmpeg4 but not mpeg1. The reason is that the ffmpeg team has decided no longer to support non-standard framerates in their mpeg1 encoder library. Also ffmpeg-0.4.9pre1 gives people problems on newer distributions. +
  • +
  • ffmpeg from CVS. This may work. We cannot continuously monitor and try every time a new source file is checked into ffmpeg. You will have to try. +
  • +
  • ffmpeg RPMs. Currently each Motion release is tested with the current Livna ffmpeg rpm package for Fedora. See the Download Files page for direct links to the version which has been certified with the latest Motion release. +
  • +
  • ffmpeg debian binaries. Latest versions from the debian repository for Debian Sarge works fine with Motion. +
  • +
  • Certified ffmpeg CVS snapshot for latest Motion release is available from the Motion Sourceforge Related Projects file area +
  • +
+

+The timelapse feature always runs mpeg1 with both ffmpeg 0.4.8 and 0.4.9 and newer. Motion simply creates the timelapse film with a standard mpeg1 framerate. Note : maximum size for timelapse files is 2GB. +

+In principle Motion can be made to support many other formats. It requires additional coding in Motion. You are welcome to submit patches. All ffmpeg related code is in the source file ffmpeg.c. It is not trivial to do because the ffmpeg libraries not documented at all. All you have is a couple of code examples. +

+To build ffpmeg from source follow these steps: +

+Download the ffmpeg and untar it to /usr/local/ffmpeg. Then it should be a simple matter of entering the ffmpeg directory and run the commands +

+

+cd /usr/local/ffmpeg
+./configure --enable-shared
+make
+make install
+
+

+This creates the libavcodec.so and libavformat.so libraries under /usr/local/lib and header files under /usr/local/include/ffmpeg. +

+You probably need to do one more step. +

+Make sure you have 'root' privileges for the next steps. +

+Open the file /etc/ld.so.conf in your favorite text editor. +

+Add this line of text if it is not already there - otherwise go to the next step (ldconfig). +

+/usr/local/lib +Run the command ldconfig. +

+Motion should now be able to find the shared libraries for ffmpeg (libavcodec.so and libavformat.so) in /usr/local/lib. +

+You can also find a pre-compiled binary package (e.g. rpm or deb) and install this. Normally an rpm will place the libavcodec.so under /usr/lib. +There are various RPMs available from different repositories. Some need additional RPMs that are actually not needed by Motion but need to be installed to satisfy dependencies. The editor has tried different RPMs of ffmpeg-0.4.8 and they all seem to work. +

+Motion then need to be built by running ./configure, make and make install. +(Note that with earlier versions of motion you had to specify the location of libavcodec. Now configure searches for the shared library in /usr/lib and /usr/local/lib by default.) +

+Note that if you install ffmpeg from source and already have ffmpeg installed from an RPM, the Motion configure may very well find the binary library from the rpm instead of the sources. Make sure to uninstall any old ffmpeg RPMs before you install ffmpeg from sources. +

+These are the config file options related to ffmpeg. +

+

+

+

ffmpeg_bps

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 9999999 +
  • +
  • Default: 400000 +
  • +
  • Option Topic +
  • +
+

+Bitrate of mpegs produced by ffmpeg. Bitrate is bits per second. Default: 400000 (400kbps). Higher value mans better quality and larger files. Option requires that ffmpeg libraries are installed. +

+To use this feature you need to install the FFmpeg Streaming Multimedia System. +

+Experiment to get the desired quality. The better quality the bigger files. This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled). +

+

+

+

+

ffmpeg_cap_motion

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Use ffmpeg libraries to encode motion type mpeg movies where you only see the pixels that changes. +

+Works like ffmpeg_cap_new but outputs motion pixel type pictures instead. +

+This feature generates the special motion type movie where you only see the pixels that changes as a graytone image. If labelling is enabled you see the largest area in blue. Smartmask is shown in red. The filename given is the same as the normal mpegs except they have an 'm' appended after the filename before the .mpg. E.g. 20040424181525m.mpg +

+To use this feature you need to install the FFmpeg Streaming Multimedia System +

+

+

+

+

ffmpeg_cap_new

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Use ffmpeg libraries to encode mpeg movies in realtime. +

+Generates a new film at the beginning of each new event and appends to the film for each motion detected within the same event. The current event ends when the time defined by the 'gap' option has passed with no motion detected. At the next detection of motion a new mpeg film is started. +

+To use this feature you need to install the FFmpeg Streaming Multimedia System +

+Must not be included in config file without having ffmpeg installed. +

+

+

+

+

ffmpeg_timelapse

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Create a timelapse movie saving a picture frame at the interval in seconds set by this parameter. Set it to 0 if not used. +

+This feature uses ffmpegs libavcodec to encode a timelaps movie saving a picture frame at the interval in seconds set by this parameter. Setting this option to 0 disables it. +

+The feature gives your viewer the chance to watch the day pass by. It makes a nice effect to film flowers etc closeup during the day. Options like frame_rate, snapshot, gap etc have no impact on the ffmpeg timelapse function. +

+Note that the timelapse format is always mpeg1 independent of ffmpeg_video_codec. This is because mpeg1 allows the timelapse to stop and the file to be reopened and more film appended. +

+To use this feature you need to install the FFmpeg Streaming Multimedia System. +

+(renamed from ffmpeg_timelaps to ffmpeg_timelapse in 3.1.14) +

+

+

+

+

ffmpeg_timelapse_mode

+

+

    +
  • Type: Discrete Strings +
  • +
  • Range / Valid values: hourly, daily, weekly-sunday, weekly-monday, monthly, manual +
  • +
  • Default: daily +
  • +
  • Option Topic +
  • +
+

+The file rollover mode of the timelapse video. +

+Note that it is important that you use the conversion specifiers in ffmpeg_filename that ensure that the new timelapse file indeed is a new file. If the filename does not change Motion will simply append the timelapse pictures to the existing file. +

+The value 'Manual' means that Motion does not automatically rollover to a new filename. You can do it manually using the http control interface by setting the option 'ffmpeg_timelapse' to 0 and then back to your chosen value. Value 'hourly' rolls over on the full hour. Value 'daily' which is the default rolls over at midnight. There are two weekly options because depending on where you come from a week may either start on Sunday or Monday. And 'monthly' naturally rolls over on the 1st of the month. +

+

+

+

+

ffmpeg_variable_bitrate

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0, 2 - 31 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Enables and defines variable bitrate for the ffmpeg encoder. ffmpeg_bps is ignored if variable bitrate is enabled. Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, or the range 2 - 31 where 2 means best quality and 31 is worst. +

+Experiment for the value that gives you the desired compromise between size and quality. +

+

+

+

+

ffmpeg_video_codec

+

+

    +
  • Type: Discrete Strings +
  • +
  • Range / Valid values: mpeg1 (ffmpeg-0.4.8 only), mpeg4, msmpeg4 +
  • +
  • Default: mpeg4 +
  • +
  • Option Topic +
  • +
+

+Codec to be used by ffmpeg for the video compression. Timelapse mpegs are always made in mpeg1 format independent from this option. +

+mpeg1 gives you files with extension .mpg. It is only supported by the old ffmpeg version 0.4.8. The ffmpeg team decided no longer to support non-standard framerates for mpeg1 from ffmpeg version 0.4.9pre1. +

+mpeg4 or msmpeg4 give you files with extension .avi +

+msmpeg4 is recommended for use with Windows Media Player because it requires with no installation of codec on the Windows client. +

+This option does not affect the timelapse feature. Timelapse is always recorded in mpeg1 format because we need to be able to append to an existing file. mpeg4 does not easily allow this. +

+

+

+


+

+See also the section Advanced Filenames where the two additional options ffmpeg_filename and timelapse_filename are defined. +

+If you want to use this feature you can read about the FFmpeg Streaming Multimedia System +

+

+

Snapshots - The Traditional Periodic Web Camera

+Motion can also act like a traditional web camera. +

+

+

+

snapshot_interval

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Make automated snapshots every 'snapshot_interval' seconds. +

+The snapshots are stored in the target directory + the directory/filename specified by the snapshot_filename option. +

+This is the traditional web camera feature where a picture is taken at a regular interval independently of motion in the picture. +

+

+

+See the also snapshot_filename option in the section Advanced Filenames. +

+

+

Text Features

+Text features are highly flexible. You can taylor the text displayed on the images and films to your taste and you can add your own user defined text. +

+This is how the overlayed text is located. +

+ + + + +
+ + + + + + + + + +
+

  





 

+
+

 CHANGES





 

+
+



TEXT_LEFT

+
+

TEXT_RIGHT
YYYY-MM-DD
HH:MM:SS 

+
+
+

+You are allowed to put the text in quotation marks. This allows you to use leading spaces. By combining spaces and new lines '\n' you can place your text anywhere on the picture. Experiment to find your preferred look. When setting the text using http remote control the text must be URL encoded. The browser does this for you. If you need to set it with a command line tool, use a browser first and let it make the encoded URL for you. Then you can copy paste it to your script file or cron line or whatever you want to use. +

+Below are the options that controls the display of text. The 'locate' option is not a text feature but described here because it is related to information overlayed on the output images. +

+The text_event feature is special in that it defines the conversion specifier %C which can be used both for text display and for filenames. +

+

+

+

locate

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off, preview +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Locate and draw a box around the moving object. Value 'preview' makes Motion only draw a box on a saved preview jpeg image and not on the saved mpeg movie. +

+The value 'preview' only works when 'output_normal' is set to either 'first' or 'best'. +

+

+

+

+

text_changes

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Turns the text showing changed pixels on/off. +

+By setting this option to 'on' the number of pixels that changed compared to the reference frame is displayed in the upper right corner of the pictures. This is good for calibration and test. Maybe not so interesting for a greater public. Set it to your personal taste. +

+

+

+

+

text_double

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
+

+Draw characters at twice normal size on images. +

+This option makes the text defined by text_left, text_right and text_changes twice the normal size. This may be useful when using large picture formats such as 640 x 480. +

+ +

+

+

+

+

text_event

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: %Y%m%d%H%M%S +
  • +
  • Option Topic +
  • +
+

+This option defines the value of the speciel event conversion specifier %C. You can use any conversion specifier in this option except %C. Date and time values are from the timestamp of the first image in the current event. +

+The idea is that %C can be used filenames and text_left/right for creating a unique identifier for each event. +

+Option text_event defines the value %C which then can be used in filenames and text_right/text_left. The text_event/%C uses the time stamp for the first image detected in a new event. %C is an empty string when no event is in progress (gap period expired). Pre_captured and minimum_motion_frames images are time stamped before the event happens so %C in text_left/right does not have any effect on those images. +

+

+

+

+

text_left

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+User defined text overlayed on each in the lower left corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > ¦ , . : - + _ \n and conversion specifiers (codes starting by a %). +

+text_left is displayed in the lower left corner of the pictures. If the option is not defined no text is displayed at this position. +

+You can place the text in quotation marks to allow leading spaces. With a combination is spaces and newlines you can position the text anywhere on the picture. +

+Detailed Description +

+A conversion specifier is a code that starts by % (except newline which is \n). The conversion specifiers used has the same function as for the C function strftime (3). The most commonly used are: +

    +
  • %Y = year +
  • +
  • %m = month as two digits +
  • +
  • %d = date +
  • +
  • %H = hour +
  • +
  • %M = minute +
  • +
  • %S = second +
  • +
  • %T = HH:MM:SS +
  • +
+

+These are unique to motion +

    +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i = width of motion area +
  • +
  • %J = height of motion area +
  • +
  • %K = X coordinate of motion center +
  • +
  • %L = Y coordinate of motion center +
  • +
  • %C = value defined by text_event +
  • +
+

+With a combination of text, spaces, new lines \n and conversion specifiers you have some very flexible text features. +

+For a full list of conversion specifiers see the section Conversion Specifiers for Advanced Filename and Text Feature. +

+

+

+

+

+

text_right

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: %Y-%m-%d\n%T +
  • +
  • Option Topic +
  • +
+

+User defined text overlayed on each in the lower right corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > ¦ , . : - + _ \n and conversion specifiers (codes starting by a %). Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +

+text_right is displayed in the lower right corner of the pictures. If the option is not defined no text is displayed at this position. +

+You can place the text in quotation marks to allow leading spaces. With a combination is spaces and newlines you can position the text anywhere on the picture. +

+A major difference from text_left is that if this option is undefined the default is %Y-%m-%d\n%T which displays the date in ISO format YYYY-MM-DD and below the time in 24 hour clock HH:MM:SS. +

+Detailed Description +

+A conversion specifier is a code that starts by % (except newline which is \n). The conversion specifiers used has the same function as for the C function strftime (3). The most commonly used are: +

    +
  • %Y = year +
  • +
  • %m = month as two digits +
  • +
  • %d = date +
  • +
  • %H = hour +
  • +
  • %M = minute +
  • +
  • %S = second +
  • +
  • %T = HH:MM:SS +
  • +
+

+These are unique to motion +

    +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i = width of motion area +
  • +
  • %J = height of motion area +
  • +
  • %K = X coordinate of motion center +
  • +
  • %L = Y coordinate of motion center +
  • +
  • %C = value defined by text_event +
  • +
+

+With a combination of text, spaces, new lines \n and conversion specifiers you have some very flexible text features. +

+For a full list of conversion specifiers see the section Conversion Specifiers for Advanced Filename and Text Feature. +

+

+

+

+

+

Advanced Filenames

+Motion has a very advanced and flexible automated filenaming feature. +

+By using conversion specifiers (codes that consist of a '%' followed by a letter) you can build up the filenames including sub directories for pictures and movies using any combination of letters, numbers and conversion specifiers which are codes that represents time, date, event number and frame numbers. +

+The option target_dir is the target directory for all snapshots, motion images and normal images. The default is the current working directory (current working directory of the terminal from which motion was started). You will normally always want to specify this parameter. +

+Note that the options snapshot_filename, jpeg_filename, ffmpeg_filename, and timelapse_filename all allow specifying directories by using '/' in the filename. These will all be relative to target_dir. This means in principle that you can specify target_dir as '/' and be 100% flexible. It also means that Motion can write files all over your harddisk if you make a mistake. It is recommended to specify the target_dir as deep or detailed as possible for this reason. And note that targer_dir does not allow conversion specifiers. +

+The conversion specifier %C which is defined by the option text_event is interesting in connection with filenames because it can be used to create files and directories for each event in a very flexible way. +

+The convertion specifier %t (thread/camera number) is also very useful. Here is an example of filename definitions in motion.conf: +

+

+target_dir /usr/local/webcam
+snapshot_filename cam%t/%v-%Y%m%d%H%M%S-snapshot
+jpeg_filename cam%t/%v-%Y%m%d%H%M%S-%q
+ffmpeg_filename cam%t/%v-%Y%m%d%H%M%S
+timelapse_filename cam%t/%Y%m%d%H-timelapse
+
+

+The smart thing is that this defines the filename of all your camera threads in motion.conf so you do not need to specify target dir and filenames in the thread config files. In the above example an mpegfile for camera thread 3 will be saved as a filename similar to /usr/local/webcam/cam3/28-20051128130840.avi +

+NOTE: Unless you use the minimum_gap option to limit the number of shots to less then one per second - you must use the frame modifier %q as part of the jpeg_filename. Otherwise the pictures saved within the same second will overwrite each other. The %q in jpeg_filename ensures that each jpeg (or ppm) picture saved gets a unique filename. +

+ALERT! Security Warning! Note that the flexibility of this feature also means you have to pay attention to the following. +

    +
  • Anyone with access to the remote control port (http) can alter the values of these options and save files anywhere on your server with the same privileges as the user running Motion. Anyone can access your control port if you have not either limited access to localhost or limited access using firewalls in the server. You should always have a router between a machine running Motion with remote control enabled and the Internet and make sure the Motion control port is not accessible from the outside. +
  • +
  • Anyone with local access to the computer and edit rights to the motion.conf file can alter the values of these options and save files anywhere on your server with the same privileges as the user running Motion. Make sure the motion.conf file is maximum readonly to anyone else but the user running Motion. +
  • +
  • It is a good idea to run Motion as a harmless user. Not as root. +
  • +
+

+These are the advanced filename options in motion.conf +

+

+

+

ffmpeg_filename

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: %v-%Y%m%d%H%M%S +
  • +
  • Option Topic +
  • +
+

+File path for motion triggered ffmpeg movies (mpeg) relative to target_dir. +

+Default value is equivalent to legacy 'oldlayout' option +For Motion 3.0 compatible mode (directories based on date and time) choose: %Y/%m/%d/%H%M%S +

+File extension .mpg or .avi is automatically added so do not include this. +

+This option uses conversion specifiers which are codes that start by % and then a letter. The conversion specifiers used has the same function as for the C function strftime (3). The most commonly used are: +

    +
  • %Y = year +
  • +
  • %m = month as two digits +
  • +
  • %d = date +
  • +
  • %H = hour +
  • +
  • %M = minute +
  • +
  • %S = second +
  • +
  • %T = HH:MM:SS +
  • +
+

+These are unique to motion +

    +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i = width of motion area +
  • +
  • %J = height of motion area +
  • +
  • %K = X coordinate of motion center +
  • +
  • %L = Y coordinate of motion center +
  • +
  • %C = value defined by text_event +
  • +
+

+If you are happy with the directory structures the way they were in earlier versions of motion use %v-%Y%m%d%H%M%S for 'oldlayout on' and %Y/%m/%d/%H%M%S for 'oldlayout off'. +

+

+

+

+

jpeg_filename

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: %v-%Y%m%d%H%M%S-%q +
  • +
  • Option Topic +
  • +
+

+File path for motion triggered images (jpeg or ppm) relative to target_dir. Value 'preview' makes a jpeg filename with the same name body as the associated saved mpeg movie file. +

+Default value is equivalent to legacy 'oldlayout' option. For Motion 3.0 compatible mode (directories based on date and time) choose: %Y/%m/%d/%H/%M/%S-%q +

+This option uses conversion specifiers which are codes that start by % and then a letter. The conversion specifiers used has the same function as for the C function strftime (3). The most commonly used are: +

    +
  • %Y = year +
  • +
  • %m = month as two digits +
  • +
  • %d = date +
  • +
  • %H = hour +
  • +
  • %M = minute +
  • +
  • %S = second +
  • +
  • %T = HH:MM:SS +
  • +
+

+These are unique to motion +

    +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i = width of motion area +
  • +
  • %J = height of motion area +
  • +
  • %K = X coordinate of motion center +
  • +
  • %L = Y coordinate of motion center +
  • +
  • %C = value defined by text_event +
  • +
+

+If you are happy with the directory structures the way they were in earlier versions of motion use %v-%Y%m%d%H%M%S-%q for 'oldlayout on' and %Y/%m/%d/%H/%M/%S-%q for 'oldlayout off'. +

+The value 'preview' only works when 'output_normal' is set to 'best'. It makes Motion name the best preview jpeg file (image with most changed pixels during the event) with the same body name as the mpeg movie created during the same event. The purpose is to create a good single image that represents the saved mpeg moview so you can decide if you want to see it and spend time downloading it from a web page. +

+

+

+

+

snapshot_filename

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: %v-%Y%m%d%H%M%S-snapshot +
  • +
  • Option Topic +
  • +
+

+File path for snapshots (jpeg or ppm) relative to target_dir. +

+Default value is equivalent to legacy 'oldlayout' option. For Motion 3.0 compatible mode (directories based on date and time) choose: %Y/%m/%d/%H/%M/%S-snapshot +

+File extension .jpg or .ppm is automatically added so do not include this +A symbolic link called lastsnap.jpg (or lastsnap.ppm) created in the target_dir will always point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' +

+This option uses conversion specifiers which are codes that start by % and then a letter. The conversion specifiers used has the same function as for the C function strftime (3). The most commonly used are: +

    +
  • %Y = year +
  • +
  • %m = month as two digits +
  • +
  • %d = date +
  • +
  • %H = hour +
  • +
  • %M = minute +
  • +
  • %S = second +
  • +
  • %T = HH:MM:SS +
  • +
+

+These are unique to motion +

    +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i = width of motion area +
  • +
  • %J = height of motion area +
  • +
  • %K = X coordinate of motion center +
  • +
  • %L = Y coordinate of motion center +
  • +
  • %C = value defined by text_event +
  • +
+

+If you are happy with the directory structures the way they were in earlier versions of motion use %v-%Y%m%d%H%M%S-snapshot for 'oldlayout on' and %Y/%m/%d/%H/%M/%S-snapshot for 'oldlayout off'. +

+For the equivalent of the now obsolete option 'snap_overwrite' use the value 'lastsnap'. +

+

+

+

+

target_dir

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined = current working directory +
  • +
  • Option Topic +
  • +
+

+Target directory for picture and movie files. +

+This is the target directory for all snapshots, images files and movie files. The default is the current working directory (current working directory of the terminal from which motion was started). You will normally always want to specify this parameter as an absolute path. +

+Note that the options snapshot_filename, jpeg_filename, ffmpeg_filename, and timelapse_filename all allows specifying directories. These will all be relative to 'target_dir'. This means in principle that you can specify target_dir as '/' and be 100% flexible. It also means that Motion can write files all over your harddisk if you make a mistake. It is recommended to specify the target_dir as deep or detailed as possible for this reason. +

+

+

+

+

timelapse_filename

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: %v-%Y%m%d-timelapse +
  • +
  • Option Topic +
  • +
+

+File path for timelapse mpegs relative to target_dir (ffmpeg only). +

+Default value is equivalent to legacy 'oldlayout' option. +

+For Motion 3.0 compatible mode (directories based on date and time) choose: %Y/%m/%d-timelapse +

+File extension .mpg is automatically added so do not include this. +

+This option uses conversion specifiers which are codes that start by % and then a letter. The conversion specifiers used has the same function as for the C function strftime (3). The most commonly used are: +

    +
  • %Y = year +
  • +
  • %m = month as two digits +
  • +
  • %d = date +
  • +
  • %H = hour +
  • +
  • %M = minute +
  • +
  • %S = second +
  • +
  • %T = HH:MM:SS +
  • +
+

+These are unique to motion +

    +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i = width of motion area +
  • +
  • %J = height of motion area +
  • +
  • %K = X coordinate of motion center +
  • +
  • %L = Y coordinate of motion center +
  • +
  • %C = value defined by text_event +
  • +
+

+If you are happy with the directory structures the way they were in earlier versions of motion use %v-%Y%m%d-timelapse for 'oldlayout on' and %Y/%m/%d-timelapse for 'oldlayout off'. +

+

+

+

+

Conversion Specifiers for Advanced Filename and Text Features

+The table below shows all the supported Conversion Specifiers you can use in the options text_event, text_left, text_right, sql_query, snapshot_filename, jpeg_filename, ffmpeg_filename, timelapse_filename, on_event_start, on_event_end, on_picture_save, on_movie_start, on_movie_end, and on_motion_detected. +

+In text_left and text_right you can additionally use '\n' for new line. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Conversion Specifier Description
%a The abbreviated weekday name according to the current locale.
%A The full weekday name according to the current locale.
%b The abbreviated month name according to the current locale.
%B The full month name according to the current locale.
%c The preferred date and time representation for the current locale.
%C Text defined by the text_event feature
%d The day of the month as a decimal number (range 01 to 31).
%D Number of pixels detected as Motion. If labelling is enabled the number is the number of pixels in the largest labelled motion area.
%E Modifier: use alternative format, see below.
%f File name - used in the on_picture_save, on_movie_start, on_movie_end, and sql_query features.
%F Equivalent to %Y-%m-%d (the ISO 8601 date format).
%H The hour as a decimal number using a 24-hour clock (range 00 to 23).
%i Width of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on).
%I The hour as a decimal number using a 12-hour clock (range 01 to 12).
%j The day of the year as a decimal number (range 001 to 366).
%J Height of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on).
%k The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.)
%K X coordinate in pixels of the center point of motion. Origin is upper left corner.
%l The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.)
%L Y coordinate in pixels of the center point of motion. Origin is upper left corner and number is positive moving downwards (I may change this soon).
%m The month as a decimal number (range 01 to 12).
%M The minute as a decimal number (range 00 to 59).
%n Filetype as used in the on_picture_save, on_movie_start, on_movie_end, and sql_query features.
%N Noise level.
%o Threshold. The number of detected pixels required to trigger motion. When threshold_tune is 'on' this can be used to show the current tuned value of threshold.
%p Either 'AM' or 'PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'.
%P Like %p but in lowercase: `am' or `pm' or a corresponding string for the current locale.
%q Picture frame number within current second. For jpeg filenames this should always be included in the filename if you save more then 1 picture per second to ensure unique filenames. It is not needed in filenames for mpegs.
%Q Number of detected labels found by the despeckle feature
%r The time in a.m. or p.m. notation.
%R The time in 24-hour notation (%H:%M).
%s The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC.
%S The second as a decimal number (range 00 to 61).
%t Thread number (camera number)
%T The time in 24-hour notation (%H:%M:%S).
%u The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w.
%U The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W.
%v Event number. An event is a series of motion detections happening with less than 'gap' seconds between them.
%V The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W.
%w The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.
%W The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.
%x The preferred date representation for the current locale without the time.
%X The preferred time representation for the current locale without the date.
%y The year as a decimal number without a century (range 00 to 99).
%Y The year as a decimal number including the century.
%z The time-zone as hour offset from GMT.
%Z The time zone or name or abbreviation.
+

+

+

Webcam Server

+Motion has simple webcam server built in. The video stream is in mjpeg format. +

+Each thread can have its own webcam server. If you enable the webcam server (option webcam_port to a number different from 0) and you have more than one camera, you must make sure to include webcam_port in each thread config file and set webcam_port to different and unique port numbers or zero (disable). Otherwise each webcam server will use the setting from the motion.conf file and try to bind to the same port. If the webcam_port numbers are not different from each other Motion will disable the webcam feature. +

+Note: The webcam server feature requires that the option ppm is set to off. (I.e. saved images are jpeg images). +

+The webcam_maxrate and webcam_quality options are important to limit the load on your server and link. Don't set them too high unless you only use it on the localhost or on an internal LAN. The option webcam_quality is equivalent to the quality level for jpeg pictures. +

+The webcam_limit option prevents people from loading your Network connection by streaming for hours and hours. The options defines the number of picture frames sent as mjpeg Motion will allow without re-connecting (e.g. clicking refresh in the browser). +

+The option webcam_localhost is a security feature. When enabled you can only access the webserver on the same machine as Motion is running on. If you want to present a live webcam on your web site this feature must be disabled. +

+The webserver generates a stream in "multipart jpeg" format (mjpeg). You cannot watch the stream with most browsers. Only certain versions of Netscape works. Mozilla and Firefox brosers can view the mjpeg stream but you often have to refresh the page once to get the streaming going. Internet Explorer cannot show the mjpeg stream. For public viewing this is not very useful. There exists a java applet called Cambozola which enabled any Java capable browser to show the stream. To enable the feature to a broad audience you should use this applet or similar. +

+To use the webcam feature with Cambozola is actually very simple. +

+1. Create a html page in which you will want the streamed picture. +

+2. In the html page include this code +

+

+<applet code=com.charliemouse.cambozola.Viewer
+    archive=cambozola.jar width="320" height="240" style="border-width:1; border-color:gray; border-style:solid;">
+    <param name=url value="http://www.myurl.com:8081">
+</applet>
+
+

+Where the width and height is the image size of the video stream. +

+Replace www.myurl.com:8081 by the real url and port number of your choice. +

+3. In the same directory you place the cambozola.jar file. No need to build the java applet from source. Simply use the applet in the package. +

+4. Enable the feature in motion.conf. +

+These are the special webcam parameters. +

+

+

+

webcam_limit

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 0 (unlimited) +
  • +
  • Option Topic +
  • +
+

+Limit the number of frames to number frames. After 'webcam_limit' number of frames the connection will be closed by motion. The value 0 means unlimited. +

+Number can be defined by multiplying actual webcam rate by desired number of seconds. Actual webcam rate is the smallest of the numbers framerate and webcam_maxrate. +

+

+

+

+

webcam_localhost

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Limits the access to the webcam to the localhost. +

+By setting this to on, the webcam can only be accessed on the same machine on which Motion is running. +

+

+

+

+

webcam_maxrate

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 100 +
  • +
  • Default: 1 +
  • +
  • Option Topic +
  • +
+

+Limit the framerate of the webcam. Default is 1. Set the value to 100 for practically unlimited. +

+Don't set 'webcam_maxrate' too high unless you only use it on the localhost or on an internal LAN. +

+

+

+

+

webcam_motion

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+If set to 'on' Motion sends slows down the webcam stream to 1 picture per second when no motion is detected. When motion is detected the stream runs as defined by webcam_maxrate. When 'off' the webcam stream always runs as defined by webcam_maxrate. +

+Use this option to save bandwidth when there is not anything important to see from the camera anyway. +

+Note that this feature was greatly improved from Motion version 3.2.2. Before 3.2.2 the option stopped the webcam stream except when Motion was detected. This made the feature not very useful because it made it difficult to connect to the webcam stream and most mjpeg viewers would timeout and give an error message. From 3.2.2 the feature has been greatly improved and actually quite recommendable. +

+

+

+

+

webcam_port

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 65535 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+TCP port on which motion will listen for incoming connects with its webcam server. +

+Note that each camera thread must have its own unique port number and it must also be different from the control_port number. +

+A good value to select is 8081 for camera 1, 8082 for camera 2, 8083 for camera 3 etc etc. +

+

+

+

+

webcam_quality

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 1 - 100 +
  • +
  • Default: 50 +
  • +
  • Option Topic +
  • +
+

+Quality setting in percent for the mjpeg picture frames transferred over the webcam connection. Keep it low to restrict needed bandwidth. +

+The mjpeg stream consists of a header followed by jpeg frames separated by content-length and boundary string. The quality level defines the size of the individual jpeg pictures in the mjpeg stream. If you set it too high you need quite a high bandwidth to view the stream. +

+

+

+

Remote Control with http

+

+Motion can be remote controlled via a simple http interface. http is the language a normal web browser talks when it requests a web page. The web server answers back with some simple http headers followed by a webpage coded in HTML. +

+Most Motion config options can be changed while Motion is running except options related to the size of the captured images and mask files which are loaded only when Motion starts. So only your fantasy sets the limit to what you can change combining cron and the remote control interface for Motion. +

+So the most obvious tool to use to remote control Motion is any web browser. All commands are sent using the http GET method which simply means that the information is sent via the URL and maybe a query string. You can use any browser (Firefox, Mozilla, Internet Explorer, Konquerer, Opera etc). You can also use the text based browser lynx to control Motion from a console. It navigates fine through the very simple and minimalistic http control interface of Motion. +

+The details about how to control Motion via the URL is described in detail in the Motion http API topic. +

+But it is probably simpler to connect to the control port with a browser, navigate to the function you want, and copy the URL from the browser URL entry line. If your control_port is 8080 and you browse from the same machine on which Motion runs simply look up http://localhost:8080/ and navigate around. Connecting from a remote machine is done by using a domain name (example http://mydomain.com:8080/) or the IP address of the machine (example http://192.168.1.4:8080/). The option control_localhost must be off to allow connection from a remote machine. +

+If you want to use a script or cron to automatically change Motion settings while Motion runs you use a program that can fetch a webpage. We simply just throw away the html page that Motion returns. Programs commonly available on Linux machines are wget and lwp-request. Here is an example of how to start and stop motion detection via cron. These two lines are added to /etc/crontab. +

+

+0 9 * * * root /usr/bin/lwp-request http://localhost:8080/0/detection/start > /dev/null
+0 18 * * * root /usr/bin/lwp-request http://localhost:8080/0/detection/pause > /dev/null
+
+

+If you want to use the http remote control from your own software (for example your own PHP front end) you can set the new motion.conf option html_output off. Then Motion answers back with very basic text only and no html around it. A bit like the xmlrpc interface did. +

+To remote control Motion from a web pages you can for example use PHP. In PHP it takes this simple code line to send a remote commend to Motion. Here we pause motion detection for camera 2 +

+readfile('http://localhost:8080/2/detection/pause'); +

+What happened to XMLRPC? +

+XMLRPC is replaced by a simpler http remote control interface. It is still being worked on but it is absolutely useable now and much nicer to work with than xmlrpc. Another advantage is that you do not need to install xmlrpc libraries. It is all written in standard C. +

+ALERT! Security Warning! Note that this feature also means you have to pay attention to the following. +

    +
  • Anyone with access to the remote control port (http) can alter the values of any options and save files anywhere on your server with the same privileges as the user running Motion. They can execute any command on your computer with the same privileges as the user running Motion. Anyone can access your control port if you have not either limited access to localhost or limited access using firewalls in the server. You should always have a router between a machine running Motion with remote control enabled and the Internet and make sure the Motion control port is not accessible from the outside. +
  • +
  • If you limit control port to localhost you still need to take care of any user logging into the server with any kind of terminal session. +
  • +
  • It is a good idea to run Motion as a harmless user. Not as root!! +
  • +
+

+These are the config file options that control Motion. +

+These must be placed in motion.conf and not in a thread config file. +

+

+

+

control_authentication

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4096 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+To protect HTTP Control by username and password, use this option for HTTP 1.1 Basic authentication. The string is specified as username:password. Do not specify this option for no authentication. This option must be placed in motion.conf and not in a thread config file. +

+By setting this to on, the control using http (browser) can only be accessed using login and password ( following the Basic Authentication defined in HTTP RFC). +

+

+

+ +

+

+

+

+

control_html_output

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Enable HTML in the answer sent back to a browser connecting to the control_port. This option must be placed in motion.conf and not in a thread config file. +

+The recommended value for most is "on" which means that you can navigate and control Motion with a normal browser. By setting this option to "off" the replies are in plain text which may be easier to parse for 3rd party programs that control Motion. +

+

+

+

+

control_localhost

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Limits the http (html) control to the localhost. This option must be placed in motion.conf and not in a thread config file. +

+By setting this to on, the control using http (browser) can only be accessed on the same machine on which Motion is running. +

+

+

+

+

control_port

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 65535 +
  • +
  • Default: 0 (disabled) +
  • +
  • Option Topic +
  • +
+

+Sets the port number for the http (html using browser) based remote control. This option must be placed in motion.conf and not in a thread config file. +

+This sets the TCP/IP port number to be used for control of motion using http (browser). Port numbers below 1024 normally requires that you have root privileges. Port 8080 is a fine choice of port to use for the purpose. +

+

+

+

+-- KennethLavrsen - 12 Apr 2005 +

+

+

External Commands

+Motion can execute external commands based on the motion detection and related events. They are all described in this section. The option quiet is also included in this section. +

+A redesign of the external commands was due. They were not very easy to understand, not all were flexible enough and some were missing. So a new external command feature set was made for 3.2.1 and on. +

+This is how the new script commands look like: +

+ + + + + + + + +
Function Old Option New Option Argument Appended
Start of event (first motion) execute on_event_start None
End of event (no motion for gap seconds) New! on_event_end None
Picture saved (jpg or ppm) onsave on_picture_save Filename of picture
Movie starts (mpeg file opened) onmpeg on_movie_start Filename of movie
Movie ends (mpeg file closed) onffmpegclose on_movie_end Filename of movie
Motion detected (each single frame with Motion detected) New! on_motion_detected None
+

+Mail and sms has been removed because they were not configurable. If you want to send event-based mails or sms, just use one of those commands above and send the mail from that script. See What happened to mail and sms? +

+

+ALERT! Security Warning! Note that this feature also means you have to pay attention to the following. +

    +
  • Anyone with access to the remote control port (http) can execute any command on your computer with the same privileges as the user running Motion. Anyone can access your control port if you have not either limited access to localhost or limited access using firewalls in the server. You should always have a router between a machine running Motion with remote control enabled and the Internet and make sure the Motion control port is not accessible from the outside. +
  • +
  • If you limit control port to localhost you still need to take care of any user logging into the server with any kind of GUI or terminal session. All it takes is a browser or single command line execution to change settings in Motion. +
  • +
  • It is a good idea to run Motion as a harmless user. Not as root!! +
  • +
+

+These are the options +

+

+

+

on_event_end

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Command to be executed when an event ends after a period of no motion. The period of no motion is defined by option gap. You can use Conversion Specifiers and spaces as part of the command. +

+Full path name of the program/script. +

+This can be any type of program or script. Remember to set the execution bit in the ACL and if it is a script type program such as perl or bash also remember the shebang line (e.g. #!/user/bin/perl) as the first line of the script. +

+The command is run when an event is over. I.e. the number of seconds defined by the time 'gap' has passed since the last detection of motion and motion closes the mpeg file. +

+

+

+

+

on_event_start

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Command to be executed when an event starts. An event starts at first motion detected after a period of no motion defined by gap. You can use ConversionSpecifiers and spaces as part of the command. +

+Full path name of the program/script. +

+This can be any type of program or script. Remember to set the execution bit in the ACL and if it is a script type program such as perl or bash also remember the shebang line (e.g. #!/user/bin/perl) as the first line of the script. +

+The command is run when an event starts. I.e. the first motion is detected since the last event. +

+This option replaces the former options 'mail', 'sms' and 'execute'. +

+

+

+

+

on_motion_detected

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Command to be executed when a motion frame is detected. You can use Conversion Specifiers and spaces as part of the command. +

+Do not write "none" if you do not want to execute commands. Simply do not include the option in the file or comment it out by placing a "#" or ";" as the first character on the line before the execute command. +

+

+

+

on_movie_end

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Command to be executed when an ffmpeg movie is closed at the end of an event. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +

+Full path name of the program/script. +

+This can be any type of program or script. Remember to set the execution bit in the ACL and if it is a script type program such as perl or bash also remember the shebang line (e.g. #!/user/bin/perl) as the first line of the script. +

+The command is run when an event is over. I.e. the number of seconds defined by the time 'gap' has passed since the last detection of motion and motion closes the mpeg file. +

+This option was previously called onffmpegclose. +

+Note that from Motion 3.2.4 the path name of the picture file is no longer appended to the command. Instead you can use the conversion specifier %f to insert the picture filename (full path) anywhere in the command. +

+Most common conversion specifiers +

+

    +
  • %Y = year, %m = month, %d = date +
  • +
  • %H = hour, %M = minute, %S = second +
  • +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i and %J = width and height of motion area +
  • +
  • %K and %L = X and Y coordinates of motion center +
  • +
  • %C = value defined by text_event +
  • +
  • %f = filename with full path +
  • +
  • %n = number indicating filetype +
  • +
+

+

+

+

+

+

on_movie_start

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Command to be executed when an mpeg movie is created. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +

+Full path name of the program/script. +

+This can be any type of program or script. Remember to set the execution bit in the ACL and if it is a script type program such as perl or bash also remember the shebang line (e.g. #!/user/bin/perl) as the first line of the script. When you use ffmpeg the film is generated on the fly and on_movie_start then runs when the new mpeg file is created. Often you will want to use the on_movie_end option which runs when the mpeg file is closed and the event is over. +

+This option was previously called onmpeg. +

+Note that from Motion 3.2.4 the path name of the picture file is no longer appended to the command. Instead you can use the conversion specifier %f to insert the picture filename (full path) anywhere in the command. +

+Most common conversion specifiers +

+

    +
  • %Y = year, %m = month, %d = date +
  • +
  • %H = hour, %M = minute, %S = second +
  • +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i and %J = width and height of motion area +
  • +
  • %K and %L = X and Y coordinates of motion center +
  • +
  • %C = value defined by text_event +
  • +
  • %f = filename with full path +
  • +
  • %n = number indicating filetype +
  • +
+

+

+

+

+

+

on_picture_save

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Command to be executed when an image is saved. You can use Conversion Specifiers and spaces as part of the command. Use %f for passing filename (with full path) to the command. +

+Full path name of the program/script. +

+This can be any type of program or script. Remember to set the execution bit in the file access control list (chmod) and if it is a script type program such as perl or bash also remember the shebang line (e.g. #!/usr/bin/perl) as the first line of the script. +

+Note that from Motion 3.2.4 the path name of the picture file is no longer appended to the command. Instead you can use the conversion specifier %f to insert the picture filename (full path) anywhere in the command. +

+Most common conversion specifiers +

+

    +
  • %Y = year, %m = month, %d = date +
  • +
  • %H = hour, %M = minute, %S = second +
  • +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i and %J = width and height of motion area +
  • +
  • %K and %L = X and Y coordinates of motion center +
  • +
  • %C = value defined by text_event +
  • +
  • %f = filename with full path +
  • +
  • %n = number indicating filetype +
  • +
+

+

+

+

+

+

quiet

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Be quiet, don't output beeps when detecting motion. +

+Only works in non-daemon mode. +

+

+

+

+ +

What happened to mail and sms?

+The 6 new on_xxxxx options replace the former execute, mail and sms options. +

+They are quite generic and flexible. These small bash scripts gives to the same functionality as mail and sms BUT you have all the flexibility you want to extend the messages, change the 'from' email address etc. +

+

Sending email at start of event

+Script written by JoergWeber +
+#!/bin/sh
+
+# Motion sample script to send an e-mail at start of an event.
+# Replaces the former 'mail' option.
+# Just define this script as 'on_event_start'-script in motion.conf like that:
+# on_event_start send_mail "%Y-%m-%d %T"
+
+#change to suit your needs:
+#location of 'mail' binary
+MAIL="/usr/bin/mail"
+#Destination e-mail address
+TO="root@localhost"
+#Subject of the e-mail
+SUBJECT="Motion detected"
+
+#Don't change anything below this line
+echo -e "This is an automated message generated by motion.\n\nMotion detected: $1\n\n" | $MAIL -s "$SUBJECT" $TO
+
+

+

Sending SMS at start of event

+Script written by JoergWeber +

+If you uncomment the line #/usr/local/bin/send_mail $1 you can combine both sending email and sms. +

+#!/bin/sh
+
+# Motion sample script to send an sms at start of an event.
+# Replaces the former 'sms' option.
+# Just define this script as 'on_event_start'-script in motion.conf like that:
+# on_event_start send_sms "%Y-%m-%d %T"
+#
+# If you want to send an e-mail message here as well, just uncomment the last
+# line of this script.
+
+#change to suit your needs:
+#location of 'sms-client' binary
+SMS_CLIENT="/usr/bin/sms_client"
+#Destination sms number
+TO="12345"
+
+#Don't change anything below this line
+$SMS_CLIENT $TO "Motion detected $1"
+
+#/usr/local/bin/send_mail $1
+
+
+

+

+

Motion Guide - Special Features

+

+

Tracking Control

+This is still at the experimental stage. Read more about it motion tracking page. +

+

Tracking Feature with Logitech Quickcam Sphere/Orbit

+Motion supports controlling the pan and tilt feature of a Logitech Quickcam Sphere/Orbit. +

+Motion can move the camera to a fixed position given in degrees pan (left-right) and tilt (down-up). Movement can be set with absolute coordinates or relative to current position. There is also an auto tracking feature for the Logitech Quickcam Sphere/Orbit but it is not very mature. It is fun to play with but not very useful yet. See this topic of how KennethLavrsen controls his Sphere: LogitechSphereControl. +

+For a detailed description of http remote control see the section Remote Control with http. +

+List of tracking options +

+

+

track_auto

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Enable auto tracking +

+Requires a tracking camera type supported by Motion. +

+

+

+

+

track_iomojo_id

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: +
  • +
  • Option Topic +
  • +
+

+Use this option if you have an iomojo smilecam connected to the serial port instead of a general stepper motor controller. +

+Only used for iomojo camera. +

+

+

+

+

track_maxx

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: +
  • +
  • Option Topic +
  • +
+

+The maximum position for servo x. +

+Only used for stepper motor tracking. +

+

+

+

+

track_motorx

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: -1 - 2147483647 +
  • +
  • Default: -1 +
  • +
  • Option Topic +
  • +
+

+The motor number that is used for controlling the x-axis. +

+Only used for stepper motor tracking. +

+

+

+

+

track_move_wait

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 2147483647 +
  • +
  • Default: 10 +
  • +
  • Option Topic +
  • +
+

+Delay during which tracking is disabled after auto tracking has moved the camera. Delay is defined as number of picture frames. +

+The actual delay is depending on the chosen framerate. If you want the camera to move maximum once every 2 seconds and the framerate is 10 then you need to set the track_move_wait value to 2 * 10 = 20. +

+

+

+

+

track_port

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+This is the device name of the serial port to which the stepper motor interface is connected. +

+Only used for stepper motor tracking. +

+

+

+

+

track_speed

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 255 +
  • +
  • Default: 255 +
  • +
  • Option Topic +
  • +
+

+Speed to set the motor to. +

+Only used for stepper motor tracking. +

+

+

+

+

track_step_angle_x

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0-90 +
  • +
  • Default: 10 +
  • +
  • Option Topic +
  • +
+

+Angle in degrees the camera moves per step on the X-axis with auto tracking. Currently only used with pwc type cameras. +

+Requires a tracking camera type pwc. +

+

+

+

+

track_step_angle_y

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0-40 +
  • +
  • Default: 10 +
  • +
  • Option Topic +
  • +
+

+Angle in degrees the camera moves per step on the Y-axis with auto tracking. Currently only used with pwc type cameras. +

+Requires a tracking camera type pwc. +

+ +

+

+

+

+

track_stepsize

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 255 +
  • +
  • Default: 40 +
  • +
  • Option Topic +
  • +
+

+Number of steps to make. +

+Only used for stepper motor tracking. +

+

+

+

+

track_type

+

+

    +
  • Type: Discrete Strings +
  • +
  • Range / Valid values: 0 (none), 1 (stepper), 2 (iomojo), 3 (pwc), 4 (generic) +
  • +
  • Default: 0 (None) +
  • +
  • Option Topic +
  • +
+

+Type of tracker. +

+Motion has special tracking options which use either a serial stepper motor controller, an iomojo smile cam or a Philips WebCam driver compatible pan/tilt camera such as the Logitech Quickcam Sphere or Orbit. +

+To disable tracking, set this to 0 and the other track options are ignored. +

+Value 1 is for the special Motion Tracking project using a stepper motor and a home made controller. +

+Value 2 is for the iomojo smilecam +

+Value 3 is for the a camera such a the Logitech Quickcam Sphere/Orbit which is driven by the pwc (Philips WebCam) driver. To use this camera your version of pwc must be at least 8.12. +

+Value 4 is the generic track type. Currently it has no other function than enabling some of the internal Motion features related to tracking. Eventually more functionality will be implemented for this type. +

+

+

+

+

+

+

Using Databases

+Motion can be compiled with both MySQL and PostgreSQL database support. When enabled Motion adds a record to a table in the database as specified by the sql_query. The query contains the fields that are used and the value are given by using conversion specifiers for dynamic data like filename, time, number of detected pixels etc. Motion does not place any binary images in the database and it cannot remove old records. +

+Motion only adds records to the database when files are created. The database contains records of saved files which means to get a record in the database the feature that enables for example motion detection, timelapse, snapshots etc must be enabled. The sql_log options defines which types of files are logged in the database. +

+The following sql_log options are common to both MySQL and PostgreSQL. +

+

+

+

sql_log_image

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Log to the database when creating motion triggered image file. +

+Configuration option common to MySQL and PostgreSQL. Motion must be built with MySQL or PostgreSQL support to use this feature. +

+

+

+

+

sql_log_mpeg

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Log to the database when creating motion triggered mpeg file. +

+Configuration option common to MySQL and PostgreSQL. Motion must be built with MySQL or PostgreSQL support to use this feature. +

+

+

+

+

sql_log_snapshot

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: on +
  • +
  • Option Topic +
  • +
+

+Log to the database when creating a snapshot image file. +

+Configuration option common to MySQL and PostgreSQL. Motion must be built with MySQL or PostgreSQL support to use this feature. +

+

+

+

+

sql_log_timelapse

+

+

    +
  • Type: Boolean +
  • +
  • Range / Valid values: on, off +
  • +
  • Default: off +
  • +
  • Option Topic +
  • +
+

+Log to the database when creating timelapse mpeg file +

+Configuration option common to MySQL and PostgreSQL. Motion must be built with MySQL or PostgreSQL support to use this feature. +

+

+

+

+

sql_query

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +
  • +
  • Option Topic +
  • +
+

+SQL query string that is sent to the database. The values for each field are given by using convertion specifiers +

+Most common conversion specifiers +

+

    +
  • %Y = year, %m = month, %d = date +
  • +
  • %H = hour, %M = minute, %S = second +
  • +
  • %v = event +
  • +
  • %q = frame number +
  • +
  • %t = thread (camera) number +
  • +
  • %D = changed pixels +
  • +
  • %N = noise level +
  • +
  • %i and %J = width and height of motion area +
  • +
  • %K and %L = X and Y coordinates of motion center +
  • +
  • %C = value defined by text_event +
  • +
  • %f = filename with full path +
  • +
  • %n = number indicating filetype +
  • +
+

+

+

+See the "MySQL" section for detailed information about the database itself. +

+

MySQL

+You can use the MySQL database to register each file that is stored by motion. +

+You need to generate a new database with a name of your own choice. You must enter this name in the config file (mysql_db option). The default value for the option sql_query requires that you create a new database in MySQL with a new table called "security" with the following fields: +

+insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +

+

    +
  • camera (int) - camera (thread) number +
  • +
  • filename (char60) - filename (full path) +
  • +
  • frame (int) - the number of the picture frame +
  • +
  • file_type (int) - file type as a number - see table below. +
  • +
  • time_stamp (timestamp) - timestamp for the picture in native database format +
  • +
  • text_event (timestamp) - The text from the text_event option which by default is compatible with timestamps in SQL. +
  • +
+

+Note from version 3.2.4 the introduction of sql_query completely redefines the way you setup the SQL feature. It is not 100% flexible and can easily be made compatible with your existing Motion database from earlier versions of Motion. +

+These are the file type descriptions and the file type numbers stored in the database. +

+ + + + + + + +
Normal image 1
Snapshot image 2
Motion image (showing only pixels defined as motion) 4
Normal mpeg image 8
Motion mpeg (showing only pixels defined as motion) 16
Timelapse mpeg 32
+

+You can create the table using the following SQL statement. +

+CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), text_event timestamp(14)); +

+If you choose to use text_event for a non-timestamp value you can instead define something like. +

+CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), text_event char(40)); +

+

+Remember to update grant table to give access to the mysql username you choose for motion. +

+It would be too much to go into detail about how to setup and use MySQL. After all this is a guide about Motion. However here are some hints and links. +

+Setting Up a MySQL Based Website - A beginners guide from Linux Planet. +

+Webmonkey PHP/!MySQL tutorial - Entertaining and easy to read. +

+The phpMyAdmin homepage. The best and simplest tool to use MySQL (editors opinion). Requires Apache/PHP. +

+The options for MySQL +

+

+

+

mysql_db

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Name of the MySQL database. +

+MySQL CONFIG FILE OPTION. Motion must be built with MySQL libraries to use this feature. +

+If you compiled motion with MySQL support you will need to set the mysql options if you want motion to log events to the database. +

+

+

+

+

mysql_host

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+IP address or domain name for the MySQL server. Use "localhost" if motion and MySQL runs on the same server. +

+MySQL CONFIG FILE OPTION. Motion must be built with MySQL libraries to use this feature. +

+

+

+

+

mysql_password

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+The MySQL password. +

+MySQL CONFIG FILE OPTION. Motion must be built with MySQL libraries to use this feature. +

+

+

+

+

mysql_user

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+The MySQL user name. +

+MySQL CONFIG FILE OPTION. Motion must be built with MySQL libraries to use this feature. +

+

+

+

+

PostgreSQL

+Same/similar as for MySQL above. +

+The options for PostgreSQL +

+

+

+

pgsql_db

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+Name of the PostgreSQL database. +

+PostgreSQL CONFIG FILE OPTION. Motion must be built with PostgreSQL libraries to use this feature. +

+If you compiled motion with PostgreSQL support you will need to set all the pgsql_ options if you want motion to log events to the database. +

+

+

+

+

pgsql_host

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+IP address or domain name for the PostgreSQL server. Use "localhost" if motion and PostgreSQL runs on the same server. +

+PostgreSQL CONFIG FILE OPTION. Motion must be built with pgsql_db libraries to use this feature. +

+

+

+

+

pgsql_password

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+The PostgreSQL password. +

+PostgreSQL CONFIG FILE OPTION. Motion must be built with PostgreSQL libraries to use this feature. +

+

+

+

+

pgsql_port

+

+

    +
  • Type: Integer +
  • +
  • Range / Valid values: 0 - 65535 +
  • +
  • Default: 5432 +
  • +
  • Option Topic +
  • +
+

+The PostgreSQL server port number. +

+PostgreSQL CONFIG FILE OPTION. Motion must be built with PostgreSQL libraries to use this feature. +

+

+

+

+

pgsql_user

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+The PostgreSQL user name. +

+PostgreSQL CONFIG FILE OPTION. Motion must be built with PostgreSQL libraries to use this feature. +

+

+

+

+

+

+

Video4Linux Loopback Device

+You can use this driver for looking at motion in realtime. The video4linux driver is written by the same author that first created Motion. You can find the source and a brief description at the video4linux loopback device web page. +

+The video4linux device is a Kernel module which installs itself as a video pipe. It has an input and an output. The module simply takes anything that comes on its input and send it out at the output. The purpose of this is to create a standard video4linux type video device that other programs can then use. You may now ask: "What do I need that for?". +

+Only one program can access a video device at a time. When motion is using a camera - no other program can access the same camera. But motion is made to be able to feed a video signal to the video loopback device. This way an additional program such as Camstream, Xawtv, a video stream server etc can watch the signal from a camera that motion uses already. What you see is not the live camera stream but the exact same picture that motion uses for detecting motion and the same pictures that are saved/streamed. You can also choose to see the "motion" type images where you see the pixels that are changing - live. Originally the video4linux pipe was used as an interface between Motion and a Webcam server. Since version 2.9 Motion has had its own webserver so this usage is no longer very relevant. +

+When you install the video loopback device it will create an input - for example /dev/video5 and an output - for example /dev/video6. You can then tell motion to "pipe" the video signal to the /dev/video5 and look at the pictures live using e.g. Camstream on /dev/video6. Camstream is "fooled" to think it is looking at a real camera. +

+Installing +

+Installing the video loopback device is not difficult. At least not when you have this document available. +

+First you must prepare your system for more video devices. You will need two extra devices for each video pipe that you want. +

+For example if you have 4 cameras they will probably run at /dev/video0, /dev/video1, /dev/video2, and /dev/video3. So you will need additional 8 video devices. This is easy to do. +

+

+mknod /dev/video4 c 81 4
+mknod /dev/video5 c 81 5
+mknod /dev/video6 c 81 6
+mknod /dev/video7 c 81 7
+mknod /dev/video8 c 81 8
+mknod /dev/video9 c 81 9
+mknod /dev/video10 c 81 10
+mknod /dev/video11 c 81 11
+
+

+Note that the video device number is the same as the last parameter given on each line. +

+You may need to set the ownership and permissions (chown and chmod) to be the same as the video devices that were already there. +

+Now you need to install the video loopback device. +

+Download the latest video4linux loopback device (version 0.91 at the time of this document - distributed as tar'ed and compressed files). Place the file in a place of your own choice. +

+Untar and uncompress the file to the place you want the program installed. Editor recommends /usr/local/vloopback. +

+cd /usr/local +

+tar -xvzf /path/to/vloopback-0.91.tar.gz +

+You now have a directory called vloopback-0.91. You can rename it to vloopback (mv vloopback-0.91 vloopback). I recommend creating a symbolic link to the current version. This way you can more easily experiment with different versions simply by changing the link. +

+ln -s vloopback-0.91 vloopback +

+Now change to the new directory +

+cd vloopback +

+Build the code +

+make +

+There is a good chance that the make will not work and give you a long list of errors. To run make the following must be available on you machine. +

    +
  • The kernel source files must be installed. +
  • +
  • The source files must be available at /usr/src/linux.
    E.g. the new Red Hat 7.3 does not have a link to the sources called linux. Instead there is a link called linux-2.4. This is easy to fix. Just create a link to the real source tree. Do not rename! Add a link using this command (replacing the kernel version number with the one you have on your machine)
    ln -s /usr/src/linux-2.4.18-4 /usr/src/linux +
  • +
  • Alternatively you can change the vloopback makefile so that the "LINUXSRC=/usr/src/linux" line is changed to the actual path. I recommend the link solution since this may solve other similar problems that you can get when installing other software. +
  • +
+

+When compiling on a newer Linux distribution you may get a warning about a header file malloc.h. To remove this warning simply change the header reference as suggested by the warning. +

+In vloopback.c you replace the line +

+#include <linux/malloc.h> +

+with the line +

+#include <linux/slab.h> +

+Install the code you built as a Kernel module. There are two options: pipes should be set to the number of video loopbacks that you want. Probably one for each camera. The dev_offset defines which video device number will be the first. If dev_offset is not defined the vloopback module will install itself from the first available video device. If you want the cameras to be assigned to the lower video device numbers you must either load vloopback after loading the video device modules OR use the dev_offset option when loading vloopback. Vloopback then installs itself in the sequence input 0, output 0, input 1, output 1, input 2, output 2 etc. Here is shown the command for our example of 4 cameras and 4 loopback devices and the first loopback device offset to /dev/video4. +

+/sbin/insmod /usr/local/vloopback/vloopback.o pipes=4 dev_offset=4 +

+When you run the command you may get a warning about tainting the Kernel. Just ignore this. +You can choose to copy the vloopback.o file into a directory in the /lib/modules tree where the insmod/modprobe programs are already looking for modules. Then the command gets simpler (/sbin/insmod vloopback pipes=.....). +

+If you want the loopback device to load during boot, you can place the call in one of the bootup scripts such as /etc/rc.d/rc.local. Vloopback should be loaded before you start motion. +

+To activate the vloopback device in motion set the 'video_pipe' option in the motion.conf file. You can also view the special motion pictures where you see the changed pixels by setting the option 'motion_video_pipe' in motion.conf. When setting the video_pipe and/or motion_video_pipe options either specify the input device as e.g. /dev/video4. You can also set the parameter to '-' which means that motion will find the first vacant video loopback device input. If you have more than one camera you may want to control which loopback device each thread uses. Then you need to define the specific device name in motion.conf for the first camera and in each thread config file for the other cameras. If you set the video_pipe parameter to '-' in the motion.conf file and not setting it in the thread config files, motion automatically assign video devices in the same sequence as the threads are loaded. You can combine both video_pipe and motion_video_pipe but then naturally you will need twice as many pipes. +

+De-activating should be done with this command +

+/sbin/modprobe -r vloopback +

+Descriotion of the motion.conf options related to video loopback device. +

+

+

+

motion_video_pipe

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+The video4linux video loopback input device for motion images. If a particular pipe is to be used then use the device filename of this pipe, if a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. Default: not set +

+Using this you can view the results in real time. E.g. by using the program camstream. The difference between this option and the video-pipe option is that this option shows the motion version of the images instead of the normal images. +

+Disable this option by not having it in the config file (or comment it out with "#" or ";"). +

+

+

+

+

+

video_pipe

+

+

    +
  • Type: String +
  • +
  • Range / Valid values: Max 4095 characters +
  • +
  • Default: Not defined +
  • +
  • Option Topic +
  • +
+

+The video4linux video loopback input device for normal images. If a particular pipe is to be used then use the device filename of this pipe. If a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. +

+Using this you can view the results in real time. E.g. by using the program camstream. +

+Disable this option by not having it in the config file (or comment it out with "#" or ";"). +

+

+

+

+

+

+-- KennethLavrsen - 13 Apr 2005 \ No newline at end of file diff --git a/netcam.c b/netcam.c new file mode 100644 index 0000000..eda383c --- /dev/null +++ b/netcam.c @@ -0,0 +1,1954 @@ +/* + * netcam.c + * + * Module for handling network cameras. + * + * This code was inspired by the original netcam.c module + * written by Jeroen Vreeken and enhanced by several Motion + * project contributors, particularly Angel Carpintero and + * Christopher Price. + * + * Copyright 2005, William M. Brack + * This software is distributed under the GNU Public license + * Version 2. See also the file 'COPYING'. + * + * + * When a netcam has been configured, instead of using the routines + * within video.c (which handle a CCTV-type camera) the routines + * within this module are used. There are only four entry points - + * one for "starting up" the camera (netcam_start), for "fetching a + * picture" from it (netcam_next), one for cleanup at the end of a + * run (netcam_cleanup), and a utility routine for receiving data + * from the camera (netcam_recv). + * + * Two quite different types of netcams are handled. The simplest + * one is the type which supplies a single JPEG frame each time it + * is accessed. The other type is one which supplies an mjpeg + * stream of data. + * + * For each of these cameras, the routinei taking care of the netcam + * will start up a completely separate thread (which I call the "camera + * handler thread" within subsequent comments). For a streaming camera, + * this handler will receive the mjpeg stream of data from the camera, + * and save the latest complete image when it begins to work on the next + * one. For the non-streaming version, this handler will be "triggered" + * (signalled) whenever the main motion-loop asks for a new image, and + * will start to fetch the next image at that time. For either type, + * the most recent image received from the camera will be returned to + * motion. + */ +#include "motion.h" + +#include +#include +#include /* For parsing of the URL */ +#include +#include +#include +#include +#include + +#include "netcam_wget.h" +#include "netcam_ftp.h" + +#define CONNECT_TIMEOUT 10 /* timeout on remote connection attempt */ +#define READ_TIMEOUT 5 /* default timeout on recv requests */ +#define MAX_HEADER_RETRIES 5 /* Max tries to find a header record */ +#define CAMERA_WARNINGS 3 /* if debug_level >= this number print */ +#define CAMERA_INFO 5 /* debug level to activate everything */ +#define MINVAL(x, y) ((x) < (y) ? (x) : (y)) + +/* + * The macro NETCAM_DEBUG is for development testing of this module. + * The macro SETUP is to assure that "configuration-setup" type messages + * are also printed when NETCAM_DEBUG is set. Set the following #if to + * 1 to enable it, or 0 (normal setting) to disable it. + */ +#define SETUP ((cnt->conf.setup_mode) || (debug_level >= CAMERA_INFO)) + +/* These strings are used for the HTTP connection */ +static const char *connect_req = "GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: Motion-netcam/" VERSION "\r\n" + "Connection: close\r\n"; + +static const char *connect_auth_req = "Authorization: Basic %s\r\n"; + +/* + * The following three routines (netcam_url_match, netcam_url_parse and + * netcam_url_free are for 'parsing' (i.e. separating into the relevant + * components) the URL provided by the user. They make use of regular + * expressions (which is outside the scope of this module, so detailed + * comments are not provided). netcam_url_parse is called from netcam_start, + * and puts the "broken-up" components of the URL into the "url" element of + * the netcam_context structure. + * + * Note that the routines are not "very clever", but they work sufficiently + * well for the limited requirements of this module. The expression: + * (http)://(((.*):(.*))@)?([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^:]*)) + * requires + * 1) a string which begins with 'http', followed by '://' + * 2) optionally a '@' which is preceded by two strings + * (with 0 or more characters each) separated by a ':' + * [this is for an optional username:password] + * 3) a string comprising alpha-numerics, '-' and '.' characters + * [this is for the hostname] + * 4) optionally a ':' followed by one or more numeric characters + * [this is for an optional port number] + * 5) finally, either an end of line or a series of segments, + * each of which begins with a '/', and contains anything + * except a ':' + */ + +/** + * netcam_url_match + * + * Finds the matched part of a regular expression + * + * Parameters: + * + * m A structure containing the regular expression to be used + * input The input string + * + * Returns: The string which was matched + * + */ +static char *netcam_url_match(regmatch_t m, const char *input) +{ + char *match = NULL; + int len; + + if (m.rm_so != -1) { + len = m.rm_eo - m.rm_so; + + if ((match = (char *) malloc(len + 1)) != NULL) { + strncpy(match, input + m.rm_so, len); + match[len] = '\0'; + } + } + + return (match); +} + +/** + * netcam_url_parse + * + * parses a string containing a URL into it's components + * + * Parameters: + * parse_url A structure which will receive the results + * of the parsing + * text_url The input string containing the URL + * + * Returns: Nothing + * + */ +static void netcam_url_parse(struct url_t *parse_url, const char *text_url) +{ + char *s; + int i; + const char *re = "(http|ftp)://(((.*):(.*))@)?" + "([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^:]*))"; + regex_t pattbuf; + regmatch_t matches[10]; + + memset(parse_url, 0, sizeof(struct url_t)); + /* + * regcomp compiles regular expressions into a form that is + * suitable for regexec searches + * regexec matches the URL string against the regular expression + * and returns an array of pointers to strings matching each match + * within (). The results that we need are finally placed in parse_url + */ + if (!regcomp(&pattbuf, re, REG_EXTENDED | REG_ICASE)) { + if (regexec(&pattbuf, text_url, 10, matches, 0) != REG_NOMATCH) { + for (i = 0; i < 10; i++) { + if ((s = netcam_url_match(matches[i], text_url)) != NULL) { + switch (i) { + case 1: + parse_url->service = s; + break; + case 3: + parse_url->userpass = s; + break; + case 6: + parse_url->host = s; + break; + case 8: + parse_url->port = atoi(s); + free(s); + break; + case 9: + parse_url->path = s; + break; + /* other components ignored */ + default: + free(s); + break; + } + } + } + } + } + if (!parse_url->port) { + if (!strcmp(parse_url->service, "http")) + parse_url->port = 80; + else if (!strcmp(parse_url->service, "ftp")) + parse_url->port = 21; + } + + regfree(&pattbuf); +} + +/** + * netcam_url_free + * + * General cleanup of the URL structure, called from netcam_cleanup. + * + * Parameters: + * + * parse_url Structure containing the parsed data + * + * Returns: Nothing + * + */ +static void netcam_url_free(struct url_t *parse_url) +{ + if (parse_url->service) { + free(parse_url->service); + parse_url->service = NULL; + } + + if (parse_url->userpass) { + free(parse_url->userpass); + parse_url->userpass = NULL; + } + + if (parse_url->host) { + free(parse_url->host); + parse_url->host = NULL; + } + + if (parse_url->path) { + free(parse_url->path); + parse_url->path = NULL; + } +} + +/** + * check_quote + * + * Checks a string to see if it's quoted, and if so removes the + * quotes. + * + * Parameters: + * + * str Pointer to a string + * + * Returns: Nothing, but updates the target if necessary + * + */ +static void check_quote(char *str) +{ + int len; + char ch; + + ch = *str; + + if ((ch == '"') || (ch == '\'')) { + len = strlen(str) - 1; + if (str[len] == ch) { + memmove(str, str+1, len-1); + str[len-1] = 0; + } + } +} + +/** + * netcam_check_content_length + * + * Analyse an HTTP-header line to see if it is a Content-length + * + * Parameters: + * + * header Pointer to a string containing the header line + * + * Returns: + * -1 Not a Content-length line + * >=0 Value of Content-length field + * + */ +static long netcam_check_content_length(char *header) +{ + long length=-1; /* note this is a long, not an int */ + + if (!header_process(header, "Content-Length", header_extract_number, &length)) { + /* + * Some netcams deliver some bad-format data, but if + * we were able to recognize the header section and the + * number we might as well try to use it. + */ + if (length > 0) + return length; + return -1; + } + + return length; +} + +/** + * netcam_check_content_type + * + * Analyse an HTTP-header line to see if it is a Content-type + * + * Parameters: + * + * header Pointer to a string containing the header line + * + * Returns: + * -1 Not a Content-type line + * 0 Content-type not recognized + * 1 image/jpeg + * 2 multipart/x-mixed-replace or multipart/mixed + * + */ +static int netcam_check_content_type(char *header) +{ + char *content_type = NULL; + int ret; + + if (!header_process(header, "Content-type", http_process_type, &content_type)) + return -1; + + if (!strcmp(content_type, "image/jpeg")) { + ret = 1; + } else if (!strcmp(content_type, "multipart/x-mixed-replace") || + !strcmp(content_type, "multipart/mixed")) { + ret = 2; + } else + ret = 0; + + if (content_type) + free(content_type); + + return ret; +} + + +/** + * netcam_read_next_header + * + * Read the next header record from the camera. + * + * Parameters + * + * netcam pointer to a netcam_context + * + * Returns: 0 for success, -1 if any error + * + */ +static int netcam_read_next_header(netcam_context_ptr netcam) +{ + int retval; + char *header; + + /* + * return if not connected + */ + if (netcam->sock == -1) return -1; + /* + * We are expecting a header which *must* contain a mime-type of + * image/jpeg, and *might* contain a Content-Length. + * + * If this is a "streaming" camera, the header *must* be preceded + * by a "boundary" string. + * + */ + netcam->caps.content_length = 0; + /* + * If this is a "streaming" camera, the stream header must be + * preceded by a "boundary" string + */ + if (netcam->caps.streaming) { + while (1) { + retval = header_get(netcam, &header, HG_NONE); + + if (retval != HG_OK) { + motion_log(LOG_ERR, 0, "Error reading image header"); + free(header); + return -1; + } + + retval = (strstr(header, netcam->boundary) == NULL); + free(header); + + if (!retval) + break; + } + } + + while (1) { + retval = header_get(netcam, &header, HG_NONE); + + if (retval != HG_OK) { + motion_log(LOG_ERR, 0, "Error reading image header"); + free(header); + return -1; + } + + if (*header == 0) + break; + + if ((retval = netcam_check_content_type(header)) >= 0) { + if (retval != 1) { + motion_log(LOG_ERR, 0, "Header not JPEG"); + free(header); + return -1; + } + } + + if ((retval = (int) netcam_check_content_length(header)) > 0) { + netcam->caps.content_length = 1; /* set flag */ + netcam->receiving->content_length = (int) retval; + } + + free(header); + } + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "Found image header record"); + + free(header); + return 0; +} + +/** + * netcam_read_first_header + * + * This routine attempts to read a header record from the netcam. If + * successful, it analyses the header to determine whether the camera is + * a "streaming" type. If it is, the routine looks for the Boundary-string; + * if found, it positions just past the string so that the image header can + * be read. It then reads the image header and continues processing that + * header as well. + * + * If the camera does not appear to be a streaming type, it is assumed that the + * header just read was the image header. It is processed to determine whether + * a Content-length is present. + * + * After this processing, the routine returns to the caller. + * + * Parameters: + * netcam Pointer to the netcam_context structure + * + * Returns: Content-type code if successful, -1 if not + * + */ +static int netcam_read_first_header(netcam_context_ptr netcam) +{ + int retval = -2; /* "Unknown err" */ + int ret; + int firstflag = 1; + char *header; + char *boundary; + struct context *cnt = netcam->cnt; /* for conf debug_level */ + + /* Send the initial command to the camera */ + if (send(netcam->sock, netcam->connect_request, + strlen(netcam->connect_request), 0) < 0) { + motion_log(LOG_ERR, 1, "Error sending 'connect' request"); + return -1; + } + + /* + * We expect to get back an HTTP header from the camera. + * Successive calls to header_get will return each line + * of the header received. We will continue reading until + * a blank line is received. + * + * As we process the header, we are looking for either of + * header lines Content-type or Content-length. Content-type + * is used to determine whether the camera is "streaming" or + * "non-streaming", and Content-length will be used to determine + * whether future reads of images will be controlled by the + * length specified before the image, or by a boundary string. + * + * The Content-length will only be present "just before" an + * image is sent (if it is present at all). That means that, if + * this is a "streaming" camera, it will not be present in the + * "first header", but will occur later (after a boundary-string). + * For a non-streaming camera, however, there is no boundary-string, + * and the first header is, in fact, the only header. In this case, + * there may be a Content-length. + * + */ + while (1) { /* 'Do forever' */ + ret = header_get(netcam, &header, HG_NONE); + if (ret != HG_OK) { + if (debug_level > 5) + motion_log(LOG_ERR, 0, "Error reading first header (%s)", header); + free(header); + return -1; + } + if (firstflag) { + if ((ret = http_result_code(header)) != 200) { + if (debug_level > 5) + motion_log(-1, 0, "HTTP Result code %d", ret); + free(header); + return ret; + } + firstflag = 0; + free(header); + continue; + } + if (*header == 0) /* blank line received */ + break; + + if (SETUP) + motion_log(LOG_DEBUG, 0, "Received first header"); + + /* Check if this line is the content type */ + if ((ret = netcam_check_content_type(header)) >= 0) { + retval = ret; + /* + * We are expecting to find one of three types: + * 'multipart/x-mixed-replace', 'multipart/mixed' + * or 'image/jpeg'. The first two will be received + * from a streaming camera, and the third from a + * camera which provides a single frame only. + */ + switch (ret) { + case 1: /* not streaming */ + if (SETUP) + motion_log(LOG_DEBUG, 0, "Non-streaming camera"); + + netcam->caps.streaming = 0; + break; + + case 2: /* streaming */ + if (SETUP) + motion_log(LOG_DEBUG, 0, "Streaming camera"); + + netcam->caps.streaming = 1; + + if ((boundary = strstr(header, "boundary="))) { + /* + * on error recovery this + * may already be set + * */ + if (netcam->boundary) + free(netcam->boundary); + + netcam->boundary = strdup(boundary + 9); + /* + * HTTP protocol apparently permits the boundary string + * to be quoted (the Lumenera does this, which caused + * trouble) so we need to get rid of any surrounding + * quotes + */ + check_quote(netcam->boundary); + netcam->boundary_length = strlen(netcam->boundary); + + if (SETUP) { + motion_log(LOG_DEBUG, 0, "Boundary string [%s]", + netcam->boundary); + } + } + break; + + default:{ /* error */ + motion_log(LOG_ERR, 0, "Unrecognized content type"); + free(header); + return -1; + } + } + } else if ((ret = (int) netcam_check_content_length(header)) >= 0) { + if (SETUP) + motion_log(LOG_DEBUG, 0, "Content-length present"); + + netcam->caps.content_length = 1; /* set flag */ + netcam->receiving->content_length = ret; + } + free(header); + } + free(header); + + return retval; +} + +/** + * netcam_disconnect + * + * Disconnect from the network camera. + * + * Parameters: + * + * netcam pointer to netcam context + * + * Returns: Nothing + * + */ +static void netcam_disconnect(netcam_context_ptr netcam) +{ + + if (netcam->sock > 0) { + if (close(netcam->sock) < 0) + motion_log(LOG_ERR, 1, "netcam_disconnect"); + + netcam->sock = -1; + rbuf_discard(netcam); + } +} + +/** + * netcam_connect + * + * Attempt to open the network camera as a stream device. + * + * Parameters: + * + * netcam pointer to netcam_context structure + * + * Returns: 0 for success, -1 for error + * + */ +static int netcam_connect(netcam_context_ptr netcam, struct timeval *timeout) +{ + struct sockaddr_in server; /* for connect */ + struct addrinfo *res; /* for getaddrinfo */ + int ret; + int saveflags; + int back_err; + socklen_t len; + fd_set fd_w; + struct timeval selecttime; + + /* Assure any previous connection has been closed */ + netcam_disconnect(netcam); + + /* create a new socket */ + if ((netcam->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + motion_log(LOG_ERR, 1, "Attempting to create socket"); + return -1; + } + + /* lookup the hostname given in the netcam URL */ + if ((ret = getaddrinfo(netcam->connect_host, NULL, NULL, &res)) != 0) { + motion_log(LOG_ERR, 0, "getaddrinfo() failed (%s): %s", + netcam->connect_host, gai_strerror(ret)); + netcam_disconnect(netcam); + return -1; + } + + /* + * Fill the hostname details into the 'server' structure and + * attempt to connect to the remote server + */ + memset(&server, 0, sizeof(server)); + memcpy(&server, res->ai_addr, sizeof(server)); + freeaddrinfo(res); + + server.sin_family = AF_INET; + server.sin_port = htons(netcam->connect_port); + + if (timeout) + netcam->timeout = *timeout; + + if ((setsockopt(netcam->sock, SOL_SOCKET, SO_RCVTIMEO, + &netcam->timeout, sizeof(netcam->timeout))) < 0) { + motion_log(LOG_ERR, 1, "setsockopt() failed"); + netcam_disconnect(netcam); + return -1; + } + + /* + * Unfortunately, the SO_RCVTIMEO does not affect the 'connect' + * call, so we need to be quite a bit more clever. We want to + * use netcam->timeout, so we set the socket non-blocking and + * then use a 'select' system call to control the timeout. + */ + + if ((saveflags = fcntl(netcam->sock, F_GETFL, 0)) < 0) { + motion_log(LOG_ERR, 1, "fcntl(1) on socket"); + close(netcam->sock); + netcam->sock = -1; + return -1; + } + + /* Set the socket non-blocking */ + if (fcntl(netcam->sock, F_SETFL, saveflags | O_NONBLOCK) < 0) { + motion_log(LOG_ERR, 1, "fcntl(2) on socket"); + close(netcam->sock); + netcam->sock = -1; + return -1; + } + + /* Now the connect call will return immediately */ + ret = connect(netcam->sock, (struct sockaddr *) &server, + sizeof(server)); + back_err = errno; /* save the errno from connect */ + + /* Restore the normal socket flags */ + if (fcntl(netcam->sock, F_SETFL, saveflags) < 0) { + motion_log(LOG_ERR, 1, "fcntl(3) on socket"); + close(netcam->sock); + netcam->sock = -1; + return -1; + } + + /* If the connect failed with anything except EINPROGRESS, error */ + if ((ret < 0) && (back_err != EINPROGRESS)) { + motion_log(LOG_ERR, 1, "connect() failed (%d)", back_err); + close(netcam->sock); + netcam->sock = -1; + return -1; + } + + /* Now we do a 'select' with timeout to wait for the connect */ + FD_ZERO(&fd_w); + FD_SET(netcam->sock, &fd_w); + selecttime.tv_sec = CONNECT_TIMEOUT; + selecttime.tv_usec = 0; + ret = select(FD_SETSIZE, NULL, &fd_w, NULL, &selecttime); + + if (ret == 0) { /* 0 means timeout */ + motion_log(LOG_ERR, 0, "timeout on connect()"); + close(netcam->sock); + netcam->sock = -1; + return -1; + } + + /* + * A +ve value returned from the select (actually, it must be a + * '1' showing 1 fd's changed) shows the select has completed. + * Now we must check the return code from the select. + */ + len = sizeof(ret); + + if (getsockopt(netcam->sock, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + motion_log(LOG_ERR, 1, "getsockopt after connect"); + netcam_disconnect(netcam); + return -1; + } + + /* If the return code is anything except 0, error on connect */ + if (ret) { + motion_log(LOG_ERR, 0, "connect returned error"); + netcam_disconnect(netcam); + return -1; + } + + /* The socket info is stored in the rbuf structure of our context */ + rbuf_initialize(netcam); + + return 0; /* success */ +} + + +/** + * netcam_check_buffsize + * + * This routine checks whether there is enough room in a buffer to copy + * some additional data. If there is not enough room, it will re-allocate + * the buffer and adjust it's size. + * + * Parameters: + * buff Pointer to a netcam_image_buffer structure + * numbytes The number of bytes to be copied + * + * Returns: Nothing + */ +static void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes) +{ + if ((buff->size - buff->used) >= numbytes) + return; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "expanding buffer from %d to %d bytes", + (int) buff->size, (int) buff->size + NETCAM_BUFFSIZE); + + buff->ptr = myrealloc(buff->ptr, buff->size + NETCAM_BUFFSIZE, + "netcam_check_buf_size"); + buff->size += NETCAM_BUFFSIZE; +} + +/** + * netcam_read_html_jpeg + * + * This routine reads a jpeg image from the netcam. When it is called, + * the stream is already positioned just after the image header. + * + * This routine is called under the four variations of two different + * conditions: + * 1) Streaming or non-streaming camera + * 2) Header does or does not include Content-Length + * Additionally, if it is a streaming camera, there must always be a + * boundary-string. + * + * The routine will (attempt to) read the JPEG image. If a Content-Length + * is present, it will be used (this will result in more efficient code, and + * also code which should be better at detecting and recovering from possible + * error conditions). + * + * If a boundary-string is present (and, if the camera is streaming, this + * *must* be the case), the routine will assure that it is recognized and + * acted upon. + * + * Our algorithm for this will be as follows: + * 1) If a Content-Length is present, set the variable "remaining" + * to be equal to that value, else set it to a "very large" + * number. + * 2) While there is more data available from the camera: + * a) If there is a "boundary string" specified (from the initial + * header): + * i) If the amount of data in the input buffer is less than + * the length of the boundary string, get more data into + * the input buffer (error if failure). + * ii) If the boundary string is found, check how many + * characters remain in the input buffer before the start + * of the boundary string. If that is less than the + * variable "remaining", reset "remaining" to be equal + * to that number. + * b) Try to copy up to "remaining" characters from the input + * buffer into our destination buffer. + * c) If there are no more characters available from the camera, + * exit this loop, else subtract the number of characters + * actually copied from the variable "remaining". + * 3) If Content-Length was present, and "remaining" is not equal + * to zero, generate a warning message for logging. + * + * + * Parameters: + * netcam Pointer to netcam context + * + * + * Returns: 0 for success, -1 for error + * + */ +static int netcam_read_html_jpeg(netcam_context_ptr netcam) +{ + netcam_buff_ptr buffer; + size_t remaining; /* # characters to read */ + size_t maxflush; /* # chars before boundary */ + size_t rem, rlen, ix; /* working vars */ + int retval; + char *ptr, *bptr, *rptr; + netcam_buff *xchg; + struct timeval curtime; + /* + * Initialisation - set our local pointers to the context + * information + */ + buffer = netcam->receiving; + /* Assure the target buffer is empty */ + buffer->used = 0; + /* Prepare for read loop */ + if (buffer->content_length != 0) + remaining = buffer->content_length; + else + remaining = 999999; + + /* Now read in the data */ + while (remaining) { + /* Assure data in input buffer */ + if (netcam->response->buffer_left <= 0) { + retval = rbuf_read_bufferful(netcam); + + if (retval <= 0) + break; + + netcam->response->buffer_left = retval; + netcam->response->buffer_pos = netcam->response->buffer; + } + + /* If a boundary string is present, take it into account */ + bptr = netcam->boundary; + + if (bptr) { + rptr = netcam->response->buffer_pos; + rlen = netcam->response->buffer_left; + + /* Loop through buffer looking for start of boundary */ + while (1) { + /* + * Logic gets a little complicated here. The + * problem is that we are reading in input + * data in packets, and there is a (small) + * chance that the boundary string could be + * split across successive packets. + * First a quick check if the string *might* + * be in the current buffer. + */ + if (rlen > remaining) + rlen = remaining; + + if (remaining < netcam->boundary_length) + break; + + if ((ptr = memchr(rptr, *bptr, rlen)) == NULL) + /* boundary not here (normal path) */ + break; + /* + * At least the first char was found in the + * buffer - check for the rest + */ + rem = rlen - (ptr - rptr); + for (ix = 1; (ix < rem) && (ix < netcam->boundary_length); ix++) { + if (ptr[ix] != bptr[ix]) + break; + } + + if ((ix != netcam->boundary_length) && (ix != rem)) { + /* + * Not pointing at a boundary string - + * step along input + */ + ix = ptr - rptr + 1; + rptr += ix; + rlen -= ix; + + if (rlen <= 0) + /* boundary not in buffer - go copy out + */ + break; + /* + * not yet decided - continue + * through input + */ + continue; + } + /* + * If we finish the 'for' with + * ix == boundary_length, that means we found + * the string, and should copy any data which + * precedes it into the target buffer, then + * exit the main loop. + */ + if (ix == netcam->boundary_length) { + if ((ptr - netcam->response->buffer) < (int) remaining) + remaining = ptr - netcam->response->buffer; + + /* go copy everything up to boundary */ + break; + } + + /* + * If not, and ix == rem, that means we reached + * the end of the input buffer in the middle of + * our check, which is the (somewhat messy) + * problem mentioned above. + * + * Assure there is data before potential + * boundary string + */ + if (ptr != netcam->response->buffer) { + /* + * We have a boundary string crossing + * packets :-(. We will copy all the + * data up to the beginning of the + * potential boundary, then re-position + * the (partial) string to the + * beginning and get some more input + * data. First we flush the input + * buffer up to the beginning of the + * (potential) boundary string + */ + ix = ptr - netcam->response->buffer_pos; + netcam_check_buffsize(buffer, ix); + retval = rbuf_flush(netcam, buffer->ptr + buffer->used, ix); + buffer->used += retval; + remaining -= retval; + + /* + * Now move the boundary fragment to + * the head of the input buffer. + * This is really a "hack" - ideally, + * we should have a function within the + * module netcam_wget.c to do this job! + */ + + if (debug_level > CAMERA_INFO) { + motion_log(-1, 0, + "Potential split boundary - " + "%d chars flushed, %d " + "re-positioned", ix, + (int) netcam->response->buffer_left); + } + + memmove(netcam->response->buffer, ptr, + netcam->response->buffer_left); + } /* end of boundary split over buffer */ + + retval = netcam_recv(netcam, netcam->response->buffer + + netcam->response->buffer_left, + sizeof(netcam->response->buffer) - + netcam->response->buffer_left, + NULL); + + if (retval <= 0) { /* this is a fatal error */ + motion_log(LOG_ERR, 1, "recv() fail after boundary string"); + return -1; + } + + /* Reset the input buffer pointers */ + netcam->response->buffer_left = retval + netcam->response->buffer_left; + netcam->response->buffer_pos = netcam->response->buffer; + + /* This will cause a 'continue' of the main loop */ + bptr = NULL; + + /* Return to do the boundary compare from the start */ + break; + } /* end of while(1) input buffer search */ + + /* !bptr shows we're processing split boundary */ + if (!bptr) + continue; + } /* end of if(bptr) */ + + /* boundary string not present, so just write out as much data as possible */ + if (remaining) { + maxflush = MINVAL(netcam->response->buffer_left, remaining); + netcam_check_buffsize(buffer, maxflush); + retval = rbuf_flush(netcam, buffer->ptr + buffer->used, maxflush); + buffer->used += retval; + remaining -= retval; + } + } + + /* + * read is complete - set the current 'receiving' buffer atomically + * as 'latest', and make the buffer previously in 'latest' become + * the new 'receiving' + */ + if (gettimeofday(&curtime, NULL) < 0) { + motion_log(LOG_ERR, 1, "gettimeofday in netcam_read_jpeg"); + } + + netcam->receiving->image_time = curtime; + + /* + * Calculate our "running average" time for this netcam's + * frame transmissions (except for the first time). + * Note that the average frame time is held in microseconds. + */ + if (netcam->last_image.tv_sec) { + netcam->av_frame_time = (9.0 * netcam->av_frame_time + + 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; + + if (debug_level > 5) + motion_log(-1, 0, "Calculated frame time %f", netcam->av_frame_time); + } + netcam->last_image = curtime; + + pthread_mutex_lock(&netcam->mutex); + + xchg = netcam->latest; + netcam->latest = netcam->receiving; + netcam->receiving = xchg; + netcam->imgcnt++; + /* + * We have a new frame ready. We send a signal so that + * any thread (e.g. the motion main loop) waiting for the + * next frame to become available may proceed. + */ + pthread_cond_signal(&netcam->pic_ready); + + pthread_mutex_unlock(&netcam->mutex); + + if (!netcam->caps.streaming) + netcam_disconnect(netcam); + + return 0; +} + +/** + * netcam_read_ftp_jpeg + * + * This routine reads from a netcam using the FTP protocol. + * The current implementation is still a little experimental, + * and needs some additional code for error detection and + * recovery. + */ +static int netcam_read_ftp_jpeg(netcam_context_ptr netcam) +{ + netcam_buff_ptr buffer; + int len; + netcam_buff *xchg; + struct timeval curtime; + + /* Point to our working buffer */ + buffer = netcam->receiving; + buffer->used = 0; + + /* Request the image from the remote server */ + if (ftp_get_socket(netcam->ftp) <= 0) { + motion_log(LOG_ERR, 0, "ftp_get_socket failed in netcam_read_jpeg"); + return -1; + } + + /* Now fetch the image using ftp_read. Note this is a blocking call */ + do { + /* Assure there's enough room in the buffer */ + netcam_check_buffsize(buffer, FTP_BUF_SIZE); + + /* Do the read */ + if ((len = ftp_read(netcam->ftp, buffer->ptr + buffer->used, FTP_BUF_SIZE)) < 0) + return -1; + + buffer->used += len; + } while (len > 0); + + if (gettimeofday(&curtime, NULL) < 0) { + motion_log(LOG_ERR, 1, "gettimeofday in netcam_read_jpeg"); + } + + netcam->receiving->image_time = curtime; + /* + * Calculate our "running average" time for this netcam's + * frame transmissions (except for the first time). + * Note that the average frame time is held in microseconds. + */ + if (netcam->last_image.tv_sec) { + netcam->av_frame_time = + ((9.0 * netcam->av_frame_time) + 1000000.0 * + (curtime.tv_sec - netcam->last_image.tv_sec) + + (curtime.tv_usec- netcam->last_image.tv_usec)) + / 10.0; + if (debug_level > 5) + motion_log(-1, 0, "Calculated frame time %f", netcam->av_frame_time); + } + + netcam->last_image = curtime; + + /* + * read is complete - set the current 'receiving' buffer atomically + * as 'latest', and make the buffer previously in 'latest' become + * the new 'receiving' + */ + pthread_mutex_lock(&netcam->mutex); + + xchg = netcam->latest; + netcam->latest = netcam->receiving; + netcam->receiving = xchg; + netcam->imgcnt++; + + /* + * We have a new frame ready. We send a signal so that + * any thread (e.g. the motion main loop) waiting for the + * next frame to become available may proceed. + */ + pthread_cond_signal(&netcam->pic_ready); + + pthread_mutex_unlock(&netcam->mutex); + + return 0; +} + +/** + * netcam_handler_loop + * This is the "main loop" for the handler thread. It is created + * in netcam_start when a streaming camera is detected. + * + * Parameters + * + * arg Pointer to the motion context for this camera + * + * Returns: NULL pointer + * + */ +static void *netcam_handler_loop(void *arg) +{ + int retval; + netcam_context_ptr netcam = arg; + struct context *cnt = netcam->cnt; /* needed for the SETUP macro :-( */ + + /* Store the corresponding motion thread number in TLS also for this + * thread (necessary for 'motion_log' to function properly). + */ + pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); + + if (SETUP) + motion_log(LOG_INFO, 0, "Camera handler thread [%d] started", netcam->threadnr); + + /* + * The logic of our loop is very simple. If this is a non- + * streaming camera, we re-establish connection with the camera + * and read the header record. If it's a streaming camera, we + * position to the next "boundary string" in the input stream. + * In either case, we then read the following JPEG image into the + * next available buffer, updating the "next" and "latest" indices + * in our netcam * structure. The loop continues until netcam->finish + * or cnt->finish is set. + */ + + while (!netcam->finish) { + if (netcam->response) { /* if html input */ + if (!netcam->caps.streaming) { + + if (netcam_connect(netcam, NULL) < 0) { + motion_log(LOG_ERR, 0, "re-opening camera (non-streaming)"); + /* need to have a dynamic delay here */ + continue; + } + /* Send our request and look at the response */ + if ((retval = netcam_read_first_header(netcam)) != 1) { + if (retval > 0) { + motion_log(LOG_ERR, 0, + "Unrecognized image header (%d)", retval); + } else if (retval != -1) { + motion_log(LOG_ERR, 0, + "Error in header (%d)", retval); + } + /* need to have a dynamic delay here */ + continue; + } + } else { + if (netcam_read_next_header(netcam) < 0) { + if (netcam_connect(netcam, NULL) < 0) { + motion_log(LOG_ERR, 0, + "re-opening camera (streaming)"); + SLEEP(5,0); + continue; + } + + if ((retval = netcam_read_first_header(netcam) != 2)) { + if (retval > 0) { + motion_log(LOG_ERR, 0, + "Unrecognized image header (%d)", + retval); + } else if (retval != -1) { + motion_log(LOG_ERR, 0, + "Error in header (%d)", retval); + } + /* FIXME need some limit */ + continue; + } + } + } + } + if (netcam->get_image(netcam) < 0) { + motion_log(LOG_ERR, 0, "Error getting jpeg image"); + /* if FTP connection, attempt to re-connect to server */ + if (netcam->ftp) { + close(netcam->ftp->control_file_desc); + if (ftp_connect(netcam) < 0) { + motion_log(LOG_ERR, 0, "Trying to re-connect"); + } + } + continue; + } + /* + * FIXME + * Need to check whether the image was received / decoded + * satisfactorily + */ + + /* + * If non-streaming, want to synchronize our thread with the + * motion main-loop. + */ + if (!netcam->caps.streaming) { + pthread_mutex_lock(&netcam->mutex); + + /* before anything else, check for system shutdown */ + if (netcam->finish) { + pthread_mutex_unlock(&netcam->mutex); + break; + } + + /* + * If our current loop has finished before the next + * request from the motion main-loop, we do a + * conditional wait (wait for signal). On the other + * hand, if the motion main-loop has already signalled + * us, we just continue. In either event, we clear + * the start_capture flag set by the main loop. + */ + if (!netcam->start_capture) + pthread_cond_wait(&netcam->cap_cond, &netcam->mutex); + + netcam->start_capture = 0; + + pthread_mutex_unlock(&netcam->mutex); + } + /* the loop continues forever, or until motion shutdown */ + } + + /* our thread is finished - decrement motion's thread count */ + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + + /* log out a termination message */ + motion_log(LOG_INFO, 0, "netcam camera handler: finish set, exiting"); + + /* setting netcam->thread_id to zero shows netcam_cleanup we're done */ + netcam->thread_id = 0; + + /* signal netcam_cleanup that we're all done */ + pthread_mutex_lock(&netcam->mutex); + pthread_cond_signal(&netcam->exiting); + pthread_mutex_unlock(&netcam->mutex); + + /* Goodbye..... */ + pthread_exit(NULL); +} + +static int netcam_setup_html(netcam_context_ptr netcam, struct url_t *url) { + struct context *cnt = netcam->cnt; + const char *ptr; /* working var */ + char *userpass; /* temp pointer to config value */ + char *encuserpass; /* temp storage for encoded ver */ + char *request_pass = NULL; /* temp storage for base64 conv */ + int ix; + + /* First the http context structure */ + netcam->response = (struct rbuf *) mymalloc(sizeof(struct rbuf)); + memset(netcam->response, 0, sizeof(struct rbuf)); + + /* + * The network camera may require a username and password. If + * so, the information can come from two different places in the + * motion configuration file. Either it can be present in + * the netcam_userpass, or it can be present as a part of the URL + * for the camera. We assume the first of these has a higher + * relevance. + */ + if (cnt->conf.netcam_userpass) + ptr = cnt->conf.netcam_userpass; + else + ptr = url->userpass; + + /* base64_encode needs up to 3 additional chars */ + if (ptr) { + userpass = mymalloc(strlen(ptr) + 3); + strcpy(userpass, ptr); + } else + userpass = NULL; + + /* + * Now we want to create the actual string which will be used to + * connect to the camera. It may or may not contain a username / + * password. We first compose a basic connect message, then check + * whether a username / password is required and, if so, just + * concatenate it with the request. + * + */ + /* space for final \r\n plus string terminator */ + ix = 3; + + /* See if username / password is required */ + if (userpass) { /* if either of the above are non-NULL */ + /* Allocate space for the base64-encoded string */ + encuserpass = mymalloc(BASE64_LENGTH(strlen(userpass)) + 1); + /* Fill in the value */ + base64_encode(userpass, encuserpass, strlen(userpass)); + /* Now create the last part (authorization) of the request */ + request_pass = mymalloc(strlen(connect_auth_req) + + strlen(encuserpass) + 1); + ix += sprintf(request_pass, connect_auth_req, encuserpass); + /* free the working variables */ + free(encuserpass); + } + + /* + * We are now ready to set up the netcam's "connect request". Most of + * this comes from the (preset) string 'connect_req', but additional + * characters are required if there is a proxy server, or if there is + * a username / password for the camera. The variable 'ix' currently + * has the number of characters required for username/password (which + * could be zero) and for the \r\n and string terminator. We will also + * always need space for the netcam path, and if a proxy is being used + * we also need space for a preceding 'http://{hostname}' for the + * netcam path. + */ + if (cnt->conf.netcam_proxy) { + /* + * Allocate space for a working string to contain the path. + * The extra 4 is for "://" and string terminator. + */ + ptr = mymalloc(strlen(url->service) + strlen(url->host) + + strlen(url->path) + 4); + sprintf((char *)ptr, "http://%s%s", url->host, url->path); + } else { + /* if no proxy, set as netcam_url path */ + ptr = url->path; + /* + * after generating the connect message the string + * will be freed, so we don't want netcam_url_free + * to free it as well. + */ + url->path = NULL; + } + + ix += strlen(ptr); + + /* + * Now that we know how much space we need, we can allocate space + * for the connect-request string. + */ + netcam->connect_request = mymalloc(strlen(connect_req) + ix + + strlen(netcam->connect_host)); + + /* Now create the request string with an sprintf */ + sprintf(netcam->connect_request, connect_req, ptr, + netcam->connect_host); + + if (userpass) { + strcat(netcam->connect_request, request_pass); + free(request_pass); + free(userpass); + } + + /* put on the final CRLF onto the request */ + strcat(netcam->connect_request, "\r\n"); + free((void *)ptr); + netcam_url_free(url); /* Cleanup the url data */ + + /* + * Our basic initialisation has been completed. Now we will attempt + * to connect with the camera so that we can then get a "header" + * in order to find out what kind of camera we are dealing with, + * as well as what are the picture dimensions. Note that for + * this initial connection, any failure will cause an error + * return from netcam_start (unlike later possible attempts at + * re-connecting, if the network connection is later interrupted). + */ + for (ix = 0; ix < MAX_HEADER_RETRIES; ix++) { + /* + * netcam_connect does an automatic netcam_close, so it's + * safe to include it as part of this loop + */ + if (netcam_connect(netcam, NULL) != 0) { + motion_log(LOG_ERR, 0,"Failed to open camera - check your config and that netcamera is online"); + + /* Fatal error on startup */ + ix = MAX_HEADER_RETRIES; + break;; + } + + if (netcam_read_first_header(netcam) >= 0) + break; + + motion_log(LOG_ERR, 0, "Error reading first header - re-trying"); + } + + if (ix == MAX_HEADER_RETRIES) { + motion_log(LOG_ERR, 0, "Failed to read first camera header - giving up for now"); + return -1; + } + + /* + * If this is a streaming camera, we need to position just + * past the boundary string and read the image header + */ + if (netcam->caps.streaming) { + if (netcam_read_next_header(netcam) < 0) { + motion_log(LOG_ERR, 0, + "Failed to read first stream header - giving up for now"); + return -1; + } + } + + netcam->get_image = netcam_read_html_jpeg; + return 0; +} + +static int netcam_setup_ftp(netcam_context_ptr netcam, struct url_t *url) { + struct context *cnt = netcam->cnt; + const char *ptr; + + if ((netcam->ftp = ftp_new_context()) == NULL) + return -1; + /* + * We copy the strings out of the url structure into the ftp_context + * structure. By setting url->{string} to NULL we effectively "take + * ownership" of the string away from the URL (i.e. it won't be freed + * when we cleanup the url structure later). + */ + netcam->ftp->path = url->path; + url->path = NULL; + + if (cnt->conf.netcam_userpass != NULL) + ptr = cnt->conf.netcam_userpass; + else { + ptr = url->userpass; /* don't set this one NULL, gets freed */ + } + + if (ptr != NULL) { + char *cptr; + + if ((cptr = strchr(ptr, ':')) == NULL) + netcam->ftp->user = strdup(ptr); + else { + netcam->ftp->user = mymalloc((cptr - ptr)); + memcpy(netcam->ftp->user,ptr,(cptr - ptr)); + netcam->ftp->passwd = strdup(cptr + 1); + } + } + + netcam_url_free(url); + + /* + * The ftp context should be all ready to attempt a connection with + * the server, so we try .... + */ + if (ftp_connect(netcam) < 0) { + ftp_free_context(netcam->ftp); + return -1; + } + + if (ftp_send_type(netcam->ftp, 'I') < 0) { + motion_log(LOG_ERR, 0, "Error sending TYPE I to ftp server"); + return -1; + } + + netcam->get_image = netcam_read_ftp_jpeg; + return 0; +} + +/** + * netcam_recv + * + * This routine receives the next block from the netcam. It takes care + * of the potential timeouts and interrupt which may occur because of + * the settings from setsockopt. + * + * Parameters: + * + * netcam Pointer to a netcam context + * buffptr Pointer to the receive buffer + * buffsize Length of the buffer + * timeout Pointer to struct timeval (NULL for no change) + * + * Returns: + * If successful, the length of the message received, otherwise the + * error reply from the system call. + * + */ +ssize_t netcam_recv(netcam_context_ptr netcam, void *buffptr, size_t buffsize, + struct timeval *timeout) { + ssize_t retval; + + if (timeout) { + if ((timeout->tv_sec != netcam->timeout.tv_sec) || + (timeout->tv_usec != netcam->timeout.tv_usec)) { + if ((setsockopt(netcam->sock, SOL_SOCKET, + SO_RCVTIMEO, timeout, sizeof(*timeout))) < 0) { + motion_log(LOG_ERR, 1, "setsockopt() in netcam_recv failed"); + } + } + netcam->timeout = *timeout; + } + + do { + retval = recv(netcam->sock, buffptr, buffsize, 0); + } while ((retval < 0) && ((errno == EINTR) || (errno == EAGAIN))); + + return retval; +} + +/** + * netcam_cleanup + * + * This routine releases any allocated data within the netcam context, + * then frees the context itself. Extreme care must be taken to assure + * that the multi-threading nature of the program is correctly + * handled. + * This function is also called from motion_init if first time connection + * fails and we start retrying until we get a valid first frame from the + * camera. + * + * Parameters: + * + * netcam Pointer to a netcam context + * init_retry_flag 1 when the function is called because we are retrying + * making the initial connection with a netcam and we know + * we do not need to kill a netcam handler thread + * 0 in any other case. + * + * Returns: Nothing. + * + */ +void netcam_cleanup(netcam_context_ptr netcam, int init_retry_flag) +{ + struct timespec waittime; + + if (!netcam) + return; + + /* + * This 'lock' is just a bit of "defensive" programming. It should + * only be necessary if the routine is being called from different + * threads, but in our Motion design, it should only be called from + * the motion main-loop. + */ + pthread_mutex_lock(&netcam->mutex); + + if (netcam->cnt->netcam == NULL) { + return; + } + + /* + * We set the netcam_context pointer in the motion main-loop context + * to be NULL, so that this routine won't be called a second time + */ + netcam->cnt->netcam = NULL; + + /* + * Next we set 'finish' in order to get the camera-handler thread + * to stop. + */ + netcam->finish = 1; + + /* + * If the camera is non-streaming, the handler thread could be waiting + * for a signal, so we send it one. If it's actually waiting on the + * condition, it won't actually start yet because we still have + * netcam->mutex locked. + */ + + if (!netcam->caps.streaming) { + pthread_cond_signal(&netcam->cap_cond); + } + + /* + * Once the camera-handler gets to the end of it's loop (probably as + * soon as we release netcam->mutex), because netcam->finish has been + * set it will exit it's loop, do anything it needs to do with the + * netcam context, and then send *us* as signal (netcam->exiting). + * Note that when we start our wait on netcam->exiting, our lock on + * netcam->mutex is automatically released, which will allow the + * handler to complete it's loop, notice that 'finish' is set and exit. + * This should always work, but again (defensive programming) we + * use pthread_cond_timedwait and, if our timeout (8 seconds) expires + * we just do the cleanup the handler would normally have done. This + * assures that (even if there is a bug in our code) motion will still + * be able to exit. + * If the init_retry_flag is not set the netcam_cleanup code was + * called while retrying the initial connection to a netcam and then + * there is no camera-handler started yet and thread_running must + * not be decremented. + */ + waittime.tv_sec = time(NULL) + 8; /* Seems that 3 is too small */ + waittime.tv_nsec = 0; + + if (!init_retry_flag && + pthread_cond_timedwait(&netcam->exiting, &netcam->mutex, &waittime) != 0) { + /* + * Although this shouldn't happen, if it *does* happen we will + * log it (just for the programmer's information) + */ + motion_log(-1, 0, "No response from camera " + "handler - it must have already died"); + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + } + + /* we don't need any lock anymore, so release it */ + pthread_mutex_unlock(&netcam->mutex); + + /* and cleanup the rest of the netcam_context structure */ + if (netcam->connect_host != NULL) { + free(netcam->connect_host); + } + + if (netcam->connect_request != NULL) { + free(netcam->connect_request); + } + + if (netcam->boundary != NULL) { + free(netcam->boundary); + } + + if (netcam->latest != NULL) { + if (netcam->latest->ptr != NULL) { + free(netcam->latest->ptr); + } + free(netcam->latest); + } + + if (netcam->receiving != NULL) { + if (netcam->receiving->ptr != NULL) { + free(netcam->receiving->ptr); + } + free(netcam->receiving); + } + + if (netcam->jpegbuf != NULL) { + if (netcam->jpegbuf->ptr != NULL) { + free(netcam->jpegbuf->ptr); + } + free(netcam->jpegbuf); + } + + if (netcam->response != NULL) { + free(netcam->response); + } + + if (netcam->ftp != NULL) { + ftp_free_context(netcam->ftp); + } + + pthread_mutex_destroy(&netcam->mutex); + pthread_cond_destroy(&netcam->cap_cond); + pthread_cond_destroy(&netcam->pic_ready); + pthread_cond_destroy(&netcam->exiting); + free(netcam); +} + +/** + * netcam_next + * + * This routine is called when the main 'motion' thread wants a new + * frame of video. It fetches the most recent frame available from + * the netcam, converts it to YUV420P, and returns it to motion. + * + * Parameters: + * cnt Pointer to the context for this thread + * image Pointer to a buffer for the returned image + * + * Returns: Error code + */ +int netcam_next(struct context *cnt, unsigned char *image) +{ + netcam_context_ptr netcam; + + /* + * Here we have some more "defensive programming". This check should + * never be true, but if it is just return with a "fatal error". + */ + if ((!cnt) || (!cnt->netcam)) + return NETCAM_FATAL_ERROR; + + netcam = cnt->netcam; + + if (!netcam->latest->used) { + if (debug_level) { + motion_log(LOG_INFO, 0, "netcam_next called with no data in buffer"); + } + return NETCAM_NOTHING_NEW_ERROR; + } + + /* + * If we are controlling a non-streaming camera, we synchronize the + * motion main-loop with the camera-handling thread through a signal, + * together with a flag to say "start your next capture". + */ + if (!netcam->caps.streaming) { + pthread_mutex_lock(&netcam->mutex); + netcam->start_capture = 1; + pthread_cond_signal(&netcam->cap_cond); + pthread_mutex_unlock(&netcam->mutex); + } + + /* + * If an error occurs in the JPEG decompression which follows this, + * jpeglib will return to the code within this 'if'. Basically, our + * approach is to just return a NULL (failed) to the caller (an + * error message has already been produced by the libjpeg routines) + */ + if (setjmp(netcam->setjmp_buffer)) { + return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; + } + /* If there was no error, process the latest image buffer */ + return netcam_proc_jpeg(netcam, image); +} + +/** + * netcam_start + * + * This routine is called from the main motion thread. It's job is + * to open up the requested camera device and do any required + * initialisation. If the camera is a streaming type, then this + * routine must also start up the camera-handling thread to take + * care of it. + * + * Parameters: + * + * cnt Pointer to the motion context structure for this device + * + * Returns: 0 on success, -1 on any failure + */ + +int netcam_start(struct context *cnt) +{ + netcam_context_ptr netcam; /* local pointer to our context */ + pthread_attr_t handler_attribute; /* attributes of our handler thread */ + int retval; /* working var */ + struct url_t url; /* for parsing netcam URL */ + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "entered netcam_start()"); + + memset(&url, 0, sizeof(url)); + if (SETUP) + motion_log(LOG_INFO, 0, "Camera thread starting..."); + + /* + * Create a new netcam_context for this camera + * and clear all the entries. + */ + cnt->netcam = (struct netcam_context *) + mymalloc(sizeof(struct netcam_context)); + memset(cnt->netcam, 0, sizeof(struct netcam_context)); + netcam = cnt->netcam; /* Just for clarity in remaining code */ + netcam->cnt = cnt; /* Fill in the "parent" info */ + + /* + * Fill in our new netcam context with all known initial + * values. + */ + + /* Our image buffers */ + netcam->receiving = mymalloc(sizeof(netcam_buff)); + memset(netcam->receiving, 0, sizeof(netcam_buff)); + netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE); + + netcam->jpegbuf = mymalloc(sizeof(netcam_buff)); + memset(netcam->jpegbuf, 0, sizeof(netcam_buff)); + netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE); + + netcam->latest = mymalloc(sizeof(netcam_buff)); + memset(netcam->latest, 0, sizeof(netcam_buff)); + netcam->latest->ptr = mymalloc(NETCAM_BUFFSIZE); + netcam->timeout.tv_sec = READ_TIMEOUT; + + /* Thread control structures */ + 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); + + /* Initialise the average frame time to the user's value */ + netcam->av_frame_time = 1000000.0 / cnt->conf.frame_limit; + + /* + * If a proxy has been specified, parse that URL. + */ + if (cnt->conf.netcam_proxy) { + netcam_url_parse(&url, cnt->conf.netcam_proxy); + + if (!url.host) { + motion_log(LOG_ERR, 0, "Invalid netcam_proxy (%s)", + cnt->conf.netcam_proxy); + netcam_url_free(&url); + return -1; + } + + if (url.userpass) { + motion_log(LOG_ERR, 0, "Username/password not allowed on a proxy URL"); + netcam_url_free(&url); + return -1; + } + + /* + * A 'proxy' means that our eventual 'connect' to our + * camera must be sent to the proxy, and that our 'GET' must + * include the full path to the camera host. + */ + netcam->connect_host = url.host; + url.host = NULL; + netcam->connect_port = url.port; + netcam_url_free(&url); /* Finished with proxy */ + } + + /* + * Parse the URL from the configuration data + */ + netcam_url_parse(&url, cnt->conf.netcam_url); + + if (!url.host) { + motion_log(LOG_ERR, 0, "Invalid netcam_url (%s)", cnt->conf.netcam_url); + netcam_url_free(&url); + return -1; + } + + if (cnt->conf.netcam_proxy == NULL) { + netcam->connect_host = url.host; + url.host = NULL; + netcam->connect_port = url.port; + } + + + if (!strcmp(url.service, "http")) { + retval = netcam_setup_html(netcam, &url); + } else if (!strcmp(url.service, "ftp")) { + retval = netcam_setup_ftp(netcam, &url); + } else { + motion_log(LOG_ERR, 0, "Invalid netcam service '%s' - " + "must be http or ftp", url.service); + netcam_url_free(&url); + return -1; + } + + if (retval < 0) + return -1; + + /* + * We expect that, at this point, we should be positioned to read + * the first image available from the camera (directly after the + * applicable header). We want to decode the image in order to get + * the dimensions (width and height). If successful, we will use + * these to set the required image buffer(s) in our netcam_struct. + */ + if ((retval = netcam->get_image(netcam)) != 0) { + motion_log(LOG_ERR, 0, "Failed trying to read first image"); + return -1; + } + + /* + * If an error occurs in the JPEG decompression which follows this, + * jpeglib will return to the code within this 'if'. If such an error + * occurs during startup, we will just abandon this attempt. + */ + if (setjmp(netcam->setjmp_buffer)) { + motion_log(LOG_ERR, 0, "libjpeg decompression failure " + "on first frame - giving up!"); + return -1; + } + netcam_get_dimensions(netcam); + + /* Motion currently requires that image height and width is a + * multiple of 16. So we check for this. + */ + if (netcam->width % 16) { + motion_log(LOG_ERR, 0, "netcam image width (%d) is not modulo 16", + netcam->width); + return -1; + } + + if (netcam->height % 16) { + motion_log(LOG_ERR, 0, "netcam image height (%d) is not modulo 16", + netcam->height); + return -1; + } + + /* Fill in camera details into context structure */ + cnt->imgs.width = netcam->width; + cnt->imgs.height = netcam->height; + cnt->imgs.size = (netcam->width * netcam->height * 3) / 2; + cnt->imgs.motionsize = netcam->width * netcam->height; + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + + /* + * Everything is now ready - start up the + * "handler thread". + */ + pthread_attr_init(&handler_attribute); + pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED); + pthread_mutex_lock(&global_lock); + netcam->threadnr = ++threads_running; + pthread_mutex_unlock(&global_lock); + + if ((retval = pthread_create(&netcam->thread_id, &handler_attribute, + &netcam_handler_loop, netcam)) < 0) { + motion_log(LOG_ERR, 1, "Starting camera handler thread [%d]", netcam->threadnr); + return -1; + } + + return 0; +} diff --git a/netcam.h b/netcam.h new file mode 100644 index 0000000..e188140 --- /dev/null +++ b/netcam.h @@ -0,0 +1,240 @@ +/* + * netcam.h + * + * Include file for handling network cameras. + * + * This code was inspired by the original netcam.c module + * written by Jeroen Vreeken and enhanced by several Motion + * project contributors, particularly Angel Carpintero and + * Christopher Price. + * + * Copyright 2005, William M. Brack + * This software is distributed under the GNU Public license + * Version 2. See also the file 'COPYING'. + */ +#ifndef _INCLUDE_NETCAM_H +#define _INCLUDE_NETCAM_H + +#include +#include +#include +#include +#include + +/* + * We are aiming to get the gcc compilation of motion practically "warning + * free", when using all the possible warning switches. The following macro + * is to allow some function protypes to include parameters which, at the + * moment, are not used, but either still "need to be declared", or else are + * planned to be used in the near future. Eventually this macro will go into + * motion.h, but that will be a little later. + */ +/** + * ATTRIBUTE_UNUSED: + * + * Macro used to signal to GCC unused function parameters + */ +#ifdef __GNUC__ +#ifdef HAVE_ANSIDECL_H +#include +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif +#else +#define ATTRIBUTE_UNUSED +#endif + +/* netcam_wget.h needs to have netcam_context_ptr */ +typedef struct netcam_context *netcam_context_ptr; + +#include "netcam_wget.h" /* needed for struct rbuf */ + +#define NETCAM_BUFFSIZE 4096 /* Initial size reserved for a JPEG + image. If expansion is required, + this value is also used for the + amount to increase. */ + +/* + * Error return codes for netcam routines. The values are "bit + * significant". All error returns will return bit 1 set to indicate + * these are "netcam errors"; additional bits set will give more detail + * as to what kind of error it was. + * Bit 0 is reserved for V4L type errors. + * + */ +#define NETCAM_GENERAL_ERROR 0x02 /* binary 000010 */ +#define NETCAM_NOTHING_NEW_ERROR 0x06 /* binary 000110 */ +#define NETCAM_JPEG_CONV_ERROR 0x0a /* binary 001010 */ +#define NETCAM_RESTART_ERROR 0x12 /* binary 010010 */ +#define NETCAM_FATAL_ERROR -2 + +/* + * struct url_t is used when parsing the user-supplied URL, as well as + * when attempting to connect to the netcam. + */ +struct url_t { + char *service; + char *userpass; + char *host; + int port; + char *path; +}; + +/* + * We use a special "triple-buffer" technique. There are + * three separate buffers (latest, receiving and jpegbuf) + * which are each described using a struct netcam_image_buff + */ +typedef struct netcam_image_buff { + 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 */ +} netcam_buff; +typedef netcam_buff *netcam_buff_ptr; + +/* + * struct netcam_context contains all the structures and other data + * for an individual netcam. + */ +typedef struct netcam_context { + struct context *cnt; /* pointer to parent motion + context structure */ + + int finish; /* flag to break the camera- + handling thread out of it's + infinite loop in emergency */ + + int threadnr; /* motion's thread number for + the camera-handling thread + (if required). Used for + error reporting */ + + pthread_t thread_id; /* thread i.d. for a camera- + handling thread (if required). + Not currently used, but may be + useful in the future */ + + pthread_mutex_t mutex; /* mutex used with conditional waits */ + + pthread_cond_t exiting; /* pthread condition structure to let + the camera-handler acknowlege that + it's finished */ + + pthread_cond_t cap_cond; /* pthread condition structure to + initiate next capture request (used + only with non-streaming cameras */ + + pthread_cond_t pic_ready; /* pthread condition structure used + for synchronisation between the + camera handler and the motion main + loop, showing new frame is ready */ + + int start_capture; /* besides our signalling condition, + we also keep a flag to assure the + camera-handler will always start + a new cycle as soon as possible, + even if it's not currently waiting + on the condition. */ + + char *connect_host; /* the host to connect to (may be + either the camera host, or + possibly a proxy) */ + + int connect_port; /* usually will be 80, but can be + specified as something else by + the user */ + + char *connect_request; /* contains the complete string + required for connection to the + camera */ + + int sock; /* fd for the camera's socket. + Note that this value is also + present within the struct + rbuf *response. */ + + struct timeval timeout; /* The current timeout setting for + the socket. */ + + struct rbuf *response; /* this structure (defined in the + netcam_wget module) contains + the context for an HTTP + connection. Note that this + structure includes a large + buffer for the HTTP data */ + + struct ftp_context *ftp; /* this structure contains the + context for FTP connection */ + + int (*get_image)(netcam_context_ptr); + /* Function to fetch the image from + the netcam. It is initialised in + netcam_setup depending upon whetheer + the picture source is from an http + server or from an ftp server */ + + + struct netcam_caps { /* netcam capabilities: */ + unsigned char streaming; /* 1 - supported */ + unsigned char content_length; /* 0 - unsupported */ + } caps; + + char *boundary; /* 'boundary' string when used to + separate mjpeg images */ + + size_t boundary_length; /* string length of the boundary + string */ + + /* Three separate buffers are used + for handling the data. Their + definitions follow: */ + + netcam_buff_ptr latest; /* This buffer contains the latest + frame received from the camera */ + + netcam_buff_ptr receiving; /* This buffer is used for receiving + data from the camera */ + + netcam_buff_ptr jpegbuf; /* This buffer is used for jpeg + decompresssion */ + + int imgcnt; /* count for # of received jpegs */ + int imgcnt_last; /* remember last count to check if a new + image arrived */ + + int error_count; /* simple count of number of errors since + last good frame was received */ + + unsigned int width; /* info for decompression */ + unsigned int height; + + struct timeval last_image; /* time the most recent image was + received */ + + float av_frame_time; /* "running average" of time between + successive frames (microseconds) */ + + struct jpeg_error_mgr jerr; + jmp_buf setjmp_buffer; + + int jpeg_error; /* flag to show error or warning + occurred during decompression*/ +} netcam_context; + + +/* + * Declare prototypes for our external entry points + */ +/* Within netcam_jpeg.c */ +int netcam_proc_jpeg (struct netcam_context *, unsigned char *); +void netcam_get_dimensions (struct netcam_context *); +/* Within netcam.c */ +int netcam_start (struct context *); +int netcam_next (struct context *, unsigned char *); +void netcam_cleanup (struct netcam_context *, int); +ssize_t netcam_recv(netcam_context_ptr, void *, size_t, struct timeval *); + +#endif diff --git a/netcam_ftp.c b/netcam_ftp.c new file mode 100644 index 0000000..8ad9c21 --- /dev/null +++ b/netcam_ftp.c @@ -0,0 +1,869 @@ +/** +* Much of the FTP code was inspired by the nanoftp.c module from +* libxml2 (Copyright Daniel Veillard, 2003). The routines have been +* modified to fit the needs of the Motion project. +* +* Copyright 2005, William M. Brack +* This software is distributed under the GNU Public license Version 2. +* See also the file 'COPYING'. +* +*/ +#include "motion.h" /* needs to come first, because _GNU_SOURCE_ set there */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netcam.h" +#include "netcam_ftp.h" + +/** +* ftp_new_context +* +* Create a new FTP context structure +* +* Parameters +* +* None +* +* Returns: Pointer to the newly-created structure, NULL if error +* +*/ +ftp_context_pointer ftp_new_context(void) +{ + ftp_context_pointer ret; + + /* note that mymalloc will exit on any problem */ + ret = mymalloc(sizeof(ftp_context)); + + memset(ret, 0, sizeof(ftp_context)); + ret->control_file_desc = -1; /* no control connection yet */ + ret->data_file_desc = -1; /* no data connection yet */ + return ret; +} + +/** +* ftp_free_context +* +* Free the resources allocated for this context +* +* Parameters +* +* ctxt Pointer to the ftp_context structure +* +* Returns: Nothing +* +*/ +void ftp_free_context(ftp_context_pointer ctxt) +{ + if (ctxt == NULL) + return; + + if (ctxt->path != NULL) + free(ctxt->path); + + if (ctxt->user) + free(ctxt->user); + + if (ctxt->passwd) + free(ctxt->passwd); + + if (ctxt->control_file_desc >= 0) + close(ctxt->control_file_desc); + + free(ctxt); +} + +/** +* ftp_parse_response +* +* Parses the answer from the server, extracting the numeric code. +* +* Parameters: +* +* buf the buffer containing the response +* len the buffer length +* +* Returns: +* 0 for errors +* +XXX for last line of response +* -XXX for response to be continued +*/ +static int ftp_parse_response(char *buf, int len) { + int val = 0; + + if (len < 3) + return(-1); + if ((*buf >= '0') && (*buf <= '9')) + val = val * 10 + (*buf - '0'); + else + return(0); + + buf++; + + if ((*buf >= '0') && (*buf <= '9')) + val = val * 10 + (*buf - '0'); + else + return(0); + + buf++; + + if ((*buf >= '0') && (*buf <= '9')) + val = val * 10 + (*buf - '0'); + else + return(0); + + buf++; + + if (*buf == '-') + return(-val); + + return(val); +} + +/** +* ftp_get_more +* +* Read more information from the FTP control connection +* +* Parameters: +* +* ctxt pointer to an FTP context +* +* Returns the number of bytes read, < 0 indicates an error +*/ +static int ftp_get_more(ftp_context_pointer ctxt) { + int len; + int size; + + /* Validate that our context structure is valid */ + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + if ((ctxt->control_buffer_index < 0) || (ctxt->control_buffer_index > FTP_BUF_SIZE)) + return(-1); + + if ((ctxt->control_buffer_used < 0) || (ctxt->control_buffer_used > FTP_BUF_SIZE)) + return(-1); + + if (ctxt->control_buffer_index > ctxt->control_buffer_used) + return(-1); + + /* + * First pack the control buffer + */ + if (ctxt->control_buffer_index > 0) { + memmove(&ctxt->control_buffer[0], + &ctxt->control_buffer[ctxt->control_buffer_index], + ctxt->control_buffer_used - ctxt->control_buffer_index); + ctxt->control_buffer_used -= ctxt->control_buffer_index; + ctxt->control_buffer_index = 0; + } + size = FTP_BUF_SIZE - ctxt->control_buffer_used; + + if (size == 0) { + return(0); + } + + /* + * Read the amount left on the control connection + */ + if ((len = recv(ctxt->control_file_desc, + &ctxt->control_buffer[ctxt->control_buffer_index], + size, 0)) < 0) { + motion_log(LOG_ERR, 1, "recv failed in ftp_get_more"); + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + ctxt->control_buffer_used += len; + ctxt->control_buffer[ctxt->control_buffer_used] = 0; + + return(len); +} + +/** +* ftp_get_response +* +* Read the response from the FTP server after a command. +* +* Parameters +* +* ctxt pointer to an FTP context +* +* Returns the code number +*/ +static int ftp_get_response(ftp_context_pointer ctxt) { + char *ptr, *end; + int len; + int res = -1, cur = -1; + + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + get_more: + /* + * Assumes everything up to control_buffer[control_buffer_index] has been read + * and analyzed. + */ + len = ftp_get_more(ctxt); + if (len < 0) { + return(-1); + } + if ((ctxt->control_buffer_used == 0) && (len == 0)) { + return(-1); + } + ptr = &ctxt->control_buffer[ctxt->control_buffer_index]; + end = &ctxt->control_buffer[ctxt->control_buffer_used]; + + while (ptr < end) { + cur = ftp_parse_response(ptr, end - ptr); + if (cur > 0) { + /* + * Successfully scanned the control code, skip + * till the end of the line, but keep the index to be + * able to analyze the result if needed. + */ + res = cur; + ptr += 3; + ctxt->control_buffer_answer = ptr - ctxt->control_buffer; + while ((ptr < end) && (*ptr != '\n')) ptr++; + if (*ptr == '\n') ptr++; + if (*ptr == '\r') ptr++; + break; + } + while ((ptr < end) && (*ptr != '\n')) + ptr++; + if (ptr >= end) { + ctxt->control_buffer_index = ctxt->control_buffer_used; + goto get_more; + } + if (*ptr != '\r') ptr++; + } + + if (res < 0) + goto get_more; + ctxt->control_buffer_index = ptr - ctxt->control_buffer; + return(res / 100); +} + +/** +* Send the user authentication +*/ + +static int ftp_send_user(ftp_context_pointer ctxt) { + char buf[200]; + int len; + int res; + + if (ctxt->user == NULL) + snprintf(buf, sizeof(buf), "USER anonymous\r\n"); + else + snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user); + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_send_user"); + return(res); + } + return(0); +} + +/** +* Send the password authentication +*/ + +static int ftp_send_passwd(ftp_context_pointer ctxt) { + char buf[200]; + int len; + int res; + + if (ctxt->passwd == NULL) + snprintf(buf, sizeof(buf), "PASS anonymous@\r\n"); + else + snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd); + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_send_passwd"); + return(res); + } + return(0); +} + +/** +* ftp_quit +* +* Send a QUIT command to the server +* +* Parameters: +* +* ctxt pointer to an FTP context +* +* Returns -1 in case of error, 0 otherwise +*/ + + +static int ftp_quit(ftp_context_pointer ctxt) { + char buf[200]; + int len, res; + + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + snprintf(buf, sizeof(buf), "QUIT\r\n"); + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_quit"); + return(res); + } + return(0); +} + +/** +* ftp_connect +* +* Tries to open a control connection +* +* Parameters: +* +* ctxt an FTP context +* +* Returns -1 in case of error, 0 otherwise +*/ + +int ftp_connect(netcam_context_ptr netcam) { + ftp_context_pointer ctxt; + struct hostent *hp; + int port; + int res; + int addrlen = sizeof (struct sockaddr_in); + + if (netcam == NULL) + return -1; + ctxt = netcam->ftp; + if (ctxt == NULL) + return(-1); + if (netcam->connect_host == NULL) + return(-1); + + /* + * do the blocking DNS query. + */ + port = netcam->connect_port; + if (port == 0) + port = 21; + + memset (&ctxt->ftp_address, 0, sizeof(ctxt->ftp_address)); + + hp = gethostbyname (netcam->connect_host); + if (hp == NULL) { + motion_log(LOG_ERR, 1, "gethostbyname failed in ftp_connect"); + return (-1); + } + if ((unsigned int) hp->h_length > + sizeof(((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr)) { + motion_log(LOG_ERR, 1, "gethostbyname address mismatch " + "in ftp_connect"); + return (-1); + } + + /* + * Prepare the socket + */ + ((struct sockaddr_in *)&ctxt->ftp_address)->sin_family = AF_INET; + memcpy (&((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr, + hp->h_addr_list[0], hp->h_length); + ((struct sockaddr_in *)&ctxt->ftp_address)->sin_port = + (u_short)htons ((unsigned short)port); + ctxt->control_file_desc = socket (AF_INET, SOCK_STREAM, 0); + addrlen = sizeof (struct sockaddr_in); + + if (ctxt->control_file_desc < 0) { + motion_log(LOG_ERR, 1, "socket failed"); + return(-1); + } + + /* + * Do the connect. + */ + if (connect(ctxt->control_file_desc, (struct sockaddr *) &ctxt->ftp_address, + addrlen) < 0) { + motion_log(LOG_ERR, 1, "Failed to create a connection"); + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + /* + * Wait for the HELLO from the server. + */ + res = ftp_get_response(ctxt); + if (res != 2) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + /* + * Do the authentication + */ + res = ftp_send_user(ctxt); + if (res < 0) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + res = ftp_get_response(ctxt); + switch (res) { + case 2: + return(0); + case 3: + break; + case 1: + case 4: + case 5: + case -1: + default: + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + res = ftp_send_passwd(ctxt); + if (res < 0) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + res = ftp_get_response(ctxt); + switch (res) { + case 2: + break; + case 3: + motion_log(LOG_ERR, 0, "FTP server asking for ACCT on anonymous"); + case 1: + case 4: + case 5: + case -1: + default: + close(ctxt->control_file_desc); ctxt->control_file_desc = -1; + ctxt->control_file_desc = -1; + return(-1); + } + + return(0); +} + +/** +* ftp_get_connection +* +* Try to open a data connection to the server. +* +* Parameters: +* +* ctxt pointer to an FTP context +* +* Returns -1 incase of error, 0 otherwise +*/ + +static int ftp_get_connection(ftp_context_pointer ctxt) { + char buf[200], *cur; + int len, i; + int res; + int on; + unsigned char ad[6], *adp, *portp; + unsigned int temp[6]; + struct sockaddr_in data_address; + unsigned int data_address_length; + + if (ctxt == NULL) + return(-1); + + /* set up a socket for our data address */ + if (ctxt->data_file_desc != -1) + close(ctxt->data_file_desc); + memset (&data_address, 0, sizeof(data_address)); + ctxt->data_file_desc = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ctxt->data_file_desc < 0) { + motion_log(LOG_ERR, 1, "socket failed"); + return (-1); + } + on = 1; + if (setsockopt(ctxt->data_file_desc, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) { + motion_log(LOG_ERR, 1, "setting socket option SO_REUSEADDR"); + return -1; + } + + ((struct sockaddr_in *)&data_address)->sin_family = AF_INET; + data_address_length = sizeof (struct sockaddr_in); + + if (ctxt->passive) { + /* send PASV command over control channel */ + snprintf (buf, sizeof(buf), "PASV\r\n"); + len = strlen (buf); + res = send(ctxt->control_file_desc, buf, len, 0); + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_get_connection"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + /* check server's answer */ + res = ftp_get_response(ctxt); + if (res != 2) { + if (res == 5) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-1); + } else { + /* + * retry with an active connection + */ + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + ctxt->passive = 0; + } + } + /* parse the IP address and port supplied by the server */ + cur = &ctxt->control_buffer[ctxt->control_buffer_answer]; + while (((*cur < '0') || (*cur > '9')) && *cur != '\0') + cur++; + if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2], + &temp[3], &temp[4], &temp[5]) != 6) { + motion_log(LOG_ERR, 0, "Invalid answer to PASV"); + if (ctxt->data_file_desc != -1) { + close (ctxt->data_file_desc); + ctxt->data_file_desc = -1; + } + return (-1); + } + for (i=0; i<6; i++) + ad[i] = (unsigned char) (temp[i] & 0xff) ; + memcpy (&((struct sockaddr_in *)&data_address)->sin_addr, &ad[0], 4); + memcpy (&((struct sockaddr_in *)&data_address)->sin_port, &ad[4], 2); + + /* Now try to connect to the data port */ + if (connect(ctxt->data_file_desc, (struct sockaddr *) &data_address, + data_address_length) < 0) { + motion_log(LOG_ERR, 1, "Failed to create a data connection"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return (-1); + } + } else { + /* + * We want to bind to a port to receive the data. To do this, + * we need the address of our host. One easy way to get it is + * to get the info from the control connection that we have + * with the remote server + */ + getsockname(ctxt->control_file_desc, (struct sockaddr *)&data_address, + &data_address_length); + ((struct sockaddr_in *)&data_address)->sin_port = 0; + + /* bind to the socket - should give us a unique port */ + if (bind(ctxt->data_file_desc, (struct sockaddr *) &data_address, + data_address_length) < 0) { + motion_log(LOG_ERR, 1, "bind failed"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return (-1); + } + + /* we get the port number by reading back in the sockaddr */ + getsockname(ctxt->data_file_desc, (struct sockaddr *)&data_address, + &data_address_length); + + /* set up a 'listen' on the port to get the server's connection */ + if (listen(ctxt->data_file_desc, 1) < 0) { + motion_log(LOG_ERR, 1, "listen failed"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return (-1); + } + + /* now generate the PORT command */ + adp = (unsigned char *) &((struct sockaddr_in *)&data_address)->sin_addr; + portp = (unsigned char *) &((struct sockaddr_in *)&data_address)->sin_port; + snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n", + adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff, + portp[0] & 0xff, portp[1] & 0xff); + + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + + /* send the PORT command to the server */ + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_get_connection"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + + res = ftp_get_response(ctxt); + + if (res != 2) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-1); + } + } + + return(ctxt->data_file_desc); +} + +/** +* ftp_close_connection +* +* Close the data connection from the server +* +* Parameters: +* +* ctxt Pointer to an FTP context +* +* Returns -1 in case of error, 0 otherwise +*/ + +static int ftp_close_connection(ftp_context_pointer ctxt) { + int res; + fd_set rfd, efd; + struct timeval tv; + + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + + /* Check for data on the control channel */ + tv.tv_sec = 15; + tv.tv_usec = 0; + FD_ZERO(&rfd); + FD_SET(ctxt->control_file_desc, &rfd); + FD_ZERO(&efd); + FD_SET(ctxt->control_file_desc, &efd); + res = select(ctxt->control_file_desc + 1, &rfd, NULL, &efd, &tv); + + if (res < 0) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + if (res == 0) { /* timeout */ + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + } else { /* read the response */ + res = ftp_get_response(ctxt); + + if (res != 2) { /* should be positive completion (2) */ + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + } + + return(0); +} + +/** +* ftp_get_socket +* +* Initiate fetch of the given file from the server. +* +* Parameters: +* +* ctxt an FTP context +* +* Returns the socket for the data connection, or <0 in case of error +*/ + + +int ftp_get_socket(ftp_context_pointer ctxt) { + char buf[300]; + int res, len; + int acfd; + + if ((ctxt == NULL) || (ctxt->path == NULL)) + return(-1); + + /* Set up the data connection */ + ctxt->data_file_desc = ftp_get_connection(ctxt); + + if (ctxt->data_file_desc == -1) + return(-1); + + /* generate a "retrieve" command for the file */ + snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path); + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + + /* send it to the server */ + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_get_socket"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + + /* check the answer */ + res = ftp_get_response(ctxt); + + if (res != 1) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-res); + } + + /* + * if not a passive connection, need to do an accept to get the + * connection from the server + */ + if (!ctxt->passive) { + struct sockaddr_in data_address; + unsigned int data_address_length = sizeof(struct sockaddr_in); + + if ((acfd = accept(ctxt->data_file_desc, (struct sockaddr *)&data_address, + &data_address_length)) < 0) { + motion_log(LOG_ERR, 1, "accept in ftp_get_socket"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return -1; + } + + close(ctxt->data_file_desc); + ctxt->data_file_desc = acfd; + } + + return(ctxt->data_file_desc); +} +/** +* ftp_send_type +* +* Send a TYPE (either 'I' or 'A') command to the server +* +* Parameters +* +* ctxt pointer to the ftp_context +* type ascii character ('I' or 'A') +* +* Returns 0 for success, negative error code for failure +* +*/ +int ftp_send_type(ftp_context_pointer ctxt, char type) { + char buf[100], utype; + int len, res; + + utype = toupper(type); + /* Assure transfer will be in "image" mode */ + snprintf(buf, sizeof(buf), "TYPE I\r\n"); + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "send failed in ftp_get_socket"); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + + res = ftp_get_response(ctxt); + + if (res != 2) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-res); + } + + return 0; +} + +/** +* ftp_read +* +* This function tries to read len bytes from the existing FTP +* connection and saves them in dest. This is a blocking call. +* +* Parameters: +* ctxt the FTP context +* dest a buffer +* len the buffer length +* +* Returns: the number of bytes read. +* 0 is an indication of an end of connection. +* -1 indicates a parameter error. +*/ +int ftp_read(ftp_context_pointer ctxt, void *dest, int len) { + if (ctxt == NULL) + return(-1); + + if (ctxt->data_file_desc < 0) + return(0); + + if (dest == NULL) + return(-1); + + if (len <= 0) + return(0); + + len = recv(ctxt->data_file_desc, dest, len, 0); + + if (len <= 0) { + if (len < 0) + motion_log(LOG_ERR, 1, "recv failed in ftp_read"); + ftp_close_connection(ctxt); + } + + return(len); +} + + +/** +* ftp_close +* +* Close the connection and both control and transport +* +* Parameters: +* +* ctxt Pointer to an FTP context +* +* Returns -1 in case of error, 0 otherwise +*/ +int ftp_close(ftp_context_pointer ctxt) { + if (ctxt == NULL) + return(-1); + + if (ctxt->data_file_desc >= 0) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + } + + if (ctxt->control_file_desc >= 0) { + ftp_quit(ctxt); + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + } + + ftp_free_context(ctxt); + return(0); +} diff --git a/netcam_ftp.h b/netcam_ftp.h new file mode 100644 index 0000000..13a7d2c --- /dev/null +++ b/netcam_ftp.h @@ -0,0 +1,44 @@ + +/** + * Much of the FTP routines was inspired by the nanoftp.c module from + * libxml2 (Copyright Daniel Veillard, 2003). The routines have been + * modified to fit the needs of the Motion project. + * + * Copyright 2005, William M. Brack + * This software is distributed under the GNU Public license Version 2. + * See also the file 'COPYING'. + * + */ +#ifndef _INCLUDE_NETCAM_FTP_H +#define _INCLUDE_NETCAM_FTP_H + +#define FTP_BUF_SIZE 1024 + +typedef struct ftp_context { + char *path; /* the path within the URL */ + char *user; /* user string */ + char *passwd; /* passwd string */ + struct sockaddr_in ftp_address; /* the socket addr structure */ + int passive; /* flag show passive/active mode used */ + int control_file_desc; /* file descriptor for the control socket */ + int data_file_desc; /* file descriptor for the data socket */ + int state; /* WRITE / READ / CLOSED */ + int returnValue; /* the protocol return value */ + + /* buffer for data received from the control connection */ + char control_buffer[FTP_BUF_SIZE + 1]; + int control_buffer_index; + int control_buffer_used; + int control_buffer_answer; +} ftp_context, *ftp_context_pointer; + +/* The public interface */ +ftp_context_pointer ftp_new_context(void); +void ftp_free_context(ftp_context_pointer); +ftp_context_pointer ftpOpen(const char *); +int ftp_connect(netcam_context_ptr); +int ftp_send_type(ftp_context_pointer, const char); +int ftp_get_socket(ftp_context_pointer); +int ftp_read(ftp_context_pointer, void *, int); +int ftp_close(ftp_context_pointer); +#endif diff --git a/netcam_jpeg.c b/netcam_jpeg.c new file mode 100644 index 0000000..4e55bbd --- /dev/null +++ b/netcam_jpeg.c @@ -0,0 +1,492 @@ +/* + * netcam_jpeg.c + * + * Module for handling JPEG decompression fornetwork cameras. + * + * This code was inspired by the original module written by + * Jeroen Vreeken and enhanced by several Motion project + * contributors, particularly Angel Carpintero and + * Christopher Price. + * + * Copyright 2005, William M. Brack + * This program is published under the GNU Public license + */ + +#include "motion.h" + +#include +#include +#include +#include +#include + +#include "rotate.h" + +/* + * netcam_source_mgr is a locally-defined structure to contain elements + * which are not present in the standard libjpeg (the element 'pub' is a + * pointer to the standard information) + */ +typedef struct { + struct jpeg_source_mgr pub; + char *data; + int length; + JOCTET *buffer; + boolean start_of_file; +} netcam_source_mgr; + +typedef netcam_source_mgr *netcam_src_ptr; + + +/* + * Here we declare function prototypes for all the routines involved with + * overriding libjpeg functions. The reason these are required is that, + * although the standard library handles input and output with stdio, + * we are working with "remote" data (from the camera or from a file). + */ +static void netcam_init_source(j_decompress_ptr); +static boolean netcam_fill_input_buffer(j_decompress_ptr); +static void netcam_skip_input_data(j_decompress_ptr, long); +static void netcam_term_source(j_decompress_ptr); +static void netcam_memory_src(j_decompress_ptr, char *, int); +static void netcam_error_exit(j_common_ptr); + +static void netcam_init_source(j_decompress_ptr cinfo) +{ + /* + * Get our "private" structure from the libjpeg structure + */ + netcam_src_ptr src = (netcam_src_ptr) cinfo->src; + /* + * Set the 'start_of_file' flag in our private structure + * (used by my_fill_input_buffer) + */ + src->start_of_file = TRUE; +} + +static boolean netcam_fill_input_buffer(j_decompress_ptr cinfo) +{ + netcam_src_ptr src = (netcam_src_ptr) cinfo->src; + size_t nbytes; + + /* + * start_of_file is set any time netcam_init_source has been called + * since the last entry to this routine. This would be the normal + * path when a new image is to be processed. It is assumed that + * this routine will only be called once for the entire image. + * If an unexpected call (with start_of_file FALSE) occurs, the + * routine will insert a "fake" "end of image" marker and return + * to the library to process whatever data remains from the original + * image (the one with errors). + * + * I'm not yet clear on what the result (from the application's + * point of view) will be from this logic. If the application + * expects that a completely new image will be started, this will + * give trouble. + */ + if (src->start_of_file) { + nbytes = src->length; + src->buffer = (JOCTET *) src->data; + } else { + /* Insert a fake EOI marker - as per jpeglib recommendation */ + if (debug_level) + motion_log(LOG_INFO, 0, "**fake EOI inserted**"); + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; /* 0xD9 */ + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + +static void netcam_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + netcam_src_ptr src = (netcam_src_ptr) cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) netcam_fill_input_buffer (cinfo); + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void netcam_term_source(j_decompress_ptr cinfo ATTRIBUTE_UNUSED) +{ +} + +/** + * netcam_memory_src + * + * Routine to setup for fetching data from a netcam buffer, used by the + * JPEG library decompression routine. + * + * Parameters: + * cinfo pointer to the jpeg decompression object + * data pointer to the image data received from a netcam + * length total size of the image + * + * Returns: Nothing + * + */ +static void netcam_memory_src(j_decompress_ptr cinfo, char *data, int length) +{ + netcam_src_ptr src; + + if (cinfo->src == NULL) { + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof (netcam_source_mgr)); + } + + src = (netcam_src_ptr)cinfo->src; + src->data = data; + src->length = length; + src->pub.init_source = netcam_init_source; + src->pub.fill_input_buffer = netcam_fill_input_buffer; + src->pub.skip_input_data = netcam_skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = netcam_term_source; + src->pub.bytes_in_buffer = 0; + src->pub.next_input_byte = NULL; +} + +/** + * netcam_error_exit + * + * Routine to override the libjpeg error exit routine so + * that we can just thow away the bad frame and continue + * with more data from the netcam. + * + * Parameters + * + * cinfo pointer to the decompression control structure + * + * Returns: does an (ugly) longjmp to get back to netcam_jpeg + * code + * + */ +static void netcam_error_exit(j_common_ptr cinfo) +{ + /* fetch our pre-stored pointer to the netcam context */ + netcam_context_ptr netcam = cinfo->client_data; + /* output the message associated with the error */ + (*cinfo->err->output_message)(cinfo); + /* set flag to show the decompression had errors */ + netcam->jpeg_error |= 1; + /* need to "cleanup" the aborted decompression */ + jpeg_destroy (cinfo); + /* jump back to wherever we started */ + longjmp(netcam->setjmp_buffer, 1); +} + +/** + * netcam_output_message + * + * Routine to override the libjpeg error message output routine. + * We do this so that we can output our module and thread i.d., + * as well as put the message to the motion log. + * + * Parameters + * + * cinfo pointer to the decompression control structure + * + * Returns Nothing + * + */ +static void netcam_output_message(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* fetch our pre-stored pointer to the netcam context */ + netcam_context_ptr netcam = cinfo->client_data; + + /* + * While experimenting with a "appro" netcam it was discovered + * that the jpeg data produced by the camera caused warning + * messages from libjpeg (JWRN_EXTRANEOUS_DATA). The following + * code is to assure that specific warning is ignored. + * + * NOTE: It's likely that we will discover other error message + * codes which we want to ignore. In that case, we should have + * some sort of table-lookup to decide which messages we really + * care about. + */ + if (cinfo->err->msg_code != JWRN_EXTRANEOUS_DATA) + netcam->jpeg_error |= 2; /* Set flag to show problem */ + /* + * We only display and log errors when debug_level + * is non-zero. The reasoning here is that these kinds + * of errors are only produced when the input data is + * wrong, and that indicates a network problem rather + * than a problem with the content. + */ + if (debug_level) { + /* + * Format the message according to library standards. + * Write it out to the motion log. + */ + (*cinfo->err->format_message)(cinfo, buffer); + motion_log(LOG_ERR, 0, buffer); + } +} + +/** + * netcam_init_jpeg + * + * Initialises the JPEG library prior to doing a + * decompression. + * + * Parameters: + * netcam pointer to netcam_context + * cinfo pointer to JPEG decompression context + * + * Returns: Error code + */ +static int netcam_init_jpeg(netcam_context_ptr netcam, j_decompress_ptr cinfo) +{ + netcam_buff_ptr buff; + + /* + * First we check whether a new image has arrived. If not, we + * setup to wait for 1/2 a frame time. This will (hopefully) + * help in synchronizing the camera frames with the motion main + * loop. + */ + pthread_mutex_lock(&netcam->mutex); + + if (netcam->imgcnt_last == netcam->imgcnt) { /* need to wait */ + struct timespec waittime; + struct timeval curtime; + int retcode; + + /* + * We calculate the delay time (representing the desired frame + * rate). This delay time is in *nanoseconds*. + * We will wait 0.5 seconds which gives a practical minimum + * framerate of 2 which is desired for the motion_loop to + * function. + */ + gettimeofday(&curtime, NULL); + curtime.tv_usec += 500000; + + if (curtime.tv_usec > 1000000) { + curtime.tv_usec -= 1000000; + curtime.tv_sec++; + } + + waittime.tv_sec = curtime.tv_sec; + waittime.tv_nsec = 1000L * curtime.tv_usec; + + do { + retcode = pthread_cond_timedwait(&netcam->pic_ready, + &netcam->mutex, &waittime); + } while (retcode == EINTR); + + if (retcode) { /* we assume a non-zero reply is ETIMEOUT */ + pthread_mutex_unlock(&netcam->mutex); + + if (debug_level > 3) + motion_log(-1,0,"no new pic, no signal rcvd"); + + return NETCAM_GENERAL_ERROR | NETCAM_NOTHING_NEW_ERROR; + } + + if (debug_level > 3) + motion_log(-1, 0, "***new pic delay successful***"); + } + + netcam->imgcnt_last = netcam->imgcnt; + + /* set latest buffer as "current" */ + buff = netcam->latest; + netcam->latest = netcam->jpegbuf; + netcam->jpegbuf = buff; + pthread_mutex_unlock(&netcam->mutex); + + /* clear any error flag from previous work */ + netcam->jpeg_error = 0; + + buff = netcam->jpegbuf; + /* prepare for the decompression */ + /* Initialize the JPEG decompression object */ + jpeg_create_decompress(cinfo); + + /* Set up own error exit routine */ + cinfo->err = jpeg_std_error(&netcam->jerr); + cinfo->client_data = netcam; + netcam->jerr.error_exit = netcam_error_exit; + netcam->jerr.output_message = netcam_output_message; + + /* Specify the data source as our own routine */ + netcam_memory_src(cinfo, buff->ptr, buff->used); + + /* Read file parameters (rejecting tables-only) */ + jpeg_read_header(cinfo, TRUE); + + /* Override the desired colour space */ + cinfo->out_color_space = JCS_YCbCr; + + /* Start the decompressor */ + jpeg_start_decompress(cinfo); + return netcam->jpeg_error; +} + +static int netcam_image_conv(netcam_context_ptr netcam, + struct jpeg_decompress_struct *cinfo, + unsigned char *image) +{ + JSAMPARRAY line; /* array of decomp data lines */ + unsigned char *wline; /* will point to line[0] */ +/* Working variables */ + int linesize, i; + unsigned char *upic, *vpic; + unsigned char *pic = image; + unsigned char y; /* switch for decoding YUV data */ + unsigned int width, height; + + width = cinfo->output_width; + height = cinfo->output_height; + if (width && ((width != netcam->width) || (height != netcam->height))) { + motion_log(LOG_ERR, 0, + "JPEG image size %dx%d, JPEG was %dx%d", + netcam->width, netcam->height, width, height); + jpeg_destroy_decompress (cinfo); + netcam->jpeg_error |= 4; + return netcam->jpeg_error; + } + /* Set the output pointers (these come from YUV411P definition */ + upic = pic + width * height; + vpic = upic + (width * height) / 4; + + + /* YCbCr format will give us one byte each for YUV */ + linesize = cinfo->output_width * 3; + + /* Allocate space for one line */ + line = (cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->output_components, 1); + + wline = line[0]; + y = 0; + while (cinfo->output_scanline < height) { + jpeg_read_scanlines (cinfo, line, 1); + for (i = 0; i < linesize; i += 3) { + pic[i / 3] = wline[i]; + if (i & 1) { + upic[(i / 3) / 2] = wline[i + 1]; + vpic[(i / 3) / 2] = wline[i + 2]; + } + } + pic += linesize / 3; + if (y++ & 1) { + upic += width / 2; + vpic += width / 2; + } + } + + jpeg_finish_decompress (cinfo); + jpeg_destroy_decompress (cinfo); + if (netcam->cnt->rotate_data.degrees > 0) { + /* rotate as specified */ + rotate_map(netcam->cnt, image); + } + return netcam->jpeg_error; +} + +/** + * netcam_proc_jpeg + * + * Routine to decode an image received from a netcam into a YUV420P buffer + * suitable for processing by motion. + * + * Parameters: + * netcam pointer to the netcam_context structure + * image Pointer to a buffer for the returned image + * + * Returns: + * + * 0 Success + * non-zero error code from other routines + * (e.g. netcam_init_jpeg or netcam_image_conv) + * or just NETCAM_GENERAL_ERROR + */ +int netcam_proc_jpeg(netcam_context_ptr netcam, unsigned char *image) +{ + struct jpeg_decompress_struct cinfo; /* decompression control struct */ + int retval = 0; /* value returned to caller */ + int ret; /* working var */ + + /* + * This routine is only called from the main thread. If there is + * a camera-handler thread, we need to "protect" the "latest" + * image while we decompress it. We can use netcam->mutex to + * do this; + */ + if (debug_level > 5) { + motion_log(LOG_INFO, 0, "processing jpeg image - content length " + "%d", netcam->latest->content_length); + } + + ret = netcam_init_jpeg(netcam, &cinfo); + + if (ret != 0) + return ret; + + /* Do a sanity check on dimensions + * If dimensions have changed we throw an error messages that will cause + * restart of Motion + */ + if (netcam->width) { /* 0 means not yet init'ed */ + if ((cinfo.output_width != netcam->width) || + (cinfo.output_height != netcam->height)) { + motion_log(LOG_ERR, 0, + "Camera width/height mismatch " + "with JPEG image - expected %dx%d, JPEG %dx%d", + netcam->width, netcam->height, + cinfo.output_width, cinfo.output_height); + retval = NETCAM_RESTART_ERROR; + return retval; + } + } + + /* do the conversion */ + ret = netcam_image_conv(netcam, &cinfo, image); + + if (ret != 0) + retval |= NETCAM_JPEG_CONV_ERROR; + + jpeg_destroy_decompress (&cinfo); + return retval; +} + +/** + * netcam_get_dimensions + * + * This function gets the height and width of the JPEG image + * located in the supplied netcam_image_buffer + * + * Parameters + * + * netcam pointer to the netcam context + * + * Returns: Nothing, but fills in width and height into context + * + */ +void netcam_get_dimensions(netcam_context_ptr netcam) +{ + struct jpeg_decompress_struct cinfo; /* decompression control struct */ + + netcam_init_jpeg(netcam, &cinfo); + netcam->width = cinfo.output_width; + netcam->height = cinfo.output_height; + + jpeg_destroy_decompress(&cinfo); +} diff --git a/netcam_wget.c b/netcam_wget.c new file mode 100644 index 0000000..f08f8ea --- /dev/null +++ b/netcam_wget.c @@ -0,0 +1,344 @@ +/* +Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002 + Free Software Foundation, Inc. + +Additional Copyright (C) 2004-2005 Christopher Price, +Angel Carpintero, and other contributing authors. + +Major part of this file is reused code from GNU Wget. It has been +merged and modified for use in the program Motion which is also +released under the terms of the GNU General Public License. + +GNU Wget and Motion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published +by the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GNU Wget is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Wget; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "motion.h" +#include +#include +#include +#include "netcam_wget.h" + +#define MINVAL(x, y) ((x) < (y) ? (x) : (y)) + +/* This file contains the generic routines for work with headers. + Currently they are used only by HTTP in http.c, but they can be + used by anything that cares about RFC822-style headers. + + Header is defined in RFC2068, as quoted below. Note that this + definition is not HTTP-specific -- it is virtually + indistinguishable from the one given in RFC822 or RFC1036. + + message-header = field-name ":" [ field-value ] CRLF + + field-name = token + field-value = *( field-content | LWS ) + + field-content = + + The public functions are header_get() and header_process(), which + see. */ + + +/* Get a header from read-buffer RBUF and return it in *HDR. + + As defined in RFC2068 and elsewhere, a header can be folded into + multiple lines if the continuation line begins with a space or + horizontal TAB. Also, this function will accept a header ending + with just LF instead of CRLF. + + The header may be of arbitrary length; the function will allocate + as much memory as necessary for it to fit. It need not contain a + `:', thus you can use it to retrieve, say, HTTP status line. + + All trailing whitespace is stripped from the header, and it is + zero-terminated. */ + +int header_get(netcam_context_ptr netcam, char **hdr, enum header_get_flags flags) +{ + int i; + int bufsize = 80; + + *hdr = (char *)mymalloc(bufsize); + for (i = 0; 1; i++) { + int res; + /* #### Use DO_REALLOC? */ + if (i > bufsize - 1) + *hdr = (char *)myrealloc(*hdr, (bufsize <<= 1), ""); + + res = RBUF_READCHAR (netcam, *hdr + i); + + if (res == 1) { + if ((*hdr)[i] == '\n') { + if (!((flags & HG_NO_CONTINUATIONS) || i == 0 + || (i == 1 && (*hdr)[0] == '\r'))) { + char next; + /* If the header is non-empty, we need to check if + it continues on to the other line. We do that by + peeking at the next character. */ + res = rbuf_peek(netcam, &next); + + if (res == 0) + { + (*hdr)[i] = '\0'; + return HG_EOF; + } + else if (res == -1) + { + (*hdr)[i] = '\0'; + return HG_ERROR; + } + /* If the next character is HT or SP, just continue. */ + + if (next == '\t' || next == ' ') + continue; + } + + /* Strip trailing whitespace. (*hdr)[i] is the newline; + decrement I until it points to the last available + whitespace. */ + while (i > 0 && isspace((*hdr)[i - 1])) + --i; + + (*hdr)[i] = '\0'; + break; + } + } else if (res == 0) + { + (*hdr)[i] = '\0'; + return HG_EOF; + } + else + { + (*hdr)[i] = '\0'; + return HG_ERROR; + } + } + + return HG_OK; +} + +/* Check whether HEADER begins with NAME and, if yes, skip the `:' and + the whitespace, and call PROCFUN with the arguments of HEADER's + contents (after the `:' and space) and ARG. Otherwise, return 0. */ +int header_process (const char *header, const char *name, + int (*procfun)(const char *, void *), + void *arg) +{ + /* Check whether HEADER matches NAME. */ + while (*name && (tolower (*name) == tolower (*header))) + ++name, ++header; + + if (*name || *header++ != ':') + return 0; + + header += skip_lws (header); + return ((*procfun) (header, arg)); +} + +/* Helper functions for use with header_process(). */ + +/* Extract a long integer from HEADER and store it to CLOSURE. If an + error is encountered, return 0, else 1. */ +int header_extract_number(const char *header, void *closure) +{ + const char *p = header; + long result; + + for (result = 0; isdigit (*p); p++) + result = 10 * result + (*p - '0'); + + /* Failure if no number present. */ + if (p == header) + return 0; + + /* Skip trailing whitespace. */ + p += skip_lws (p); + + /* We return the value, even if a format error follows */ + *(long *)closure = result; + + /* Indicate failure if trailing garbage is present. */ + if (*p) + return 0; + + return 1; +} + +/* Strdup HEADER, and place the pointer to CLOSURE. */ +int header_strdup(const char *header, void *closure) +{ + *(char **)closure = strdup(header); + return 1; +} + + +/* Skip LWS (linear white space), if present. Returns number of + characters to skip. */ +int skip_lws(const char *string) +{ + const char *p = string; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + ++p; + + return p - string; +} + + +/* + Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. +*/ +void base64_encode(const char *s, char *store, int length) +{ + /* Conversion table. */ + static const char tbl[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + int i; + unsigned char *p = (unsigned char *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + + /* Pad the result if necessary... */ + if (i == length + 1) + *(p - 1) = '='; + else if (i == length + 2) + *(p - 1) = *(p - 2) = '='; + + /* ...and zero-terminate it. */ + *p = '\0'; +} + +char *strdupdelim(const char *beg, const char *end) +{ + char *res = (char *)mymalloc(end - beg + 1); + memcpy (res, beg, end - beg); + + res[end - beg] = '\0'; + return res; +} + +int http_process_type(const char *hdr, void *arg) +{ + char **result = (char **)arg; + /* Locate P on `;' or the terminating zero, whichever comes first. */ + const char *p = strchr (hdr, ';'); + + if (!p) + p = hdr + strlen (hdr); + + while (p > hdr && isspace (*(p - 1))) + --p; + + *result = strdupdelim (hdr, p); + return 1; +} + +/* This is a simple implementation of buffering IO-read functions. */ + +void rbuf_initialize(netcam_context_ptr netcam) +{ + netcam->response->buffer_pos = netcam->response->buffer; + netcam->response->buffer_left = 0; +} + +int rbuf_read_bufferful(netcam_context_ptr netcam) +{ + return netcam_recv(netcam, netcam->response->buffer, + sizeof (netcam->response->buffer), 0); +} + +/* Like rbuf_readchar(), only don't move the buffer position. */ +int rbuf_peek(netcam_context_ptr netcam, char *store) +{ + if (!netcam->response->buffer_left) { + int res; + netcam->response->buffer_pos = netcam->response->buffer; + netcam->response->buffer_left = 0; + res = netcam_recv (netcam, netcam->response->buffer, + sizeof (netcam->response->buffer), NULL); + + if (res <= 0) + return res; + + netcam->response->buffer_left = res; + } + + *store = *netcam->response->buffer_pos; + return 1; +} + +/* + Flush RBUF's buffer to WHERE. Flush MAXSIZE bytes at most. + Returns the number of bytes actually copied. If the buffer is + empty, 0 is returned. +*/ +int rbuf_flush(netcam_context_ptr netcam, char *where, int maxsize) +{ + if (!netcam->response->buffer_left) + return 0; + else { + int howmuch = MINVAL ((int)netcam->response->buffer_left, maxsize); + + if (where) + memcpy(where, netcam->response->buffer_pos, howmuch); + + netcam->response->buffer_left -= howmuch; + netcam->response->buffer_pos += howmuch; + return howmuch; + } +} + +/* Discard any cached data in RBUF. */ +void rbuf_discard (netcam_context_ptr netcam) +{ + netcam->response->buffer_left = 0; + netcam->response->buffer_pos = netcam->response->buffer; +} + +/* Get the HTTP result code */ +int http_result_code(const char *header) +{ + char *cptr; + + /* assure the header starts out right */ + if (strncmp(header, "HTTP", 4)) + return -1; + + /* find the space following the HTTP/1.x */ + if ((cptr = strchr(header+4, ' ')) == NULL) + return -1; + + return atoi(cptr + 1); +} + diff --git a/netcam_wget.h b/netcam_wget.h new file mode 100644 index 0000000..e20de07 --- /dev/null +++ b/netcam_wget.h @@ -0,0 +1,92 @@ +/* +Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002 + Free Software Foundation, Inc. + +Additional Copyright (C) 2004-2005 Christopher Price, +Angel Carpintero, and other contributing authors. + +Major part of this file is reused code from GNU Wget. It has been +merged and modified for use in the program Motion which is also +released under the terms of the GNU General Public License. + +GNU Wget and Motion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published +by the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GNU Wget is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Wget; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef NETCAM_WGET_H +#define NETCAM_WGET_H + +#include "netcam.h" + +/* Retrieval stream */ +struct rbuf +{ + char buffer[4096]; /* the input buffer */ + char *buffer_pos; /* current position in the buffer */ + size_t buffer_left; /* number of bytes left in the buffer: + buffer_left = buffer_end - buffer_pos */ + int ret; /* used by RBUF_READCHAR macro */ +}; + +/* Read a character from RBUF. If there is anything in the buffer, + the character is returned from the buffer. Otherwise, refill the + buffer and return the first character. + + The return value is the same as with read(2). On buffered read, + the function returns 1. + + #### That return value is totally screwed up, and is a direct + result of historical implementation of header code. The macro + should return the character or EOF, and in case of error store it + to rbuf->err or something. */ + +#define RBUF_READCHAR(netcam, store) \ +((netcam)->response->buffer_left ? (--(netcam)->response->buffer_left, \ +*((char *) (store)) = *(netcam)->response->buffer_pos++, 1) \ +: ((netcam)->response->buffer_pos = (netcam)->response->buffer, \ +((((netcam)->response->ret = rbuf_read_bufferful (netcam)) <= 0) \ +? (netcam)->response->ret : ((netcam)->response->buffer_left = (netcam->response)->ret - 1, \ +*((char *) (store)) = *(netcam)->response->buffer_pos++,1)))) + +/* Function declarations */ +void rbuf_initialize(netcam_context_ptr); +int rbuf_initialized_p(netcam_context_ptr); +void rbuf_uninitialize(netcam_context_ptr); +int rbuf_readchar(netcam_context_ptr, char *); +int rbuf_peek(netcam_context_ptr, char *); +int rbuf_flush(netcam_context_ptr, char *, int); +void rbuf_discard(netcam_context_ptr); + +/* Internal, but used by the macro. */ +int rbuf_read_bufferful(netcam_context_ptr); + +/* How many bytes it will take to store LEN bytes in base64. */ +#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) + +void base64_encode(const char *, char *, int); +char *strdupdelim(const char *, const char *); +int http_process_type(const char *, void *); + +enum { HG_OK, HG_ERROR, HG_EOF}; +enum header_get_flags{ HG_NONE = 0,HG_NO_CONTINUATIONS = 0x2 }; + +int header_get (netcam_context_ptr, char **, enum header_get_flags); +int header_process (const char *, const char *, + int (*) (const char *, void *), void *); + +int header_extract_number(const char *, void *); +int header_strdup(const char *, void *); +int skip_lws(const char *); +int http_result_code(const char *); + +#endif /* NETCAM_WGET_H */ diff --git a/picture.c b/picture.c new file mode 100644 index 0000000..57d0b80 --- /dev/null +++ b/picture.c @@ -0,0 +1,681 @@ +/* picture.c + * + * Various funtions for saving/loading pictures. + * Copyright 2002 by Jeroen Vreeken (pe1rxq@amsat.org) + * Portions of this file are Copyright by Lionnel Maugis + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#include "picture.h" +#include "motion.h" +#include "event.h" + +#include +#include + +/* The following declarations and 5 functions are jpeg related + * functions used by put_jpeg_grey_memory and put_jpeg_yuv420p_memory + */ +typedef struct { + struct jpeg_destination_mgr pub; + JOCTET *buf; + size_t bufsize; + size_t jpegsize; +} mem_destination_mgr; + +typedef mem_destination_mgr *mem_dest_ptr; + + +METHODDEF(void) init_destination(j_compress_ptr cinfo) +{ + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + + dest->pub.next_output_byte = dest->buf; + dest->pub.free_in_buffer = dest->bufsize; + dest->jpegsize = 0; +} + +METHODDEF(boolean) empty_output_buffer(j_compress_ptr cinfo) +{ + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + + dest->pub.next_output_byte = dest->buf; + dest->pub.free_in_buffer = dest->bufsize; + + return FALSE; + ERREXIT(cinfo, JERR_BUFFER_SIZE); +} + +METHODDEF(void) term_destination(j_compress_ptr cinfo) +{ + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + dest->jpegsize = dest->bufsize - dest->pub.free_in_buffer; +} + +static GLOBAL(void) jpeg_mem_dest(j_compress_ptr cinfo, JOCTET* buf, size_t bufsize) +{ + mem_dest_ptr dest; + + if (cinfo->dest == NULL) { + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(mem_destination_mgr)); + } + + dest = (mem_dest_ptr) cinfo->dest; + + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + + dest->buf = buf; + dest->bufsize = bufsize; + dest->jpegsize = 0; +} + +static GLOBAL(int) jpeg_mem_size(j_compress_ptr cinfo) +{ + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + return dest->jpegsize; +} + + +/* put_jpeg_yuv420p_memory converts an input image in the YUV420P format into a jpeg image and puts + * it in a memory buffer. + * Inputs: + * - image_size is the size of the input image buffer. + * - input_image is the image in YUV420P format. + * - width and height are the dimentions of the image + * - quality is the jpeg encoding quality 0-100% + * Output: + * - dest_image is a pointer to the jpeg image buffer + * Returns buffer size of jpeg image + */ +static int put_jpeg_yuv420p_memory(unsigned char *dest_image, int image_size, + unsigned char *input_image, int width, int height, int quality) +{ + int i, j, jpeg_image_size; + + JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane) + JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5 + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + data[0] = y; + data[1] = cb; + data[2] = cr; + + cinfo.err = jpeg_std_error(&jerr); // errors get written to stderr + + jpeg_create_compress(&cinfo); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + jpeg_set_defaults (&cinfo); + + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + cinfo.raw_data_in = TRUE; // supply downsampled data + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_set_quality(&cinfo, quality, TRUE); + cinfo.dct_method = JDCT_FASTEST; + + jpeg_mem_dest(&cinfo, dest_image, image_size); // data written to mem + + jpeg_start_compress (&cinfo, TRUE); + + for (j=0; j>16; + g = g>>16; + b = b>>16; + if (r<0) + r=0; + else if (r>255) + r=255; + if (g<0) + g=0; + else if (g>255) + g=255; + if (b<0) + b=0; + else if (b>255) + b=255; + + rgb[0]=b; + rgb[1]=g; + rgb[2]=r; + + l++; + if (x&1) { + u++; + v++; + } + /* ppm is rgb not bgr */ + warningkiller=fwrite(rgb, 1, 3, picture); + } + if (y&1) { + u-=width/2; + v-=width/2; + } + } +} + +/* copy smartmask as an overlay into motion images and movies */ +void overlay_smartmask(struct context *cnt, unsigned char *out) +{ + int i, x, v, width, height, line; + struct images *imgs=&cnt->imgs; + unsigned char *smartmask=imgs->smartmask_final; + unsigned char *out_y, *out_u, *out_v; + + i=imgs->motionsize; + v=i+((imgs->motionsize)/4); + width=imgs->width; + height=imgs->height; + + /* set V to 255 to make smartmask appear red */ + out_v=out+v; + out_u=out+i; + for ( i=0; imotionsize; i++){ + if (smartmask[i]==0) + *out_y=0; + *out_y++; + } +} + +/* copy fixed mask as an overlay into motion images and movies */ +void overlay_fixed_mask(struct context *cnt, unsigned char *out) +{ + int i; + struct images *imgs=&cnt->imgs; + unsigned char *motion_img=imgs->out; + unsigned char *mask=imgs->mask; + int pixel; + + /* set y to mask + motion-pixel to keep motion pixels visible on grey background*/ + for (i=0; imotionsize; i++){ + pixel=255-mask[i]+motion_img[i]; + if (pixel>255) + *out=255; + else + *out=pixel; + out++; + } +} + +/* copy largest label as an overlay into motion images and movies */ +void overlay_largest_label(struct context *cnt, unsigned char *out) +{ + int i, x, v, width, height, line; + struct images *imgs=&cnt->imgs; + int *labels=imgs->labels; + unsigned char *out_y, *out_u, *out_v; + + i=imgs->motionsize; + v=i+((imgs->motionsize)/4); + width=imgs->width; + height=imgs->height; + + /* set U to 255 to make label appear blue */ + out_u=out+i; + out_v=out+v; + for ( i=0; imotionsize; i++) { + if (*labels++ & 32768) + *out_y=0; + out_y++; + } +} + +/* put_picture_mem is used for the webcam feature. Depending on the image type + * (colour YUV420P or greyscale) the corresponding put_jpeg_X_memory function is called. + * Inputs: + * - cnt is the global context struct and only cnt->imgs.type is used. + * - image_size is the size of the input image buffer + * - *image points to the image buffer that contains the YUV420P or Grayscale image about to be put + * - quality is the jpeg quality setting from the config file. + * Output: + * - **dest_image is a pointer to a pointer that points to the destination buffer in which the + * converted image it put + * Function returns the dest_image_size if successful. Otherwise 0. + */ +int put_picture_memory(struct context *cnt, unsigned char* dest_image, int image_size, + unsigned char *image, int quality) +{ + switch (cnt->imgs.type) { + case VIDEO_PALETTE_YUV420P: + return put_jpeg_yuv420p_memory(dest_image, image_size, image, + cnt->imgs.width, cnt->imgs.height, quality); + case VIDEO_PALETTE_GREY: + return put_jpeg_grey_memory(dest_image, image_size, image, + cnt->imgs.width, cnt->imgs.height, quality); + } + + return 0; +} + +void put_picture_fd(struct context *cnt, FILE *picture, unsigned char *image, int quality) +{ + if (cnt->conf.ppm) { + put_ppm_bgr24_file(picture, image, cnt->imgs.width, cnt->imgs.height); + } else { + switch (cnt->imgs.type) { + case VIDEO_PALETTE_YUV420P: + put_jpeg_yuv420p_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); + break; + case VIDEO_PALETTE_GREY: + put_jpeg_grey_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); + break; + } + } +} + + +void put_picture(struct context *cnt, char *file, unsigned char *image, int ftype) +{ + FILE *picture; + + picture = myfopen(file, "w"); + + if (!picture) { + /* Report to syslog - suggest solution if the problem is access rights to target dir */ + if (errno == EACCES) { + motion_log(LOG_ERR, 1, + "Can't write picture to file %s - check access rights to target directory", file); + motion_log(LOG_ERR, 1, "Thread is going to finish due to this fatal error"); + cnt->finish = 1; + return; + } else { + /* If target dir is temporarily unavailable we may survive */ + motion_log(LOG_ERR, 1, "Can't write picture to file %s", file); + return; + } + } + + put_picture_fd(cnt, picture, image, cnt->conf.quality); + fclose(picture); + event(cnt, EVENT_FILECREATE, NULL, file, (void *)(unsigned long)ftype, NULL); +} + +/* Get the pgm file used as fixed mask */ +unsigned char *get_pgm(FILE *picture, int width, int height) +{ + int x = 0 ,y = 0, maxval; + char line[256]; + unsigned char *image; + + line[255]=0; + + if (!fgets(line, 255, picture)) { + motion_log(LOG_ERR, 1, "Could not read from ppm file"); + return NULL; + } + + if (strncmp(line, "P5", 2)) { + motion_log(LOG_ERR, 1, "This is not a ppm file, starts with '%s'", line); + return NULL; + } + + /* skip comment */ + line[0] = '#'; + while (line[0] == '#') + if (!fgets(line, 255, picture)) + return NULL; + + + /* check size */ + if (sscanf(line, "%d %d", &x, &y)!=2) { + motion_log(LOG_ERR, 1, "Failed reading size in pgm file"); + return NULL; + } + + if (x!=width || y!=height) { + motion_log(LOG_ERR, 1, "Wrong image size %dx%d% should be %dx%d", x, y, width, height); + return NULL; + } + /* Maximum value */ + line[0] = '#'; + while (line[0] == '#') + if (!fgets(line, 255, picture)) + return NULL; + + if (sscanf(line, "%d", &maxval)!=1) { + motion_log(LOG_ERR, 1, "Failed reading maximum value in pgm file"); + return NULL; + } + + /* read data */ + + image=mymalloc(width*height); + + for (y=0; yimgs.out, 255, cnt->imgs.motionsize); /* initialize to unset */ + + /* Write pgm-header */ + fprintf(picture, "P5\n"); + fprintf(picture, "%d %d\n", cnt->conf.width, cnt->conf.height); + fprintf(picture, "%d\n", 255); + + /* write pgm image data at once */ + if ((int)fwrite(cnt->imgs.out, cnt->conf.width, cnt->conf.height, picture) != cnt->conf.height) { + motion_log(LOG_ERR, 1, "Failed writing default mask as pgm file"); + return; + } + + fclose(picture); + + motion_log(LOG_ERR, 0, "Creating empty mask %s",cnt->conf.mask_file); + motion_log(LOG_ERR, 0, "Please edit this file and re-run motion to enable mask feature"); +} + +/* save 'best' preview_shot */ +void preview_best(struct context *cnt) +{ +#ifdef HAVE_FFMPEG + int use_jpegpath; +#endif /* HAVE_FFMPEG */ + const char *jpegpath; + char previewname[PATH_MAX]; + char filename[PATH_MAX]; + + if(cnt->preview_max){ +#ifdef HAVE_FFMPEG + /* Use filename of movie i.o. jpeg_filename when set to 'preview' */ + use_jpegpath=strcmp(cnt->conf.jpegpath, "preview"); + + if (cnt->ffmpeg_new && !use_jpegpath){ + /* Replace avi/mpg with jpg/ppm and keep the rest of the filename */ + strncpy(previewname, cnt->newfilename, strlen(cnt->newfilename)-3); + strcat(previewname, imageext(cnt)); + put_picture(cnt, previewname, cnt->imgs.preview_buffer , FTYPE_IMAGE); + return; + } +#endif /* HAVE_FFMPEG */ + /* Save best preview-shot also when no movies are recorded or jpegpath + is used. Filename has to be generated - nothing available to reuse! */ + //printf("preview_shot: different filename or picture only!\n"); + + /* conf.jpegpath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.jpegpath) + jpegpath = cnt->conf.jpegpath; + else + jpegpath = (char *)DEF_JPEGPATH; + + mystrftime(cnt, filename, sizeof(filename), jpegpath, cnt->currenttime_tm, NULL, 0); + sprintf(previewname, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); + put_picture(cnt, previewname, cnt->imgs.preview_buffer , FTYPE_IMAGE); + } +} diff --git a/picture.h b/picture.h new file mode 100644 index 0000000..42f9178 --- /dev/null +++ b/picture.h @@ -0,0 +1,25 @@ +/* + * picture.h + * + * Copyright 2002 by Jeroen Vreeken (pe1rxq@amsat.org) + * Portions of this file are Copyright by Lionnel Maugis + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ +#ifndef _INCLUDE_PICTURE_H_ +#define _INCLUDE_PICTURE_H_ + +#include "motion.h" + +void overlay_smartmask(struct context *, unsigned char *); +void overlay_fixed_mask(struct context *, unsigned char *); +void put_fixed_mask(struct context *, const char *); +void overlay_largest_label(struct context *, unsigned char *); +void put_picture_fd(struct context *, FILE *, unsigned char *, int); +int put_picture_memory(struct context *, unsigned char*, int, unsigned char *, int); +void put_picture(struct context *, char *, unsigned char *, int ); +unsigned char *get_pgm(FILE *, int, int); +void preview_best(struct context *); + +#endif /* _INCLUDE_PICTURE_H_ */ diff --git a/pwc-ioctl.h b/pwc-ioctl.h new file mode 100644 index 0000000..65805ea --- /dev/null +++ b/pwc-ioctl.h @@ -0,0 +1,292 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transfering x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +#endif diff --git a/pwc-ioctl.h-10.0.5 b/pwc-ioctl.h-10.0.5 new file mode 100644 index 0000000..65805ea --- /dev/null +++ b/pwc-ioctl.h-10.0.5 @@ -0,0 +1,292 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transfering x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +#endif diff --git a/pwc-ioctl.h-pwc8.0 b/pwc-ioctl.h-pwc8.0 new file mode 100644 index 0000000..0161986 --- /dev/null +++ b/pwc-ioctl.h-pwc8.0 @@ -0,0 +1,229 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2003 Nemosoft Unv. webcam@smcc.demon.nl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.10 */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + + +struct pwc_probe +{ + char name[32]; + int type; +}; + + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ + +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute = 1, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ + int zoom; /* N/A, set to -1 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + The zoom is not used, maybe in the future... + + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; + int zoom_min, zoom_max; /* -1, -1 */ +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + +#endif diff --git a/pwc-ioctl.h-pwc9.0.1 b/pwc-ioctl.h-pwc9.0.1 new file mode 100644 index 0000000..2535a3c --- /dev/null +++ b/pwc-ioctl.h-pwc9.0.1 @@ -0,0 +1,279 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. webcam@smcc.demon.nl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transfering x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) + +#endif diff --git a/rotate.c b/rotate.c new file mode 100644 index 0000000..e5273a6 --- /dev/null +++ b/rotate.c @@ -0,0 +1,423 @@ +/* + * rotate.c + * + * Module for handling image rotation. + * + * Copyright 2004-2005, Per Jonsson (per@pjd.nu) + * + * This software is distributed under the GNU Public license + * Version 2. See also the file 'COPYING'. + * + * Image rotation is a feature of Motion that can be used when the + * camera is mounted upside-down or on the side. The module only + * supports rotation in multiples of 90 degrees. Using rotation + * increases the Motion CPU usage slightly. + * + * Version history: + * v6 (29-Aug-2005) - simplified the code as Motion now requires + * that width and height are multiples of 16 + * v5 (3-Aug-2005) - cleanup in code comments + * - better adherence to coding standard + * - fix for __bswap_32 macro collision + * - fixed bug where initialization would be + * incomplete for invalid degrees of rotation + * - now uses motion_log for error reporting + * v4 (26-Oct-2004) - new fix for width/height from imgs/conf due to + * earlier misinterpretation + * v3 (11-Oct-2004) - cleanup of width/height from imgs/conf + * v2 (26-Sep-2004) - separation of capture/internal dimensions + * - speed optimization, including bswap + * v1 (28-Aug-2004) - initial version + */ +#include "rotate.h" + +#ifndef __uint32 +/** + * We don't have a 32-bit unsigned integer type, so define it, given + * a 32-bit type was found by configure. + */ +# ifdef TYPE_32BIT +typedef unsigned TYPE_32BIT __uint32; +# else +# error "Failed to find a 32-bit integer type." +# endif +#endif + +/*============================================================================= + Start of code from bits/byteswap.h + =============================================================================*/ + +/** + * The code below is copied (with modification) from bits/byteswap.h. It provides + * a macro/function named rot__bswap_32 that swaps the bytes in a 32-bit integer, + * preferrably using the bswap assembler instruction if configure found support + * for it. + * + * It would be neater to simply include byteswap.h and use the bswap_32 macro + * defined there, but the problem is that the bswap asm instruction would then + * only be used for certain processor architectures, excluding athlon (and + * probably athlon64 as well). Moreover, byteswap.h doesn't seem to exist on + * FreeBSD. So, we rely on the HAVE_BSWAP macro defined by configure instead. + * + * Note that the macro names have been prefixed with "rot" in order to avoid + * collision since we have the include chain rotate.h -> motion.h -> netcam.h -> + * netinet/in.h -> ... -> byteswap.h -> bits/byteswap.h. + */ + +/* Swap bytes in 32 bit value. This is used as a fallback and for constants. */ +#define rot__bswap_constant_32(x) \ + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) + +#ifdef __GNUC__ +# if (__GNUC__ >= 2) && (i386 || __i386 || __i386__) +/* We're on an Intel-compatible platform, so we can use inline Intel assembler + * for the swapping. + */ +# ifndef HAVE_BSWAP +/* Bswap is not available, we have to use three instructions instead. */ +# define rot__bswap_32(x) \ + (__extension__ \ + ({ register __uint32 __v, __x = (x); \ + if (__builtin_constant_p (__x)) \ + __v = rot__bswap_constant_32 (__x); \ + else \ + __asm__ ("rorw $8, %w0;" \ + "rorl $16, %0;" \ + "rorw $8, %w0" \ + : "=r" (__v) \ + : "0" (__x) \ + : "cc"); \ + __v; })) +# else +# define rot__bswap_32(x) \ + (__extension__ \ + ({ register __uint32 __v, __x = (x); \ + if (__builtin_constant_p (__x)) \ + __v = rot__bswap_constant_32 (__x); \ + else \ + __asm__ ("bswap %0" : "=r" (__v) : "0" (__x)); \ + __v; })) +# endif +# else +/* Non-Intel platform or too old version of gcc. */ +# define rot__bswap_32(x) \ + (__extension__ \ + ({ register __uint32 __x = (x); \ + rot__bswap_constant_32 (__x); })) +# endif +#else +/* Not a GNU compiler. */ +static inline __uint32 rot__bswap_32(__uint32 __bsx) +{ + return __bswap_constant_32 (__bsx); +} +#endif + +/*============================================================================= + End of code from bits/byteswap.h + =============================================================================*/ + +/* Finally define a macro with a more appropriate name, to be used below. */ +#define swap_bytes(x) rot__bswap_32(x) + +/** + * reverse_inplace_quad + * + * Reverses a block of memory in-place, 4 bytes at a time. This function + * requires the __uint32 type, which is 32 bits wide. + * + * Parameters: + * + * src - the memory block to reverse + * size - the size (in bytes) of the memory block + * + * Returns: nothing + */ +static void reverse_inplace_quad(unsigned char *src, int size) +{ + __uint32 *nsrc = (__uint32 *)src; /* first quad */ + __uint32 *ndst = (__uint32 *)(src + size - 4); /* last quad */ + register __uint32 tmp; + + while (nsrc < ndst) { + tmp = swap_bytes(*ndst); + *ndst-- = swap_bytes(*nsrc); + *nsrc++ = tmp; + } +} + +/** + * rot90cw + * + * Performs a 90 degrees clockwise rotation of the memory block pointed to + * by src. The rotation is NOT performed in-place; dst must point to a + * receiving memory block the same size as src. + * + * Parameters: + * + * src - pointer to the memory block (image) to rotate clockwise + * dst - where to put the rotated memory block + * size - the size (in bytes) of the memory blocks (both src and dst) + * width - the width of the memory block when seen as an image + * height - the height of the memory block when seen as an image + * + * Returns: nothing + */ +static void rot90cw(unsigned char *src, register unsigned char *dst, int size, + int width, int height) +{ + unsigned char *endp; + register unsigned char *base; + int j; + + endp = src + size; + for (base = endp - width; base < endp; base++) { + src = base; + for (j = 0; j < height; j++, src -= width) { + *dst++ = *src; + } + } +} + +/** + * rot90ccw + * + * Performs a 90 degrees counterclockwise rotation of the memory block pointed + * to by src. The rotation is not performed in-place; dst must point to a + * receiving memory block the same size as src. + * + * Parameters: + * + * src - pointer to the memory block (image) to rotate counterclockwise + * dst - where to put the rotated memory block + * size - the size (in bytes) of the memory blocks (both src and dst) + * width - the width of the memory block when seen as an image + * height - the height of the memory block when seen as an image + * + * Returns: nothing + */ +static inline void rot90ccw(unsigned char *src, register unsigned char *dst, + int size, int width, int height) +{ + unsigned char *endp; + register unsigned char *base; + int j; + + endp = src + size; + dst = dst + size - 1; + for(base = endp - width; base < endp; base++) { + src = base; + for(j = 0; j < height; j++, src -= width) { + *dst-- = *src; + } + } +} + +/** + * rotate_init + * + * Initializes rotation data - allocates memory and determines which function + * to use for 180 degrees rotation. + * + * Parameters: + * + * cnt - the current thread's context structure + * + * Returns: nothing + */ +void rotate_init(struct context *cnt) +{ + int size; + + /* Make sure temp_buf isn't freed if it hasn't been allocated. */ + cnt->rotate_data.temp_buf = NULL; + + /* Assign the value in conf.rotate_deg to rotate_data.degrees. This way, + * we have a value that is safe from changes caused by motion-control. + */ + if((cnt->conf.rotate_deg % 90) > 0) { + motion_log(LOG_ERR, 0, "Config option \"rotate\" not a multiple of 90: %d", + cnt->conf.rotate_deg); + cnt->conf.rotate_deg = 0; /* disable rotation */ + cnt->rotate_data.degrees = 0; /* force return below */ + } else { + cnt->rotate_data.degrees = cnt->conf.rotate_deg % 360; /* range: 0..359 */ + } + + /* Upon entrance to this function, imgs.width and imgs.height contain the + * capture dimensions (as set in the configuration file, or read from a + * netcam source). + * + * If rotating 90 or 270 degrees, the capture dimensions and output dimensions + * are not the same. Capture dimensions will be contained in cap_width and + * cap_height in cnt->rotate_data, while output dimensions will be contained + * in imgs.width and imgs.height. + */ + + /* 1. Transfer capture dimensions into cap_width and cap_height. */ + cnt->rotate_data.cap_width = cnt->imgs.width; + cnt->rotate_data.cap_height = cnt->imgs.height; + + if((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) { + /* 2. "Swap" imgs.width and imgs.height. */ + cnt->imgs.width = cnt->rotate_data.cap_height; + cnt->imgs.height = cnt->rotate_data.cap_width; + } + + /* If we're not rotating, let's exit once we have setup the capture dimensions + * and output dimensions properly. + */ + if(cnt->rotate_data.degrees == 0) { + return; + } + + switch(cnt->imgs.type) + { + case VIDEO_PALETTE_YUV420P: + /* For YUV 4:2:0 planar, the memory block used for 90/270 degrees + * rotation needs to be width x height x 1.5 bytes large. + */ + size = cnt->imgs.width * cnt->imgs.height * 3 / 2; + break; + case VIDEO_PALETTE_GREY: + /* For greyscale, the memory block used for 90/270 degrees rotation + * needs to be width x height bytes large. + */ + size = cnt->imgs.width * cnt->imgs.height; + break; + default: + cnt->rotate_data.degrees = 0; + motion_log(LOG_ERR, 0, "Unsupported palette (%d), rotation is disabled", + cnt->imgs.type); + return; + } + + /* Allocate memory if rotating 90 or 270 degrees, because those rotations + * cannot be performed in-place (they can, but it would be too slow). + */ + if((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) { + cnt->rotate_data.temp_buf = mymalloc(size); + } +} + +/** + * rotate_deinit + * + * Frees resources previously allocated by rotate_init. + * + * Parameters: + * + * cnt - the current thread's context structure + * + * Returns: nothing + */ +void rotate_deinit(struct context *cnt) +{ + if(cnt->rotate_data.temp_buf) { + free(cnt->rotate_data.temp_buf); + } +} + +/** + * rotate_map + * + * Main entry point for rotation. This is the function that is called from + * video.c/video_freebsd.c to perform the rotation. + * + * Parameters: + * + * map - pointer to the image/data to rotate + * cnt - the current thread's context structure + * + * Returns: + * + * 0 - success + * -1 - failure (shouldn't happen) + */ +int rotate_map(struct context *cnt, unsigned char *map) +{ + /* The image format is either YUV 4:2:0 planar, in which case the pixel + * data is divided in three parts: + * Y - width x height bytes + * U - width x height / 4 bytes + * V - as U + * or, it is in greyscale, in which case the pixel data simply consists + * of width x height bytes. + */ + int wh, wh4 = 0, w2 = 0, h2 = 0; /* width*height, width*height/4 etc. */ + int size, deg; + int width, height; + + deg = cnt->rotate_data.degrees; + width = cnt->rotate_data.cap_width; + height = cnt->rotate_data.cap_height; + + /* Pre-calculate some stuff: + * wh - size of the Y plane, or the entire greyscale image + * size - size of the entire memory block + * wh4 - size of the U plane, and the V plane + * w2 - width of the U plane, and the V plane + * h2 - as w2, but height instead + */ + wh = width * height; + if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + size = wh * 3 / 2; + wh4 = wh / 4; + w2 = width / 2; + h2 = height / 2; + } + else { /* VIDEO_PALETTE_GREY */ + size = wh; + } + + switch (deg) { + case 90: + /* first do the Y part */ + rot90cw(map, cnt->rotate_data.temp_buf, wh, width, height); + if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + /* then do U and V */ + rot90cw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); + rot90cw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, + wh4, w2, h2); + } + + /* then copy back from the temp buffer to map */ + memcpy(map, cnt->rotate_data.temp_buf, size); + + break; + + case 180: + /* 180 degrees is easy - just reverse the data within + * Y, U and V. + */ + reverse_inplace_quad(map, wh); + if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + reverse_inplace_quad(map + wh, wh4); + reverse_inplace_quad(map + wh + wh4, wh4); + } + break; + + case 270: + + /* first do the Y part */ + rot90ccw(map, cnt->rotate_data.temp_buf, wh, width, height); + if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + /* then do U and V */ + rot90ccw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); + rot90ccw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, + wh4, w2, h2); + } + + /* then copy back from the temp buffer to map */ + memcpy(map, cnt->rotate_data.temp_buf, size); + break; + + default: + /* invalid */ + return -1; + } + + return 0; +} + diff --git a/rotate.h b/rotate.h new file mode 100644 index 0000000..6557cfe --- /dev/null +++ b/rotate.h @@ -0,0 +1,67 @@ +/* + * rotate.h + * + * Include file for handling image rotation. + * + * Copyright 2004-2005, Per Jonsson (per@pjd.nu) + * + * This software is distributed under the GNU Public license + * Version 2. See also the file 'COPYING'. + */ +#ifndef _INCLUDE_ROTATE_H +#define _INCLUDE_ROTATE_H + +#include "motion.h" /* for struct context */ + +/** + * rotate_init + * + * Sets up rotation data by allocating a temporary buffer for 90/270 degrees + * rotation, and by determining the right rotate-180-degrees function. + * + * Parameters: + * + * cnt - current thread's context structure + * + * Returns: nothing + */ +void rotate_init(struct context *cnt); + +/** + * rotate_deinit + * + * Frees memory allocated by rotate_init. + * + * Parameters: + * + * cnt - current thread's context structure + */ +void rotate_deinit(struct context *cnt); + +/** + * rotate_map + * + * Rotates the image stored in map according to the rotation data + * available in cnt. Rotation is performed clockwise. Supports 90, + * 180 and 270 degrees rotation. 180 degrees rotation is performed + * in-place by simply reversing the image data, which is a very + * fast operation. 90 and 270 degrees rotation are performed using + * a temporary buffer and a somewhat more complicated algorithm, + * which makes them slower. + * + * Note that to the caller, all rotations will seem as they are + * performed in-place. + * + * Parameters: + * + * map - the image map/data to rotate + * cnt - current thread's context structure + * + * Returns: + * + * 0 - success + * -1 - failure (rare, shouldn't happen) + */ +int rotate_map(struct context *cnt, unsigned char *map); + +#endif diff --git a/thread1.conf b/thread1.conf new file mode 100644 index 0000000..aa85588 --- /dev/null +++ b/thread1.conf @@ -0,0 +1,57 @@ +# /usr/local/etc/thread1.conf +# +# This config file was generated by motion 3.2.4 + + + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +videodevice /dev/video0 + +# The video input to be used (default: 8) +# Should normally be set to 1 for video/TV cards, and 8 for USB cameras +input 8 + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +text_left CAMERA 1 + + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute patch. (Default: current working directory) +target_dir /usr/local/apache2/htdocs/cam1 + + +############################################################ +# Live Webcam Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +webcam_port 8081 + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# The filename of the picture is appended as an argument for the command. +on_picture_save /usr/local/motion-extras/camparse1.pl + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# Filename of movie is appended as an argument for the command. +on_movie_end /usr/local/motion-extras/mpegparse1.pl diff --git a/thread2.conf b/thread2.conf new file mode 100644 index 0000000..a3ddc76 --- /dev/null +++ b/thread2.conf @@ -0,0 +1,58 @@ +# /usr/local/etc/thread2.conf +# +# This config file was generated by motion 3.2.4 + + + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +videodevice /dev/video1 + +# The video input to be used (default: 8) +# Should normally be set to 1 for video/TV cards, and 8 for USB cameras +input 1 + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +text_left CAMERA 2 + + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute patch. (Default: current working directory) +target_dir /usr/local/apache2/htdocs/cam2 + + +############################################################ +# Live Webcam Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +webcam_port 8082 + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# The filename of the picture is appended as an argument for the command. +on_picture_save /usr/local/motion-extras/camparse2.pl + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# Filename of movie is appended as an argument for the command. +on_movie_end /usr/local/motion-extras/mpegparse2.pl + diff --git a/thread3.conf b/thread3.conf new file mode 100644 index 0000000..ae0626f --- /dev/null +++ b/thread3.conf @@ -0,0 +1,60 @@ +# /usr/local/etc/thread3.conf +# +# This config file was generated by motion 3.2.4 + + + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +videodevice /dev/video2 + +# The video input to be used (default: 8) +# Should normally be set to 1 for video/TV cards, and 8 for USB cameras +input 8 + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +text_left CAMERA 3 + + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute patch. (Default: current working directory) +target_dir /usr/local/apache2/htdocs/cam3 + + +############################################################ +# Live Webcam Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +webcam_port 8083 + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# The filename of the picture is appended as an argument for the command. +on_picture_save /usr/local/motion-extras/camparse3.pl + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# Filename of movie is appended as an argument for the command. +on_movie_end /usr/local/motion-extras/mpegparse3.pl + + + diff --git a/thread4.conf b/thread4.conf new file mode 100644 index 0000000..da55192 --- /dev/null +++ b/thread4.conf @@ -0,0 +1,58 @@ +# /usr/local/etc/thread4.conf +# +# This config file was generated by motion 3.2.4 + + + +########################################################### +# Capture device options +############################################################ + +# URL to use if you are using a network camera, size will be autodetected (incl http://) +# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined +netcam_url http://192.168.1.6:8093/ + +# The video input to be used (default: 8) +# Should normally be set to 1 for video/TV cards, and 8 for USB cameras +input 1 + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +text_left CAMERA 4 + + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, jpeg_, mpeg_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute patch. (Default: current working directory) +target_dir /usr/local/apache2/htdocs/cam4 + + +############################################################ +# Live Webcam Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +webcam_port 8084 + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# The filename of the picture is appended as an argument for the command. +on_picture_save /usr/local/motion-extras/camparse4.pl + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# Filename of movie is appended as an argument for the command. +on_movie_end /usr/local/motion-extras/mpegparse4.pl + diff --git a/track.c b/track.c new file mode 100644 index 0000000..4dfaafb --- /dev/null +++ b/track.c @@ -0,0 +1,471 @@ +/* track.c + * + * Experimental motion tracking. + * + * Copyright 2000, Jeroen Vreeken + * This program is published under the GNU Public license + */ + +#include +#include +#include "motion.h" + +#ifdef __freebsd__ +#include "video_freebsd.h" +#else +#include "video.h" +#endif /* __freebsd__ */ + +#include "conf.h" +#include "track.h" +#include "alg.h" + +#include "pwc-ioctl.h" + + +struct trackoptions track_template = { + dev: -1, /* dev open */ + port: NULL, /* char *port */ + motorx: 0, /* int motorx */ + maxx: 0, /* int maxx; */ + speed: TRACK_SPEED, /* speed */ + stepsize: TRACK_STEPSIZE, /* stepsize */ + active: 0, /* auto tracking active */ + minmaxfound: 0, /* flag for minmax values stored for pwc based camera */ + step_angle_x: 10, /* step angle in degrees X-axis that camera moves during auto tracking */ + step_angle_y: 10, /* step angle in degrees Y-axis that camera moves during auto tracking */ + move_wait: 10 /* number of frames to disable motion detection after camera moving */ +}; + + +/* Add your own center and move functions here: */ +static int stepper_center(struct context *cnt, int xoff, int yoff ATTRIBUTE_UNUSED); +static int stepper_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs); +static int iomojo_center(struct context *cnt, int xoff, int yoff); +static int iomojo_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs); +static int lqos_center(struct context *cnt, int dev, int xoff, int yoff); +static int lqos_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, int manual); + +/* Add a call to your functions here: */ +int track_center(struct context *cnt, int dev, int manual, int xoff, int yoff) +{ + if (!manual && !cnt->track.active) + return 0; + if (cnt->track.type == TRACK_TYPE_STEPPER) + return stepper_center(cnt, xoff, yoff); + else if (cnt->track.type == TRACK_TYPE_PWC) + return lqos_center(cnt, dev, xoff, yoff); + else if (cnt->track.type == TRACK_TYPE_IOMOJO) + return iomojo_center(cnt, xoff, yoff); + else if (cnt->track.type == TRACK_TYPE_GENERIC) + return 10; // FIX ME. I chose to return something reasonable. + + motion_log(LOG_ERR, 1, "track_move: internal error, %d is not a known track-type", cnt->track.type); + + return 0; +} + +/* Add a call to your functions here: */ +int track_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, int manual) +{ + if (!manual && !cnt->track.active) + return 0; + if (cnt->track.type == TRACK_TYPE_STEPPER) + return stepper_move(cnt, dev, cent, imgs); + else if (cnt->track.type == TRACK_TYPE_PWC) + return lqos_move(cnt, dev, cent, imgs, manual); + else if (cnt->track.type == TRACK_TYPE_IOMOJO) + return iomojo_move(cnt, dev, cent, imgs); + else if (cnt->track.type == TRACK_TYPE_GENERIC) + return cnt->track.move_wait; // FIX ME. I chose to return something reasonable. + + motion_log(LOG_ERR, 1, "track_move: internal error, %d is not a known track-type", cnt->track.type); + + return 0; +} + + +/****************************************************************************** + + Stepper motor on serial port + +******************************************************************************/ + + +static int stepper_command(struct context *cnt, int motor, int command, int n) +{ + char buffer[3]; + time_t timeout=time(NULL); + + buffer[0]=motor; + buffer[1]=command; + buffer[2]=n; + if (write(cnt->track.dev, buffer, 3)!=3) + return -1; + + while (read(cnt->track.dev, buffer, 1)!=1 && time(NULL) < timeout+1); + if (time(NULL) >= timeout+2) { + motion_log(LOG_ERR, 1, "Status byte timeout!"); + return 0; + } + return buffer[0]; +} + + +static int stepper_status(struct context *cnt, int motor) +{ + return stepper_command(cnt, motor, STEPPER_COMMAND_STATUS, 0); +} + + +static int stepper_center(struct context *cnt, int x_offset, int y_offset ATTRIBUTE_UNUSED) +{ + struct termios adtio; + + if (cnt->track.dev<0) { + if ((cnt->track.dev=open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { + motion_log(LOG_ERR, 1, "Unable to open serial device %s", cnt->track.port); + exit(1); + } + + bzero (&adtio, sizeof(adtio)); + adtio.c_cflag= STEPPER_BAUDRATE | CS8 | CLOCAL | CREAD; + adtio.c_iflag= IGNPAR; + adtio.c_oflag= 0; + adtio.c_lflag= 0; /* non-canon, no echo */ + adtio.c_cc[VTIME]=0; /* timer unused */ + adtio.c_cc[VMIN]=0; /* blocking read until 1 char */ + tcflush (cnt->track.dev, TCIFLUSH); + + if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { + motion_log(LOG_ERR, 1, "Unable to initialize serial device %s", cnt->track.port); + exit(1); + } + } + + stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_SPEED, cnt->track.speed); + stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_LEFT_N, cnt->track.maxx); + + while (stepper_status(cnt, cnt->track.motorx) & STEPPER_STATUS_LEFT); + + stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_RIGHT_N, + cnt->track.maxx / 2 + x_offset * cnt->track.stepsize); + + while (stepper_status(cnt, cnt->track.motorx) & STEPPER_STATUS_RIGHT); + + return cnt->track.move_wait; +} + +static int stepper_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs) +{ + int command = 0; + int n = 0; + + if (dev < 0) + if (stepper_center(cnt, 0, 0) < 0) + return 0; + + if (cent->x < imgs->width / 2) { + command = STEPPER_COMMAND_LEFT_N; + n = imgs->width / 2 - cent->x; + } + + if (cent->x > imgs->width / 2) { + command = STEPPER_COMMAND_RIGHT_N; + n = cent->x - imgs->width / 2; + } + + n = n * cnt->track.stepsize / imgs->width; + + if (n) { + stepper_command(cnt, cnt->track.motorx, command, n); + return n / 5; + } + + return 0; +} + +/****************************************************************************** + + Iomojo Smilecam on serial port + +******************************************************************************/ + +static char iomojo_command(struct context *cnt, char *command, int len, int ret) +{ + char buffer[1]; + time_t timeout = time(NULL); + + if (write(cnt->track.dev, command, len) != len) + return 0; + + if (ret) { + while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout + 2); + + if (time(NULL) >= timeout + 2) { + motion_log(LOG_ERR, 1, "Return byte timeout!"); + return 0; + } + } + return buffer[0]; +} + +static void iomojo_setspeed(struct context *cnt, int speed) +{ + char command[3]; + + command[0] = IOMOJO_SETSPEED_CMD; + command[1] = cnt->track.iomojo_id; + command[2] = speed; + + if (iomojo_command(cnt, command, 3, 1)!=IOMOJO_SETSPEED_RET) + motion_log(LOG_ERR, 1, "Unable to set camera speed"); +} + +static void iomojo_movehome(struct context *cnt) +{ + char command[2]; + + command[0] = IOMOJO_MOVEHOME; + command[1] = cnt->track.iomojo_id; + + iomojo_command(cnt, command, 2, 0); +} + +static int iomojo_center(struct context *cnt, int x_offset, int y_offset) +{ + struct termios adtio; + char command[5], direction=0; + + if (cnt->track.dev<0) { + if ((cnt->track.dev=open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { + motion_log(LOG_ERR, 1, "Unable to open serial device %s", cnt->track.port); + return 0; + } + bzero (&adtio, sizeof(adtio)); + adtio.c_cflag = IOMOJO_BAUDRATE | CS8 | CLOCAL | CREAD; + adtio.c_iflag = IGNPAR; + adtio.c_oflag = 0; + adtio.c_lflag = 0; /* non-canon, no echo */ + adtio.c_cc[VTIME] = 0; /* timer unused */ + adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ + tcflush(cnt->track.dev, TCIFLUSH); + if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { + motion_log(LOG_ERR, 1, "Unable to initialize serial device %s", cnt->track.port); + return 0; + } + } + + iomojo_setspeed(cnt, 40); + iomojo_movehome(cnt); + + if (x_offset || y_offset) { + if (x_offset > 0) + direction |= IOMOJO_DIRECTION_RIGHT; + else { + direction |= IOMOJO_DIRECTION_LEFT; + x_offset *= -1; + } + + if (y_offset > 0) + direction |= IOMOJO_DIRECTION_UP; + else { + direction |= IOMOJO_DIRECTION_DOWN; + y_offset *= -1; + } + + if (x_offset > 180) + x_offset = 180; + + if (y_offset > 60) + y_offset = 60; + + command[0] = IOMOJO_MOVEOFFSET_CMD; + command[1] = cnt->track.iomojo_id; + command[2] = direction; + command[3] = x_offset; + command[4] = y_offset; + iomojo_command(cnt, command, 5, 0); + } + + return cnt->track.move_wait; +} + +static int iomojo_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs) +{ + char command[5]; + int direction = 0; + int nx = 0, ny = 0; + int i; + + if (dev < 0) + if (iomojo_center(cnt, 0, 0) < 0) + return 0; + + if (cent->x < imgs->width / 2) { + direction |= IOMOJO_DIRECTION_LEFT; + nx = imgs->width / 2 - cent->x; + } + + if (cent->x > imgs->width / 2) { + direction |= IOMOJO_DIRECTION_RIGHT; + nx = cent->x - imgs->width / 2; + } + + if (cent->y < imgs->height / 2) { + direction |= IOMOJO_DIRECTION_DOWN; + ny = imgs->height / 2 - cent->y; + } + + if (cent->y > imgs->height / 2) { + direction |= IOMOJO_DIRECTION_UP; + ny = cent->y - imgs->height / 2; + } + + nx = nx * 72 / imgs->width; + ny = ny * 72 / imgs->height; + + if (nx || ny) { + if (nx > 180) + nx = 180; + + if (ny > 60) + ny = 60; + + command[0] = IOMOJO_MOVEOFFSET_CMD; + command[1] = cnt->track.iomojo_id; + command[2] = direction; + command[3] = nx; + command[4] = ny; + iomojo_command(cnt, command, 5, 0); + + /* Number of frames to skip while moving */ + if (ny >= nx) + i = 25 * ny / 90; + else + i = 25 * nx / 90; + return i; + } + + return 0; +} + +/****************************************************************************** + + Logitech QuickCam Orbit camera tracking code by folkert@vanheusden.com + +******************************************************************************/ + +static int lqos_center(struct context *cnt, int dev, int x_angle, int y_angle) +{ + int reset = 3; + struct pwc_mpt_angles pma; + struct pwc_mpt_range pmr; + + if (cnt->track.dev==-1) { + + if (ioctl(dev, VIDIOCPWCMPTRESET, &reset) == -1) { + motion_log(LOG_ERR, 1, "Failed to reset camera to starting position! Reason"); + return 0; + } + + SLEEP(6,0) + + if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { + motion_log(LOG_ERR, 1, "failed VIDIOCPWCMPTGRANGE"); + return 0; + } + + cnt->track.dev = dev; + cnt->track.minmaxfound = 1; + cnt->track.panmin = pmr.pan_min; + cnt->track.panmax = pmr.pan_max; + cnt->track.tiltmin = pmr.tilt_min; + cnt->track.tiltmax = pmr.tilt_max; + } + + if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) + motion_log(LOG_ERR, 1, "ioctl VIDIOCPWCMPTGANGLE"); + + pma.absolute = 1; + + if (x_angle * 100 < cnt->track.panmax && x_angle * 100 > cnt->track.panmin) + pma.pan = x_angle * 100; + + if (y_angle * 100 < cnt->track.tiltmax && y_angle * 100 > cnt->track.tiltmin) + pma.tilt = y_angle * 100; + + if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { + motion_log(LOG_ERR, 1, "Failed to pan/tilt camera! Reason"); + return 0; + } + + return cnt->track.move_wait; +} + +static int lqos_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, int manual) +{ + int delta_x = cent->x - (imgs->width / 2); + int delta_y = cent->y - (imgs->height / 2); + int move_x_degrees, move_y_degrees; + struct pwc_mpt_angles pma; + struct pwc_mpt_range pmr; + + /* If we are on auto track we calculate delta, otherwise we use user input in degrees times 100 */ + if (!manual) { + if (delta_x > imgs->width * 3/8 && delta_x < imgs->width * 5/8) + return 0; + if (delta_y > imgs->height * 3/8 && delta_y < imgs->height * 5/8) + return 0; + + move_x_degrees = delta_x * cnt->track.step_angle_x * 100 / (imgs->width / 2); + move_y_degrees = -delta_y * cnt->track.step_angle_y * 100 / (imgs->height / 2); + } else { + move_x_degrees = cent->x * 100; + move_y_degrees = cent->y * 100; + } + + /* If we never checked for the min/max values for pan/tilt we do it now */ + if (cnt->track.minmaxfound == 0) { + if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { + motion_log(LOG_ERR, 1, "failed VIDIOCPWCMPTGRANGE"); + return 0; + } + cnt->track.minmaxfound = 1; + cnt->track.panmin = pmr.pan_min; + cnt->track.panmax = pmr.pan_max; + cnt->track.tiltmin = pmr.tilt_min; + cnt->track.tiltmax = pmr.tilt_max; + } + + /* Get current camera position */ + if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) + motion_log(LOG_ERR, 1, "ioctl VIDIOCPWCMPTGANGLE"); + + + /* Check current position of camera and see if we need to adjust + values down to what is left to move */ + if (move_x_degrees<0 && (cnt->track.panmin - pma.pan) > move_x_degrees) + move_x_degrees = (cnt->track.panmin - pma.pan); + + if (move_x_degrees>0 && (cnt->track.panmax - pma.pan) < move_x_degrees) + move_x_degrees = (cnt->track.panmax - pma.pan); + + if (move_y_degrees<0 && (cnt->track.tiltmin - pma.tilt) > move_y_degrees) + move_y_degrees = (cnt->track.tiltmin - pma.tilt); + + if (move_y_degrees>0 && (cnt->track.tiltmax - pma.tilt) < move_y_degrees) + move_y_degrees = (cnt->track.tiltmax - pma.tilt); + + /* Move camera relative to current position */ + pma.absolute = 0; + pma.pan = move_x_degrees; + pma.tilt = move_y_degrees; + + if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { + motion_log(LOG_ERR, 1, "Failed to pan/tilt camera! Reason"); + return 0; + } + + return cnt->track.move_wait; +} diff --git a/track.h b/track.h new file mode 100644 index 0000000..86205f8 --- /dev/null +++ b/track.h @@ -0,0 +1,101 @@ +/* track.h + * + * Experimental motion tracking. + * + * Copyright 2000, Jeroen Vreeken + * This program is published under the GNU Public license + */ + +#ifndef _INCLUDE_TRACK_H +#define _INCLUDE_TRACK_H + +#include "alg.h" + +struct trackoptions { + int dev; + /* Config options: */ + int type; + char *port; + int motorx; + int maxx; + int stepsize; + int speed; + int iomojo_id; + int active; + int panmin; + int panmax; + int tiltmin; + int tiltmax; + int minmaxfound; + int step_angle_x; + int step_angle_y; + int move_wait; +}; + +extern struct trackoptions track_template; + +int track_center(struct context *, int, int, int, int); +int track_move(struct context *, int, struct coord *, struct images *, int); + +/* + Some default values: + */ +#define TRACK_SPEED 255 +#define TRACK_STEPSIZE 40 + +#define TRACK_TYPE_STEPPER 1 +#define TRACK_TYPE_IOMOJO 2 +#define TRACK_TYPE_PWC 3 +#define TRACK_TYPE_GENERIC 4 + +/* + Some defines for the Serial stepper motor: + */ + +#define STEPPER_BAUDRATE B9600 + +#define STEPPER_STATUS_LEFT 1 +#define STEPPER_STATUS_RIGHT 2 +#define STEPPER_STATUS_SAFETYL 4 +#define STEPPER_STATUS_SAFETYR 8 + +#define STEPPER_COMMAND_STATUS 0 +#define STEPPER_COMMAND_LEFT_N 1 +#define STEPPER_COMMAND_RIGHT_N 2 +#define STEPPER_COMMAND_LEFT 3 +#define STEPPER_COMMAND_RIGHT 4 +#define STEPPER_COMMAND_SWEEP 5 +#define STEPPER_COMMAND_STOP 6 +#define STEPPER_COMMAND_SPEED 7 + + +/* + Some defines for the Iomojo Smilecam: + */ + +#define IOMOJO_BAUDRATE B19200 + +#define IOMOJO_CHECKPOWER_CMD 0xff +#define IOMOJO_CHECKPOWER_RET 'Q' +#define IOMOJO_MOVEOFFSET_CMD 0xfe +#define IOMOJO_SETSPEED_CMD 0xfd +#define IOMOJO_SETSPEED_RET 'P' +#define IOMOJO_MOVEHOME 0xf9 +#define IOMOJO_RESTART 0xf7 + +#define IOMOJO_DIRECTION_RIGHT 0x01 +#define IOMOJO_DIRECTION_LEFT 0x02 +#define IOMOJO_DIRECTION_DOWN 0x04 +#define IOMOJO_DIRECTION_UP 0x08 + + +/* + Defines for the Logitech QuickCam Orbit/Sphere USB webcam +*/ + +#define LQOS_VERTICAL_DEGREES 180 +#define LQOS_HORIZONAL_DEGREES 120 + + + +#endif /* _INCLUDE_TRACK_H */ diff --git a/video.c b/video.c new file mode 100644 index 0000000..a35adb4 --- /dev/null +++ b/video.c @@ -0,0 +1,971 @@ +/* video.c + * + * Video stream functions for motion. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +/* Common stuff: */ +#include "motion.h" +#include "video.h" +#include "conf.h" +/* for rotation */ +#include "rotate.h" + +#ifndef WITHOUT_V4L + +/* for the v4l stuff: */ +#include "pwc-ioctl.h" +#include +//#include "sys/ioctl.h" +#include +#include +#include + +#define MAX2(x, y) ((x) > (y) ? (x) : (y)) +#define MIN2(x, y) ((x) < (y) ? (x) : (y)) + +static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) +{ + int dev = viddev->fd; + unsigned char *image = cnt->imgs.image_ring_buffer; + struct video_picture vid_pic; + int make_change = 0; + + if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) { + + if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGPICT)"); + + make_change = 1; + vid_pic.contrast = cnt->conf.contrast * 256; + viddev->contrast = cnt->conf.contrast; + } + + if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) { + + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGPICT)"); + } + + make_change = 1; + vid_pic.colour = cnt->conf.saturation * 256; + viddev->saturation = cnt->conf.saturation; + } + + if (cnt->conf.hue && cnt->conf.hue != viddev->hue) { + + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGPICT)"); + } + + make_change = 1; + vid_pic.hue = cnt->conf.hue * 256; + viddev->hue = cnt->conf.hue; + } + + if (cnt->conf.autobright) { + + int brightness_window_high; + int brightness_window_low; + int brightness_target; + int i, j = 0, avg = 0, step = 0; + + if (cnt->conf.brightness) + brightness_target = cnt->conf.brightness; + else + brightness_target = 128; + + brightness_window_high = MIN2(brightness_target + 10, 255); + brightness_window_low = MAX2(brightness_target - 10, 1); + + for (i = 0; i < cnt->imgs.motionsize; i += 101) { + avg += image[i]; + j++; + } + avg = avg / j; + + if (avg > brightness_window_high || avg < brightness_window_low) { + /* If we already read the VIDIOGPICT - we should not do it again */ + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGPICT)"); + } + /* average is above window - turn down brigtness - go for the target */ + if (avg > brightness_window_high) { + step = MIN2((avg - brightness_target)/5+1, viddev->brightness); + if (viddev->brightness > step+1) { + viddev->brightness -= step; + vid_pic.brightness = viddev->brightness * 256; + make_change = 1; + } + } + /* average is below window - turn up brigtness - go for the target */ + if (avg < brightness_window_low) { + step = MIN2((brightness_target - avg)/5+1, 255 - viddev->brightness); + if (viddev->brightness < 255-step ) { + viddev->brightness += step; + vid_pic.brightness = viddev->brightness * 256; + make_change = 1; + } + } + } + } else { + if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGPICT)"); + } + + make_change = 1; + vid_pic.brightness = cnt->conf.brightness * 256; + viddev->brightness = cnt->conf.brightness; + } + } + + if (make_change) { + if (ioctl(dev, VIDIOCSPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSPICT)"); + } +} + + +static void yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height) +{ + unsigned char *src, *dest, *src2, *dest2; + int i, j; + + /* Create the Y plane */ + src=cap_map; + dest=map; + for (i=width*height; i; i--) { + *dest++=*src; + src+=2; + } + /* Create U and V planes */ + src=cap_map+1; + src2=cap_map+width*2+1; + dest=map+width*height; + dest2=dest+(width*height)/4; + for (i=height/2; i; i--) { + for (j=width/2; j; j--) { + *dest=((int)*src+(int)*src2)/2; + src+=2; + src2+=2; + dest++; + *dest2=((int)*src+(int)*src2)/2; + src+=2; + src2+=2; + dest2++; + } + src+=width*2; + src2+=width*2; + } +} + +static void rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height) +{ + unsigned char *y, *u, *v; + unsigned char *r, *g, *b; + int i, loop; + + b=cap_map; + g=b+1; + r=g+1; + y=map; + u=y+width*height; + v=u+(width*height)/4; + memset(u, 0, width*height/4); + memset(v, 0, width*height/4); + + for(loop=0; loop>15; + *u+=((-4784**r-9437**g+14221**b)>>17)+32; + *v+=((20218**r-16941**g-3277**b)>>17)+32; + r+=3; + g+=3; + b+=3; + *y++=(9796**r+19235**g+3736**b)>>15; + *u+=((-4784**r-9437**g+14221**b)>>17)+32; + *v+=((20218**r-16941**g-3277**b)>>17)+32; + r+=3; + g+=3; + b+=3; + u++; + v++; + } + + if ((loop & 1) == 0) + { + u-=width/2; + v-=width/2; + } + } +} + +/******************************************************************************************* + Video4linux capture routines +*/ + + +static unsigned char *v4l_start(struct context *cnt, struct video_dev *viddev, int width, int height, + int input, int norm, unsigned long freq, int tuner_number) +{ + int dev = viddev->fd; + struct video_capability vid_caps; + struct video_channel vid_chnl; + struct video_tuner vid_tuner; + struct video_mbuf vid_buf; + struct video_mmap vid_mmap; + void *map; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGCAP)"); + return (NULL); + } + + if (vid_caps.type & VID_TYPE_MONOCHROME) + viddev->v4l_fmt=VIDEO_PALETTE_GREY; + + if (input != IN_DEFAULT) { + memset(&vid_chnl, 0, sizeof(struct video_channel)); + vid_chnl.channel = input; + + if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGCHAN)"); + } else { + vid_chnl.channel = input; + vid_chnl.norm = norm; + if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSCHAN)"); + return (NULL); + } + } + } + + if (freq) { + vid_tuner.tuner = tuner_number; + if (ioctl (dev, VIDIOCGTUNER, &vid_tuner)==-1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGTUNER)"); + } else { + if (vid_tuner.flags & VIDEO_TUNER_LOW) { + freq=freq*16; /* steps of 1/16 KHz */ + } else { + freq=(freq*10)/625; + } + if (ioctl(dev, VIDIOCSFREQ, &freq)==-1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSFREQ)"); + return (NULL); + } + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Frequency set"); + } + } + + if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { + motion_log(LOG_ERR, 0, "ioctl(VIDIOCGMBUF) - Error device does not support memory map"); + motion_log(LOG_ERR, 0, "V4L capturing using read is deprecated!"); + motion_log(LOG_ERR, 0, "Motion only supports mmap."); + return NULL; + } else { + map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + viddev->size_map=vid_buf.size; + if (vid_buf.frames>1) { + viddev->v4l_maxbuffer=2; + viddev->v4l_buffers[0]=map; + viddev->v4l_buffers[1]=(unsigned char *)map + vid_buf.offsets[1]; + } else { + viddev->v4l_buffers[0]=map; + viddev->v4l_maxbuffer=1; + } + + if (MAP_FAILED == map) { + return (NULL); + } + viddev->v4l_curbuffer=0; + vid_mmap.format=viddev->v4l_fmt; + vid_mmap.frame=viddev->v4l_curbuffer; + vid_mmap.width=width; + vid_mmap.height=height; + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "Failed with YUV420P, trying YUV422 palette"); + viddev->v4l_fmt=VIDEO_PALETTE_YUV422; + vid_mmap.format=viddev->v4l_fmt; + /* Try again... */ + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "Failed with YUV422, trying RGB24 palette"); + viddev->v4l_fmt=VIDEO_PALETTE_RGB24; + vid_mmap.format=viddev->v4l_fmt; + /* Try again... */ + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "Failed with RGB24, trying GREYSCALE palette"); + viddev->v4l_fmt=VIDEO_PALETTE_GREY; + vid_mmap.format=viddev->v4l_fmt; + /* Try one last time... */ + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_ERR, 1, "Failed with all supported palettes " + "- giving up"); + return (NULL); + } + } + } + } + } + + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_YUV420P: + viddev->v4l_bufsize=(width*height*3)/2; + break; + case VIDEO_PALETTE_YUV422: + viddev->v4l_bufsize=(width*height*2); + break; + case VIDEO_PALETTE_RGB24: + viddev->v4l_bufsize=(width*height*3); + break; + case VIDEO_PALETTE_GREY: + viddev->v4l_bufsize=width*height; + break; + } + return map; +} + + +/** + * v4l_next + * v4l_next fetches a video frame from a v4l device + * + * Parameters: + * cnt Pointer to the context for this thread + * viddev Pointer to struct containing video device handle amd device parameters + * map Pointer to the buffer in which the function puts the new image + * width Width of image in pixels + * height Height of image in pixels + * + * Returns + * 0 Success + * V4L_FATAL_ERROR Fatal error + * Positive with bit 0 set and bit 1 unset + * Non fatal error (not implemented) + */ +static int v4l_next(struct video_dev *viddev, unsigned char *map, int width, int height) +{ + int dev = viddev->fd; + int frame = viddev->v4l_curbuffer; + struct video_mmap vid_mmap; + unsigned char *cap_map; + + sigset_t set, old; + + /* MMAP method is used */ + vid_mmap.format = viddev->v4l_fmt; + vid_mmap.width = width; + vid_mmap.height = height; + + /* Block signals during IOCTL */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + pthread_sigmask (SIG_BLOCK, &set, &old); + + cap_map = viddev->v4l_buffers[viddev->v4l_curbuffer]; + viddev->v4l_curbuffer++; + + if (viddev->v4l_curbuffer >= viddev->v4l_maxbuffer) + viddev->v4l_curbuffer = 0; + + vid_mmap.frame = viddev->v4l_curbuffer; + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_ERR, 1, "mcapture error in proc %d", getpid()); + sigprocmask (SIG_UNBLOCK, &old, NULL); + return V4L_FATAL_ERROR; + } + + vid_mmap.frame=frame; + + if (ioctl(dev, VIDIOCSYNC, &vid_mmap.frame) == -1) { + motion_log(LOG_ERR, 1, "sync error in proc %d", getpid()); + sigprocmask (SIG_UNBLOCK, &old, NULL); + } + + pthread_sigmask (SIG_UNBLOCK, &old, NULL); /*undo the signal blocking*/ + + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_RGB24: + rgb24toyuv420p(map, cap_map, width, height); + break; + case VIDEO_PALETTE_YUV422: + yuv422to420p(map, cap_map, width, height); + break; + default: + memcpy(map, cap_map, viddev->v4l_bufsize); + } + + + return 0; +} + +static void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, int input, + int norm, int skip, unsigned long freq, int tuner_number) +{ + int dev=viddev->fd; + int i; + struct video_channel vid_chnl; + struct video_tuner vid_tuner; + unsigned long frequnits = freq; + + if (input != viddev->input || width != viddev->width || height!=viddev->height || + freq!=viddev->freq || tuner_number!=viddev->tuner_number) { + if (freq) { + vid_tuner.tuner = tuner_number; + if (ioctl (dev, VIDIOCGTUNER, &vid_tuner)==-1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGTUNER)"); + } else { + if (vid_tuner.flags & VIDEO_TUNER_LOW) { + frequnits=freq*16; /* steps of 1/16 KHz */ + } else { + frequnits=(freq*10)/625; + } + if (ioctl(dev, VIDIOCSFREQ, &frequnits)==-1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSFREQ)"); + return; + } + } + } + + vid_chnl.channel = input; + + if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGCHAN)"); + } else { + vid_chnl.channel = input; + vid_chnl.norm = norm; + if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSCHAN)"); + return; + } + } + v4l_picture_controls(cnt, viddev); + viddev->input=input; + viddev->width=width; + viddev->height=height; + viddev->freq=freq; + viddev->tuner_number=tuner_number; + /* skip a few frames if needed */ + for (i=0; i1) { + 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]=='-') { + sprintf(pipepath, "/dev/%s", input); + pipe_fd=open(pipepath, O_RDWR); + if (pipe_fd>=0) { + motion_log(-1, 0, "\tInput: /dev/%s", input); + motion_log(-1, 0, "\tOutput: /dev/%s", 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, "Failed to open '%s'", prefix); + return -1; + } + while ((dirp=readdir(dir)) != NULL) { + if (!strncmp(dirp->d_name, "video", 5)) { + strcpy(buffer, prefix); + strcat(buffer, dirp->d_name); + strcat(buffer, "/name"); + 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/"); + strcat(buffer, dirp->d_name); + if ((tfd=open(buffer, O_RDWR))>=0) { + strcpy(pipepath, buffer); + if (pipe_fd>=0) { + close(pipe_fd); + } + pipe_fd = tfd; + low = tnum; + } + } + close(fd); + } + } + } + closedir(dir); + if (pipe_fd >= 0) + motion_log(-1, 0, "Opened input of %s", pipepath); + } + return pipe_fd; +} + +static int v4l_startpipe(char *devname, int width, int height, int type) +{ + int dev; + struct video_picture vid_pic; + struct video_window vid_win; + + if (!strcmp(devname, "-")) { + dev=v4l_open_vidpipe(); + } else { + dev=open(devname, O_RDWR); + } + if (dev < 0) + return(-1); + + if (ioctl(dev, VIDIOCGPICT, &vid_pic)== -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGPICT)"); + return(-1); + } + vid_pic.palette=type; + if (ioctl(dev, VIDIOCSPICT, &vid_pic)== -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSPICT)"); + return(-1); + } + if (ioctl(dev, VIDIOCGWIN, &vid_win)== -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGWIN)"); + return(-1); + } + vid_win.height=height; + vid_win.width=width; + if (ioctl(dev, VIDIOCSWIN, &vid_win)== -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSWIN)"); + return(-1); + } + return dev; +} + +static int v4l_putpipe (int dev, unsigned char *image, int size) +{ + return write(dev, image, size); +} +#endif /*WITHOUT_V4L*/ + +/***************************************************************************** + Wrappers calling the actual capture routines + *****************************************************************************/ + +/* big lock for vid_start to ensure exclusive access to viddevs while adding + * devices during initialization of each thread + */ +pthread_mutex_t vid_mutex; + +#ifndef WITHOUT_V4L + +/* Here we setup the viddevs structure which is used globally in the vid_* + * functions. + */ +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 + */ +void vid_init(void) +{ + if (!viddevs) { + viddevs = mymalloc(sizeof(struct video_dev *)); + viddevs[0] = NULL; + } + + pthread_mutex_init(&vid_mutex, NULL); +} + +/** + * vid_close + * + * 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. + */ +void vid_close(void) +{ + int i=-1; + + if (viddevs) { + while(viddevs[++i]) { + close(viddevs[i]->fd); + } + } +} + +/** + * vid_cleanup + * + * vid_cleanup is called from motion.c when Motion is stopped or restarted + * It free all the memory held by the viddevs structs. + */ +void vid_cleanup(void) +{ + int i=-1; + if (viddevs) { + while(viddevs[++i]) { + munmap(viddevs[i]->v4l_buffers[0],viddevs[i]->size_map); + free(viddevs[i]); + } + free(viddevs); + viddevs=NULL; + } +} +#endif /*WITHOUT_V4L*/ + +/** + * vid_start + * + * vid_start setup the capture device. This will be either a V4L device or a netcam. + * The function does the following: + * - If the camera is a netcam - netcam_start is called and function returns + * - Width and height are checked for valid value (multiple of 16) + * - Copy the config height and width to the imgs struct. Note that height and width are + * only copied to the from the conf struct to the imgs struct during program startup + * The width and height can no leter be changed via http remote control as this would + * require major re-memory allocations of all image buffers. + * - Setup basic V4L properties incl palette incl setting + * - Open the device + * - Returns the device number. + * + * Parameters: + * cnt Pointer to the context for this thread + * + * "Global" variable + * viddevs The viddevs struct is "global" within the context of video.c + * and used in functions vid_*. + * vid_mutex Mutex needed to handle exclusive access to the viddevs struct when + * each thread adds a new video device durig startup calling vid_start + * + * Returns + * device number + * -1 if failed to open device. + */ +int vid_start(struct context *cnt) +{ + struct config *conf = &cnt->conf; + int dev = -1; + + if (conf->netcam_url) { + return netcam_start(cnt); + } + +#ifndef WITHOUT_V4L + /* Start a new block so we can make declarations without breaking good old + * gcc 2.95 or older. + */ + { + int i = -1; + 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. + */ + 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; + } + + 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); + + /* 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_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; + } + } + + 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; + + pthread_mutexattr_init(&viddevs[i]->attr); + pthread_mutex_init(&viddevs[i]->mutex, NULL); + + dev = open(conf->video_device, O_RDWR); + + if (dev <0) { + motion_log(LOG_ERR, 1, "Failed to open video device %s", conf->video_device); + return -1; + } + + viddevs[i]->video_device = conf->video_device; + 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; + + /* 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; + + if (!v4l_start(cnt, viddevs[i], width, height, input, norm, frequency, tuner_number)) { + pthread_mutex_unlock(&vid_mutex); + return -1; + } + + cnt->imgs.type = viddevs[i]->v4l_fmt; + + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.size = width*height; + cnt->imgs.motionsize = width*height; + break; + 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); + } +#endif /*WITHOUT_V4L*/ + + return dev; +} + + +/** + * vid_next + * + * vid_next fetches a video frame from a either v4l device or netcam + * + * Parameters: + * cnt Pointer to the context for this thread + * map Pointer to the buffer in which the function puts the new image + * + * Global variable + * viddevs The viddevs struct is "global" within the context of video.c + * and used in functions vid_*. + * Returns + * 0 Success + * -1 Fatal V4L error + * -2 Fatal Netcam error + * Positive numbers... + * with bit 0 set Non fatal V4L error (not implemented) + * with bit 1 set Non fatal Netcam error + */ +int vid_next(struct context *cnt, unsigned char *map) +{ + 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; + } +#ifndef WITHOUT_V4L + + /* We start a new block so we can make declarations without breaking + * gcc 2.95 or older + */ + { + int i = -1; + 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) + break; + + if (!viddevs[i]) + 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; + cnt->switched = 1; + } + + v4l_set_input(cnt, viddevs[i], map, width, height, conf->input, conf->norm, + conf->roundrobin_skip, conf->frequency, conf->tuner_number); + + ret = v4l_next(viddevs[i], map, width, height); + + if (--viddevs[i]->frames <= 0) { + viddevs[i]->owner = -1; + pthread_mutex_unlock(&viddevs[i]->mutex); + } + + if(cnt->rotate_data.degrees > 0) { + /* rotate the image as specified */ + rotate_map(cnt, map); + } + } +#endif /*WITHOUT_V4L*/ + return ret; +} + +#ifndef WITHOUT_V4L +int vid_startpipe(const char *devname, int width, int height, int type) +{ + return v4l_startpipe( (char *)devname, width, height, type); +} + +int vid_putpipe (int dev, unsigned char *image, int size) +{ + return v4l_putpipe(dev, image, size); +} +#endif /*WITHOUT_V4L*/ diff --git a/video.h b/video.h new file mode 100644 index 0000000..be65f78 --- /dev/null +++ b/video.h @@ -0,0 +1,79 @@ +/* video.h + * + * Include file for video.c + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#ifndef _INCLUDE_VIDEO_H +#define _INCLUDE_VIDEO_H + +#define _LINUX_TIME_H 1 +#ifndef WITHOUT_V4L +#include +#endif + +/* video4linux stuff */ +#define NORM_DEFAULT 0 +#define NORM_PAL 0 +#define NORM_NTSC 1 +#define NORM_SECAM 2 +#define NORM_PAL_NC 3 +#define IN_DEFAULT 8 +#define IN_TV 0 +#define IN_COMPOSITE 1 +#define IN_COMPOSITE2 2 +#define IN_SVIDEO 3 + +/* video4linux error codes */ +#define V4L_GENERAL_ERROR 0x01 /* binary 000001 */ +#define V4L_BTTVLOST_ERROR 0x05 /* binary 000101 */ +#define V4L_FATAL_ERROR -1 + + +#define VIDEO_DEVICE "/dev/video0" + +struct video_dev { + int fd; + const char *video_device; + int input; + int width; + int height; + int brightness; + int contrast; + int saturation; + int hue; + unsigned long freq; + int tuner_number; + + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + int owner; + int frames; + + /* Device type specific stuff: */ +#ifndef WITHOUT_V4L + /* v4l */ + int size_map; + int v4l_fmt; + unsigned char *v4l_buffers[2]; + int v4l_curbuffer; + int v4l_maxbuffer; + int v4l_bufsize; +#endif +}; + +/* video functions, video.c */ +int vid_start(struct context *); +int vid_next(struct context *, unsigned char *map); +#ifndef WITHOUT_V4L +void vid_init(void); +int vid_startpipe(const char *devname, int width, int height, int); +int vid_putpipe(int dev, unsigned char *image, int); +void vid_close(void); +void vid_cleanup(void); +#endif + +#endif /* _INCLUDE_VIDEO_H */ diff --git a/video_freebsd.c b/video_freebsd.c new file mode 100644 index 0000000..c4505b1 --- /dev/null +++ b/video_freebsd.c @@ -0,0 +1,1106 @@ +/* video_freebsd.c + * + * BSD Video stream functions for motion. + * Copyright 2004 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +/* Common stuff: */ +#include "motion.h" +#include "video_freebsd.h" +#include "conf.h" +/* for rotation */ +#include "rotate.h" + +#ifndef WITHOUT_V4L + +/* for the v4l stuff: */ +#include +#include + +/* Hack from xawtv 4.x */ + +#define VIDEO_NONE 0 +#define VIDEO_RGB08 1 /* bt848 dithered */ +#define VIDEO_GRAY 2 +#define VIDEO_RGB15_LE 3 /* 15 bpp little endian */ +#define VIDEO_RGB16_LE 4 /* 16 bpp little endian */ +#define VIDEO_RGB15_BE 5 /* 15 bpp big endian */ +#define VIDEO_RGB16_BE 6 /* 16 bpp big endian */ +#define VIDEO_BGR24 7 /* bgrbgrbgrbgr (LE) */ +#define VIDEO_BGR32 8 /* bgr-bgr-bgr- (LE) */ +#define VIDEO_RGB24 9 /* rgbrgbrgbrgb (BE) */ +#define VIDEO_RGB32 10 /* -rgb-rgb-rgb (BE) */ +#define VIDEO_LUT2 11 /* lookup-table 2 byte depth */ +#define VIDEO_LUT4 12 /* lookup-table 4 byte depth */ +#define VIDEO_YUYV 13 /* 4:2:2 */ +#define VIDEO_YUV422P 14 /* YUV 4:2:2 (planar) */ +#define VIDEO_YUV420P 15 /* YUV 4:2:0 (planar) */ +#define VIDEO_MJPEG 16 /* MJPEG (AVI) */ +#define VIDEO_JPEG 17 /* JPEG (JFIF) */ +#define VIDEO_UYVY 18 /* 4:2:2 */ +#define VIDEO_MPEG 19 /* MPEG1/2 */ +#define VIDEO_FMT_COUNT 20 + +#define MAX(x,y) ( (x) > (y) ? (x) : (y) ) +#define MIN(x,y) ( (x) < (y) ? (x) : (y) ) +#define array_elem(x) (sizeof(x) / sizeof( (x)[0] )) + +static const struct camparam_st { + int min, max, range, drv_min, drv_range, def; +} CamParams[] = { + { + BT848_BRIGHTMIN, BT848_BRIGHTMIN + BT848_BRIGHTRANGE, + BT848_BRIGHTRANGE, BT848_BRIGHTREGMIN, + BT848_BRIGHTREGMAX - BT848_BRIGHTREGMIN + 1, BT848_BRIGHTCENTER, }, + { + BT848_CONTRASTMIN, (BT848_CONTRASTMIN + BT848_CONTRASTRANGE), + BT848_CONTRASTRANGE, BT848_CONTRASTREGMIN, + (BT848_CONTRASTREGMAX - BT848_CONTRASTREGMIN + 1), + BT848_CONTRASTCENTER, }, + { + BT848_CHROMAMIN, (BT848_CHROMAMIN + BT848_CHROMARANGE), BT848_CHROMARANGE, + BT848_CHROMAREGMIN, (BT848_CHROMAREGMAX - BT848_CHROMAREGMIN + 1 ), + BT848_CHROMACENTER, }, +}; + +#define BRIGHT 0 +#define CONTR 1 +#define CHROMA 2 + +/* Not tested yet */ + +static void yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height) +{ + unsigned char *src, *dest, *src2, *dest2; + int i, j; + + /* Create the Y plane */ + src=cap_map; + dest=map; + for (i=width*height; i; i--) { + *dest++=*src; + src+=2; + } + /* Create U and V planes */ + src=cap_map+1; + src2=cap_map+width*2+1; + dest=map+width*height; + dest2=dest+(width*height)/4; + for (i=height/2; i; i--) { + for (j=width/2; j; j--) { + *dest=((int)*src+(int)*src2)/2; + src+=2; + src2+=2; + dest++; + *dest2=((int)*src+(int)*src2)/2; + src+=2; + src2+=2; + dest2++; + } + src+=width*2; + src2+=width*2; + } + +} + +/* FIXME seems no work with METEOR_GEO_RGB24 , check BPP as well ? */ + +static void rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height) +{ + unsigned char *y, *u, *v; + unsigned char *r, *g, *b; + int i, loop; + + b=cap_map; + g=b+1; + r=g+1; + y=map; + u=y+width*height; + v=u+(width*height)/4; + memset(u, 0, width*height/4); + memset(v, 0, width*height/4); + + for(loop=0; loop>15; + *u+=((-4784**r-9437**g+14221**b)>>17)+32; + *v+=((20218**r-16941**g-3277**b)>>17)+32; + r+=3; + g+=3; + b+=3; + *y++=(9796**r+19235**g+3736**b)>>15; + *u+=((-4784**r-9437**g+14221**b)>>17)+32; + *v+=((20218**r-16941**g-3277**b)>>17)+32; + r+=3; + g+=3; + b+=3; + u++; + v++; + } + + if ((loop & 1) == 1) { + u-=width/2; + v-=width/2; + } + } + +} + + +/********************************************************************************************* + CAPTURE CARD STUFF +**********************************************************************************************/ +/* NOT TESTED YET FIXME */ + +/* +static int camparam_normalize( int param, int cfg_value, int *ioctl_val ) +{ + int val; + + cfg_value = MIN( CamParams[ param ].max, MAX( CamParams[ param ].min, cfg_value ) ); + val = (cfg_value - CamParams[ param ].min ) / + (CamParams[ param ].range + 0.01) * CamParams[param].drv_range + CamParams[param].drv_min; + val = MAX( CamParams[ param ].min, + MIN( CamParams[ param ].drv_min + CamParams[ param ].drv_range-1,val )); + *ioctl_val = val; + return cfg_value; +} +*/ + +static int set_hue( int viddev, int new_hue ) +{ + signed char ioctlval=new_hue; + + if( ioctl( viddev, METEORSHUE, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORSHUE Error setting hue [%d]",new_hue); + return -1; + } + + motion_log(-1, 0, "set hue to [%d]",ioctlval); + + return ioctlval; +} + +static int get_hue( int viddev , int *hue) +{ + signed char ioctlval; + + if( ioctl( viddev, METEORGHUE, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORGHUE Error getting hue"); + return -1; + } + + *hue = ioctlval; + return ioctlval; +} + +static int set_saturation( int viddev, int new_saturation ) +{ + unsigned char ioctlval= new_saturation; + + if( ioctl( viddev, METEORSCSAT, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORSCSAT Error setting saturation [%d]",new_saturation); + return -1; + } + + motion_log(-1, 0, "set saturation to [%d]",ioctlval); + + return ioctlval; +} + +static int get_saturation( int viddev , int *saturation) +{ + unsigned char ioctlval; + + if( ioctl( viddev, METEORGCSAT, &ioctlval ) < 0 ) { + + motion_log(LOG_ERR, 1, "METEORGCSAT Error getting saturation"); + return -1; + } + + *saturation = ioctlval; + return ioctlval; +} + +static int set_contrast( int viddev, int new_contrast ) +{ + unsigned char ioctlval = new_contrast; + + if( ioctl( viddev, METEORSCONT, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORSCONT Error setting contrast [%d]", new_contrast); + return 0; + } + + motion_log(-1, 0, "set contrast to [%d]",ioctlval); + + return ioctlval; +} + +static int get_contrast( int viddev, int *contrast ) +{ + unsigned char ioctlval; + + if( ioctl (viddev, METEORGCONT, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORGCONT Error getting contrast"); + return -1; + } + + *contrast = ioctlval; + return ioctlval; +} + + +static int set_brightness( int viddev, int new_bright ) +{ + unsigned char ioctlval = new_bright; + + if( ioctl( viddev, METEORSBRIG, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORSBRIG brightness [%d]",new_bright); + return -1; + } + + motion_log(-1, 0, "set brightness to [%d]",ioctlval); + + return ioctlval; +} + + +static int get_brightness( int viddev, int *brightness ) +{ + unsigned char ioctlval; + + if( ioctl( viddev, METEORGBRIG, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORGBRIG getting brightness"); + return -1; + } + + *brightness = ioctlval; + return ioctlval; +} + +// Set channel needed ? FIXME +/* +static int set_channel( struct video_dev *viddev, int new_channel ) +{ + int ioctlval; + + ioctlval = new_channel; + if( ioctl( viddev->fd_tuner, TVTUNER_SETCHNL, &ioctlval ) < 0 ) { + motion_log(LOG_ERR, 1, "Error channel %d",ioctlval); + return -1; + } else { + motion_log(LOG_DEBUG, 0, "channel set to %d",ioctlval); + } + + viddev->channel = new_channel; + + return 0; +} +*/ + +/* set frequency to tuner */ + +static int set_freq(struct video_dev *viddev, unsigned long freq) +{ + int tuner_fd = viddev->fd_tuner; + int old_audio; + + /* HACK maybe not need it , but seems that is needed to mute before changing frequency */ + + if ( ioctl( tuner_fd, BT848_GAUDIO, &old_audio ) < 0 ) { + motion_log(LOG_ERR, 1, "BT848_GAUDIO"); + return -1; + } + + if (ioctl(tuner_fd, TVTUNER_SETFREQ, &freq) < 0){ + motion_log(LOG_ERR, 1, "Tuning (TVTUNER_SETFREQ) failed , freq [%lu]",freq); + return -1; + } + + old_audio &= AUDIO_MUTE; + if ( old_audio ){ + old_audio = AUDIO_MUTE; + if ( ioctl(tuner_fd , BT848_SAUDIO, &old_audio ) < 0 ) { + motion_log(LOG_ERR, 1, "BT848_SAUDIO %i",old_audio); + return -1; + } + } + + return 0; +} + +/* + set the input to capture images , could be tuner (METEOR_INPUT_DEV1) + or any of others input : + RCA/COMPOSITE1 (METEOR_INPUT_DEV0) + COMPOSITE2/S-VIDEO (METEOR_INPUT_DEV2) + S-VIDEO (METEOR_INPUT_DEV3) + VBI ?! (METEOR_INPUT_DEV_SVIDEO) +*/ + +static int set_input(struct video_dev *viddev, unsigned short input) +{ + int actport; + int portdata[] = { METEOR_INPUT_DEV0, METEOR_INPUT_DEV1, + METEOR_INPUT_DEV2, METEOR_INPUT_DEV3, + METEOR_INPUT_DEV_SVIDEO }; + + if( input >= array_elem( portdata ) ) { + motion_log(LOG_WARNING, 0, "Channel Port %d out of range (0-4)",input); + input = IN_DEFAULT; + } + + actport = portdata[ input ]; + if( ioctl( viddev->fd_bktr, METEORSINPUT, &actport ) < 0 ) { + if( input != IN_DEFAULT ) { + motion_log(LOG_WARNING, 0, + "METEORSINPUT %d invalid - Trying default %d ", input, IN_DEFAULT ); + input = IN_DEFAULT; + actport = portdata[ input ]; + if( ioctl( viddev->fd_bktr, METEORSINPUT, &actport ) < 0 ) { + motion_log(LOG_ERR, 1, "METEORSINPUT %d init", input); + return -1; + } + } else { + motion_log(LOG_ERR, 1, "METEORSINPUT %d init",input); + return -1; + } + } + + return 0; +} + +static int set_geometry(struct video_dev *viddev, int width, int height) +{ + struct meteor_geomet geom; + + geom.columns = width; + geom.rows = height; + +// geom.rows = geom.rows / 2; +// geom.oformat |= METEOR_GEO_ODD_ONLY; + + + geom.oformat = METEOR_GEO_YUV_422 | METEOR_GEO_YUV_12; +// geom.oformat |= METEOR_GEO_EVEN_ONLY; + +// geom.oformat = 0; + geom.frames = 1; + + if( ioctl( viddev->fd_bktr, METEORSETGEO, &geom ) < 0 ) { + motion_log(LOG_ERR, 1, "Couldn't set the geometry"); + return -1; + } + + return 0; +} + +/* + set input format ( PAL, NTSC, SECAM, etc ... ) +*/ + +static int set_input_format(struct video_dev *viddev, unsigned short newformat) +{ + int input_format[] = { NORM_PAL_NEW, NORM_NTSC_NEW, NORM_SECAM_NEW, NORM_DEFAULT_NEW}; + int format; + + if( newformat >= array_elem( input_format ) ) { + motion_log(LOG_WARNING, 0, "Input format %d out of range (0-2)",newformat ); + format = NORM_DEFAULT_NEW; + newformat = 3; + } else + format = input_format[newformat]; + + if( ioctl( viddev->fd_bktr, BT848SFMT, &format ) < 0 ) { + motion_log(LOG_ERR, 1, "BT848SFMT, Couldn't set the input format , try again with default"); + format = NORM_DEFAULT_NEW; + newformat = 3; + + if( ioctl( viddev->fd_bktr, BT848SFMT, &format ) < 0 ) { + motion_log(LOG_ERR, 1, "BT848SFMT, Couldn't set the input format either default"); + return -1; + } + } + + return 0; +} + +/* +statict int setup_pixelformat( int bktr ) +{ + int i; + struct meteor_pixfmt p; + int format=-1; + + for( i=0; ; i++ ){ + p.index = i; + if( ioctl( bktr, METEORGSUPPIXFMT, &p ) < 0 ){ + if( errno == EINVAL ) + break; + motion_log(LOG_ERR, 1, "METEORGSUPPIXFMT getting pixformat %d",i); + return -1; + } + + + // Hack from xawtv 4.x + + switch ( p.type ){ + case METEOR_PIXTYPE_RGB: + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB"); + switch(p.masks[0]) { + case 31744: // 15 bpp + format = p.swap_bytes ? VIDEO_RGB15_LE : VIDEO_RGB15_BE; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB15"); + break; + case 63488: // 16 bpp + format = p.swap_bytes ? VIDEO_RGB16_LE : VIDEO_RGB16_BE; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB16"); + break; + case 16711680: // 24/32 bpp + if (p.Bpp == 3 && p.swap_bytes == 1) { + format = VIDEO_BGR24; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_BGR24"); + } else if (p.Bpp == 4 && p.swap_bytes == 1 && p.swap_shorts == 1) { + format = VIDEO_BGR32; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_BGR32"); + } else if (p.Bpp == 4 && p.swap_bytes == 0 && p.swap_shorts == 0) { + format = VIDEO_RGB32; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB32"); + } + } + break; + case METEOR_PIXTYPE_YUV: + format = VIDEO_YUV422P; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV"); + break; + case METEOR_PIXTYPE_YUV_12: + format = VIDEO_YUV422P; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV_12"); + break; + case METEOR_PIXTYPE_YUV_PACKED: + format = VIDEO_YUV422P; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV_PACKED"); + break; + + } + + if( p.type == METEOR_PIXTYPE_RGB && p.Bpp == 3 ){ + // Found a good pixeltype -- set it up + if( ioctl( bktr, METEORSACTPIXFMT, &i ) < 0 ){ + motion_log(LOG_WARNING, 1, "METEORSACTPIXFMT etting pixformat METEOR_PIXTYPE_RGB Bpp == 3"); + // Not immediately fatal + } + motion_log(LOG_DEBUG, 0, "input format METEOR_PIXTYPE_RGB %i",i); + format=i; + } + + if( p.type == METEOR_PIXTYPE_YUV_PACKED ){ + // Found a good pixeltype -- set it up + if( ioctl( bktr, METEORSACTPIXFMT, &i ) < 0 ){ + motion_log(LOG_WARNING, 1, "METEORSACTPIXFMT setting pixformat METEOR_PIXTYPE_YUV_PACKED"); + // Not immediately fatal + } + motion_log(LOG_DEBUG, 0, "input format METEOR_PIXTYPE_YUV_PACKED %i",i); + format=i; + } + + } + + return format; +} + +*/ + +static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) +{ + int dev=viddev->fd_bktr; + + if ( (cnt->conf.contrast) && (cnt->conf.contrast != viddev->contrast) ){ + set_contrast(dev,cnt->conf.contrast); + viddev->contrast = cnt->conf.contrast; + } + + if ( (cnt->conf.hue) && (cnt->conf.hue != viddev->hue) ) { + set_hue(dev, cnt->conf.hue); + viddev->hue = cnt->conf.hue; + } + + if ( (cnt->conf.brightness) && + (cnt->conf.brightness != viddev->brightness)) { + set_brightness(dev, cnt->conf.brightness); + viddev->brightness = cnt->conf.brightness; + } + + if ( (cnt->conf.saturation ) && + (cnt->conf.saturation != viddev->saturation) ){ + set_saturation(dev, cnt->conf.saturation); + viddev->saturation = cnt->conf.saturation; + } +} + + +/******************************************************************************************* + Video capture routines + + - set input + - setup_pixelformat + - set_geometry + + - set_brightness + - set_chroma + - set_contrast + - set_channelset + - set_channel + - set_capture_mode + +*/ +static unsigned char *v4l_start(struct context *cnt, struct video_dev *viddev, int width, int height, unsigned short input, unsigned short norm, unsigned long freq) +{ + int dev_bktr=viddev->fd_bktr; + //int dev_tunner=viddev->fd_tuner; + /* to ensure that all device will be support the capture mode + _TODO_ : Autodected the best capture mode . + */ + int dummy = 1; +// int pixelformat = BSD_VIDFMT_I420; + int single = METEOR_CAP_SINGLE; + + void *map; + + /* if we have choose the tuner is needed to setup the frequency */ + if ( ((viddev->tuner_device != NULL)) && ( input == IN_TV ) ) { + if (!freq) { + motion_log(LOG_ERR, 1, "Not valid Frequency [%lu] for Source input [%i]", freq, input); + return (NULL); + }else if (set_freq(viddev, freq) == -1) { + motion_log(LOG_ERR, 1, "Frequency [%lu] Source input [%i]", freq, input); + return (NULL); + } + } + + /* FIXME if we set as input tuner , we need to set option for tuner not for bktr */ + + if ( set_input_format(viddev, norm) == -1 ) { + motion_log(LOG_ERR, 1, "set input format [%d]",norm); + return (NULL); + } + + if (set_geometry(viddev, width, height) == -1) { + motion_log(LOG_ERR, 1, "set geometry [%d]x[%d]",width, height); + return (NULL); + } +/* + if (ioctl(dev_bktr, METEORSACTPIXFMT, &pixelformat) < 0 ){ + motion_log(LOG_ERR, 1, "set enconding method BSD_VIDFMT_I420"); + return(NULL); + } + + + NEEDED !? FIXME + + if ( setup_pixelformat(viddev) == -1) { + return (NULL); + } +*/ + + if (freq) { + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Frequency set (no implemented yet"); + /* + TODO missing implementation + set_channelset(viddev); + set_channel(viddev); + if (set_freq (viddev, freq) == -1) { + return (NULL); + } + */ + } + + + /* set capture mode and capture buffers */ + + /* That is the buffer size for capture images , + so is dependent of color space of input format / FIXME */ + + viddev->v4l_bufsize = (((width*height*3/2)) * sizeof(unsigned char *)); + viddev->v4l_fmt = VIDEO_PALETTE_YUV420P; + + + map = mmap((caddr_t)0,viddev->v4l_bufsize,PROT_READ|PROT_WRITE,MAP_SHARED, dev_bktr, (off_t)0); + + if (map == MAP_FAILED){ + motion_log(LOG_ERR, 1, "mmap failed"); + return (NULL); + } + + /* FIXME double buffer */ + if (0) { + viddev->v4l_maxbuffer=2; + viddev->v4l_buffers[0]=map; + viddev->v4l_buffers[1]=(unsigned char *)map+0; /* 0 is not valid just a test */ + //viddev->v4l_buffers[1]=map+vid_buf.offsets[1]; + } else { + viddev->v4l_buffers[0]=map; + viddev->v4l_maxbuffer=1; + } + + viddev->v4l_curbuffer=0; + + /* Clear the buffer */ + + if (ioctl(dev_bktr, BT848SCBUF, &dummy) < 0) { + motion_log(LOG_ERR, 1, "BT848SCBUF"); + return NULL; + } + + // settle , sleep(1) replaced + SLEEP(1,0) + + if (ioctl(dev_bktr, METEORCAPTUR, &single) < 0){ + motion_log(LOG_ERR, 1, "METEORCAPTUR using single method Error capturing"); + }else viddev->capture_method = CAPTURE_SINGLE; + + /* FIXME*/ + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_YUV420P: + viddev->v4l_bufsize=(width*height*3)/2; + motion_log(-1, 0, "VIDEO_PALETTE_YUV420P palette setting bufsize"); + break; + case VIDEO_PALETTE_YUV422: + viddev->v4l_bufsize=(width*height*2); + break; + case VIDEO_PALETTE_RGB24: + viddev->v4l_bufsize=(width*height*3); + break; + case VIDEO_PALETTE_GREY: + viddev->v4l_bufsize=width*height; + break; + } + + { + int val; + motion_log(LOG_INFO,0,"HUE [%d]",get_hue(dev_bktr,&val)); + motion_log(LOG_INFO,0,"SATURATION [%d]",get_saturation(dev_bktr,&val)); + motion_log(LOG_INFO,0,"BRIGHTNESS [%d]",get_brightness(dev_bktr,&val)); + motion_log(LOG_INFO,0,"CONTRAST [%d]",get_contrast(dev_bktr,&val)); + } + return map; +} + + +/** + * v4l_next fetches a video frame from a v4l device + * Parameters: + * viddev Pointer to struct containing video device handle + * map Pointer to the buffer in which the function puts the new image + * width Width of image in pixels + * height Height of image in pixels + * + * Returns + * 0 Success + * -1 Fatal error + * 1 Non fatal error (not implemented) + */ +static int v4l_next(struct video_dev *viddev,unsigned char *map, int width, int height) +{ + int dev_bktr=viddev->fd_bktr; + unsigned char *cap_map=NULL; + int single = METEOR_CAP_SINGLE; + int continous = METEOR_CAP_CONTINOUS; + sigset_t set, old; + + + /* ONLY MMAP method is used to Capture */ + + /* Allocate a new mmap buffer */ + /* Block signals during IOCTL */ + sigemptyset (&set); + sigaddset (&set, SIGCHLD); + sigaddset (&set, SIGALRM); + sigaddset (&set, SIGUSR1); + sigaddset (&set, SIGTERM); + sigaddset (&set, SIGHUP); + pthread_sigmask (SIG_BLOCK, &set, &old); + cap_map=viddev->v4l_buffers[viddev->v4l_curbuffer]; + + viddev->v4l_curbuffer++; + if (viddev->v4l_curbuffer >= viddev->v4l_maxbuffer) + viddev->v4l_curbuffer=0; + + /* capture */ + + if (viddev->capture_method == CAPTURE_CONTINOUS){ + if (ioctl(dev_bktr, METEORCAPTUR, &continous) < 0) { + motion_log(LOG_ERR, 1, "Error capturing using continous method"); + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (-1); + } + }else{ + + if (ioctl(dev_bktr, METEORCAPTUR, &single) < 0) { + motion_log(LOG_ERR, 1, "Error capturing using single method"); + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (-1); + } + } + + /*undo the signal blocking*/ + pthread_sigmask (SIG_UNBLOCK, &old, NULL); + + + switch (viddev->v4l_fmt){ + case VIDEO_PALETTE_RGB24: + rgb24toyuv420p(map, cap_map, width, height); + break; + case VIDEO_PALETTE_YUV422: + yuv422to420p(map, cap_map, width, height); + break; + default: + memcpy(map, cap_map, viddev->v4l_bufsize); + } + + + return 0; +} + + +/* set input & freq if needed FIXME not allowed use Tuner yet */ + +static void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, + unsigned short input, unsigned short norm, int skip, unsigned long freq) +{ + int i; + unsigned long frequnits = freq; + + if (input != viddev->input || width != viddev->width || height!=viddev->height || freq!=viddev->freq){ + motion_log(LOG_WARNING, 0, "v4l_set_input really needed ?"); + + if (set_input(viddev, input) == -1) + return; + + if (set_input_format(viddev, norm) == -1 ) + return; + + if ((viddev->tuner_device != NULL) && ( input == IN_TV ) && (frequnits > 0)) { + if (set_freq (viddev, freq) == -1) + return; + } + + // FIXME + /* + if ( setup_pixelformat(viddev) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSFREQ)"); + return + } + */ + + if (set_geometry(viddev, width, height) == -1) + return; + + v4l_picture_controls(cnt, viddev); + + viddev->input=input; + viddev->width=width; + viddev->height=height; + viddev->freq=freq; + viddev->norm=norm; + + /* skip a few frames if needed */ + for (i=0; ifd_bktr); + close(viddevs[i]->fd_tuner); + } + } +} + +void vid_cleanup(void) +{ + int i=-1; + if (viddevs){ + while(viddevs[++i]){ + munmap(viddevs[i]->v4l_buffers[0],viddevs[i]->v4l_bufsize); + viddevs[i]->v4l_buffers[0] = MAP_FAILED; + free(viddevs[i]); + } + free(viddevs); + viddevs=NULL; + } +} + +#endif /*WITHOUT_V4L*/ + + +int vid_start(struct context *cnt) +{ + struct config *conf=&cnt->conf; + int fd_bktr=-1; + + if (conf->netcam_url) + return netcam_start(cnt); + +#ifndef WITHOUT_V4L + { + int fd_tuner=-1; + int i=-1; + int width, height; + unsigned short input, norm; + 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 are multiples of 16 so we check for this + */ + 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; + } + width = conf->width; + height = conf->height; + input = conf->input; + norm = conf->norm; + frequency = conf->frequency; + + 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; + + /* 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; + motion_log(-1, 0, "vid_start cnt->imgs.type [%i]", cnt->imgs.type); + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.motionsize=width*height; + cnt->imgs.size=width*height; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type=VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + motion_log(-1, 0, + " VIDEO_PALETTE_YUV420P setting imgs.size and imgs.motionsize"); + cnt->imgs.motionsize=width*height; + cnt->imgs.size=(width*height*3)/2; + break; + } + fd=viddevs[i]->fd_bktr; // FIXME return fd_tuner ?! + pthread_mutex_unlock(&vid_mutex); + return fd; + } + } + + viddevs=myrealloc(viddevs, sizeof(struct video_dev *)*(i+2), "vid_start"); + viddevs[i]=mymalloc(sizeof(struct video_dev)); + viddevs[i+1]=NULL; + + pthread_mutexattr_init(&viddevs[i]->attr); + pthread_mutex_init(&viddevs[i]->mutex, NULL); + + fd_bktr=open(conf->video_device, O_RDWR); + if (fd_bktr <0) { + motion_log(LOG_ERR, 1, "open video device %s",conf->video_device); + motion_log(LOG_ERR, 0, "Motion Exits."); + return -1; + } + + /* Only open tuner if conf->tuner_device has set , freq and input is 1 */ + if ( (conf->tuner_device != NULL) && (frequency > 0) && ( input == IN_TV )) { + fd_tuner=open(conf->tuner_device, O_RDWR); + if (fd_tuner <0) { + motion_log(LOG_ERR, 1, "open tuner device %s",conf->tuner_device); + motion_log(LOG_ERR, 0, "Motion Exits."); + return -1; + } + } + + viddevs[i]->video_device=conf->video_device; + viddevs[i]->tuner_device=conf->tuner_device; + viddevs[i]->fd_bktr=fd_bktr; + viddevs[i]->fd_tuner=fd_tuner; + viddevs[i]->input=input; + viddevs[i]->height=height; + viddevs[i]->width=width; + viddevs[i]->freq=frequency; + viddevs[i]->owner=-1; + + /* 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; + + /* default palette */ + viddevs[i]->v4l_fmt=VIDEO_PALETTE_YUV420P; + viddevs[i]->v4l_curbuffer=0; + viddevs[i]->v4l_maxbuffer=1; + + if (!v4l_start (cnt, viddevs[i], width, height, input, norm, frequency)){ + pthread_mutex_unlock(&vid_mutex); + return -1; + } + + cnt->imgs.type=viddevs[i]->v4l_fmt; + + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.size=width*height; + cnt->imgs.motionsize=width*height; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type=VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + motion_log(-1, 0, "VIDEO_PALETTE_YUV420P imgs.type"); + cnt->imgs.size=(width*height*3)/2; + cnt->imgs.motionsize=width*height; + break; + } + + pthread_mutex_unlock(&vid_mutex); + } +#endif /*WITHOUT_V4L*/ + + /* FIXME needed tuner device ?! */ + return fd_bktr; +} + + +/** + * vid_next fetches a video frame from a either v4l device or netcam + * Parameters: + * cnt Pointer to the context for this thread + * map Pointer to the buffer in which the function puts the new image + * + * Returns + * 0 Success + * -1 Fatal V4L error + * -2 Fatal Netcam error + * 1 Non fatal V4L error (not implemented) + * 2 Non fatal Netcam error + */ +int vid_next(struct context *cnt, unsigned char *map) +{ + 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; + } + +#ifndef WITHOUT_V4L + + int i=-1; + int width, height; + int dev_bktr = 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_bktr==dev_bktr) + break; + + if (!viddevs[i]) + return -1; + + if (viddevs[i]->owner!=cnt->threadnr) { + pthread_mutex_lock(&viddevs[i]->mutex); + viddevs[i]->owner=cnt->threadnr; + viddevs[i]->frames=conf->roundrobin_frames; + cnt->switched=1; + } + + + v4l_set_input(cnt, viddevs[i], map, width, height, conf->input, conf->norm, + conf->roundrobin_skip, conf->frequency); + + ret = v4l_next(viddevs[i], map, width, height); + + if (--viddevs[i]->frames <= 0) { + viddevs[i]->owner=-1; + pthread_mutex_unlock(&viddevs[i]->mutex); + } + + if(cnt->rotate_data.degrees > 0){ + /* rotate the image as specified */ + rotate_map(cnt, map); + } + +#endif /*WITHOUT_V4L*/ + return ret; +} diff --git a/video_freebsd.h b/video_freebsd.h new file mode 100644 index 0000000..0ddf54c --- /dev/null +++ b/video_freebsd.h @@ -0,0 +1,116 @@ +/* video_freebsd.h + * + * Include file for video_freebsd.c + * Copyright 2004 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#ifndef _INCLUDE_VIDEO_FREEBSD_H +#define _INCLUDE_VIDEO_FREEBSD_H + +#ifndef WITHOUT_V4L + +#include +#include + +#endif + +/* bktr (video4linux) stuff FIXME more modes not only these */ + +/* not used yet FIXME ! only needed for tuner use */ +/* +#define TV_INPUT_NTSCM BT848_IFORM_F_NTSCM +#define TV_INPUT_NTSCJ BT848_IFORM_F_NTSCJ +#define TV_INPUT_PALBDGHI BT848_IFORM_F_PALBDGHI +#define TV_INPUT_PALM BT848_IFORM_F_PALM +#define TV_INPUT_PALN BT848_IFORM_F_PALN +#define TV_INPUT_SECAM BT848_IFORM_F_SECAM +#define TV_INPUT_PALNCOMB BT848_IFORM_F_RSVD +*/ + +/* video4linux error codes */ +#define V4L_GENERAL_ERROR 0x01 /* binary 000001 */ +#define V4L_BTTVLOST_ERROR 0x05 /* binary 000101 */ +#define V4L_FATAL_ERROR -1 + +#define NORM_DEFAULT 0x00800 // METEOR_FMT_AUTOMODE +#define NORM_PAL 0x00200 // METEOR_FMT_PAL +#define NORM_NTSC 0x00100 // METEOR_FMT_NTSC +#define NORM_SECAM 0x00400 // METEOR_FMT_SECAM +#define NORM_PAL_NC 0x00200 // METEOR_FMT_PAL /* Greyscale howto ?! FIXME */ + +#define NORM_DEFAULT_NEW BT848_IFORM_F_AUTO +#define NORM_PAL_NEW BT848_IFORM_F_PALBDGHI +#define NORM_NTSC_NEW BT848_IFORM_F_NTSCM +#define NORM_SECAM_NEW BT848_IFORM_F_SECAM +#define NORM_PAL_NC_NEW BT848_IFORM_F_AUTO /* FIXME */ + +#define BSD_VIDFMT_NONE 0 +#define BSD_VIDFMT_YV12 1 +#define BSD_VIDFMT_I420 2 +#define BSD_VIDFMT_YV16 3 +#define BSD_VIDFMT_YUY2 4 +#define BSD_VIDFMT_UYVY 5 +#define BSD_VIDFMT_RV15 6 +#define BSD_VIDFMT_RV16 7 +#define BSD_VIDFMT_LAST 8 + + +#define IN_DEFAULT 0 +#define IN_COMPOSITE 0 +#define IN_TV 1 +#define IN_COMPOSITE2 2 +#define IN_SVIDEO 3 + +#define CAPTURE_SINGLE 0 +#define CAPTURE_CONTINOUS 1 + +#define VIDEO_DEVICE "/dev/bktr0" +#define TUNER_DEVICE "/dev/turner0" + +struct video_dev { + int fd_bktr; + int fd_tuner; + const char *video_device; + const char *tuner_device; + unsigned short input; + unsigned short norm; + int width; + int height; + int contrast; + int saturation; + int hue; + int brightness; + int channel; + int channelset; + unsigned long freq; + + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + int owner; + int frames; + + /* Device type specific stuff: */ +#ifndef WITHOUT_V4L + int capture_method; + int v4l_fmt; + unsigned char *v4l_buffers[2]; + int v4l_curbuffer; + int v4l_maxbuffer; + int v4l_bufsize; +#endif +}; + +/* video functions, video_freebsd.c */ +int vid_start(struct context *); +int vid_next(struct context *, unsigned char *map); + +#ifndef WITHOUT_V4L +void vid_init(void); +void vid_close(void); +void vid_cleanup(void); +#endif + +#endif /* _INCLUDE_VIDEO_FREEBSD_H */ diff --git a/webcam.c b/webcam.c new file mode 100644 index 0000000..ef14b42 --- /dev/null +++ b/webcam.c @@ -0,0 +1,441 @@ +/* + * webcam.c + * Streaming webcam using jpeg images over a multipart/x-mixed-replace stream + * Copyright (C) 2002 Jeroen Vreeken (pe1rxq@amsat.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "picture.h" +#include "webcam.h" +#include "motion.h" +#include +#include +#include +#include +#include +#include +#include + + + + +/* This function sets up a TCP/IP socket for incomming requests. It is called only during + * initiatization of Motion from the function webcam_init + * The function sets up a a socket on the port number given by _port_. + * If the parameter _local_ is not zero the socket is setup to only accept connects from localhost. + * Otherwise any client IP address is accepted. The function returns an integer representing the socket. + */ +int http_bindsock(int port, int local) +{ + int sl, optval=1; + struct sockaddr_in sin; + + if ((sl=socket(PF_INET, SOCK_STREAM, 0))<0) { + motion_log(LOG_ERR, 1, "socket()"); + return -1; + } + + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family=AF_INET; + sin.sin_port=htons(port); + + if (local) + sin.sin_addr.s_addr=htonl(INADDR_LOOPBACK); + else + sin.sin_addr.s_addr=htonl(INADDR_ANY); + + setsockopt(sl, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + if (bind(sl, (struct sockaddr *)&sin, sizeof(struct sockaddr_in))==-1) { + motion_log(LOG_ERR, 1, "bind()"); + close(sl); + return -1; + } + + if (listen(sl, DEF_MAXWEBQUEUE)==-1) { + motion_log(LOG_ERR, 1, "listen()"); + close(sl); + return -1; + } + + return sl; +} + + +static int http_acceptsock(int sl) +{ + int sc; + unsigned long i; + struct sockaddr_in sin; + socklen_t addrlen=sizeof(struct sockaddr_in); + + if ((sc=accept(sl, (struct sockaddr *)&sin, &addrlen))>=0) { + i=1; + ioctl(sc, FIONBIO, &i); + return sc; + } + + motion_log(LOG_ERR, 1, "accept()"); + + return -1; +} + + +/* Webcam flush sends any outstanding data to all connected clients. + * It continuously goes through the client list until no data is able + * to be sent (either because there isn't any, or because the clients + * are not able to accept it). + */ +static void webcam_flush(struct webcam *list, int *stream_count, int lim) +{ + int written; /* the number of bytes actually written */ + struct webcam *client; /* pointer to the client being served */ + int workdone = 0; /* flag set any time data is successfully + written */ + + client = list->next; + + while (client) { + + /* If data waiting for client, try to send it */ + if (client->tmpbuffer) { + + /* We expect that list->filepos < list->tmpbuffer->size + * should always be true. The check is more for safety, + * in case of trouble is some other part of the code. + * Note that if it is false, the following section will + * clean up. + */ + if (client->filepos < client->tmpbuffer->size) { + + /* Here we are finally ready to write out the + * data. Remember that (because the socket + * has been set non-blocking) we may only + * write out part of the buffer. The var + * 'filepos' contains how much of the buffer + * has already been written. + */ + written=write(client->socket, + client->tmpbuffer->ptr + client->filepos, + client->tmpbuffer->size - client->filepos); + + /* If any data has been written, update the + * data pointer and set the "workdone flag + */ + if (written>0) { + client->filepos+=written; + workdone = 1; + } + } else + written = 0; + + /* If we have written the entire buffer to the socket, + * or if there was some error (other than EAGAIN, which + * means the system couldn't take it), this request is + * finished. + */ + if ( (client->filepos >= client->tmpbuffer->size) || + (written < 0 && errno!=EAGAIN)) { + /* If no other clients need this buffer, free it */ + if (--client->tmpbuffer->ref <= 0) { + free(client->tmpbuffer->ptr); + free(client->tmpbuffer); + } + + /* Mark this client's buffer as empty */ + client->tmpbuffer=NULL; + client->nr++; + } + + /* If the client is no longer connected, or the total + * number of frames already sent to this client is + * greater than our configuration limit, disconnect + * the client and free the webcam struct + */ + if ( (written<0 && errno!=EAGAIN) || + (lim && !client->tmpbuffer && client->nr>lim) ) { + void *tmp; + + close(client->socket); + + if (client->next) + client->next->prev=client->prev; + + client->prev->next=client->next; + tmp=client; + client=client->prev; + free(tmp); + (*stream_count)--; + } + } /* end if (client->tmpbuffer) */ + + /* Step the the next client in the list. If we get to the + * end of the list, check if anything was written during + * that loop; (if so) reset the 'workdone' flag and go back + * to the beginning + */ + client = client->next; + if (!client && workdone) { + client = list->next; + workdone = 0; + } + } /* end while (client) */ +} + +/* Routine to create a new "tmpbuffer", which is a common + * object used by all clients connected to a single camera + */ +static struct webcam_buffer *webcam_tmpbuffer(int size) +{ + struct webcam_buffer *tmpbuffer=mymalloc(sizeof(struct webcam_buffer)); + tmpbuffer->ref=0; + tmpbuffer->ptr=mymalloc(size); + + return tmpbuffer; +} + + +static void webcam_add_client(struct webcam *list, int sc) +{ + struct webcam *new = mymalloc(sizeof(struct webcam)); + static const char header[] = "HTTP/1.0 200 OK\r\n" + "Server: Motion/"VERSION"\r\n" + "Connection: close\r\n" + "Max-Age: 0\r\n" + "Expires: 0\r\n" + "Cache-Control: no-cache, private\r\n" + "Pragma: no-cache\r\n" + "Content-Type: multipart/x-mixed-replace; boundary=--BoundaryString\r\n\r\n"; + + memset(new, 0, sizeof(struct webcam)); + new->socket=sc; + + if ((new->tmpbuffer = webcam_tmpbuffer(sizeof(header))) == NULL) { + motion_log(LOG_ERR, 1, "Error creating tmpbuffer in webcam_add_client"); + } else { + memcpy(new->tmpbuffer->ptr, header, sizeof(header)-1); + new->tmpbuffer->size = sizeof(header)-1; + } + + new->prev=list; + new->next=list->next; + + if (new->next) + new->next->prev=new; + + list->next=new; +} + + +static void webcam_add_write(struct webcam *list, struct webcam_buffer *tmpbuffer, unsigned int fps) +{ + struct timeval curtimeval; + unsigned long int curtime; + + gettimeofday(&curtimeval, NULL); + curtime=curtimeval.tv_usec+1000000L*curtimeval.tv_sec; + + while (list->next) { + list=list->next; + + if (list->tmpbuffer==NULL && ((curtime-list->last) >= 1000000L/fps)) { + list->last=curtime; + list->tmpbuffer=tmpbuffer; + tmpbuffer->ref++; + list->filepos=0; + } + } + + if (tmpbuffer->ref<=0) { + free(tmpbuffer->ptr); + free(tmpbuffer); + } +} + + +/* We walk through the chain of webcam structs until we reach the end. + * Here we check if the tmpbuffer points to NULL + * We return 1 if it finds a list->tmpbuffer which is a NULL pointer which would + * be the next client ready to be sent a new image. If not a 0 is returned. + */ +static int webcam_check_write(struct webcam *list) +{ + while (list->next) { + list=list->next; + + if (list->tmpbuffer==NULL) + return 1; + } + return 0; +} + + +/* This function is called from motion.c for each motion thread starting up. + * The function setup the incomming tcp socket that the clients connect to + * The function returns an integer representing the socket. + */ +int webcam_init(struct context *cnt) +{ + cnt->webcam.socket=http_bindsock(cnt->conf.webcam_port, cnt->conf.webcam_localhost); + cnt->webcam.next=NULL; + cnt->webcam.prev=NULL; + return cnt->webcam.socket; +} + +/* This function is called from the motion_loop when it ends + * and motion is terminated or restarted + */ +void webcam_stop(struct context *cnt) +{ + struct webcam *list; + struct webcam *next = cnt->webcam.next; + + if (cnt->conf.setup_mode) + motion_log(-1, 0, "Closing webcam listen socket"); + + close(cnt->webcam.socket); + + if (cnt->conf.setup_mode) + motion_log(LOG_ERR, 1, "Closing active webcam sockets"); + + while (next) { + list=next; + next=list->next; + + if (list->tmpbuffer) { + free(list->tmpbuffer->ptr); + free(list->tmpbuffer); + } + + close(list->socket); + free(list); + } +} + +/* webcam_put is the starting point of the webcam loop. It is called from + * the motion_loop with the argument 'image' pointing to the latest frame. + * If config option 'webcam_motion' is 'on' this function is called once + * per second (frame 0) and when Motion is detected excl pre_capture. + * If config option 'webcam_motion' is 'off' this function is called once + * per captured picture frame. + * It is always run in setup mode for each picture frame captured and with + * the special setup image. + * The function does two things: + * It looks for possible waiting new clients and adds them. + * It sends latest picture frame to all connected clients. + * Note: Clients that have disconnected are handled in the webcam_flush() + * function + */ +void webcam_put(struct context *cnt, unsigned char *image) +{ + struct timeval timeout; + struct webcam_buffer *tmpbuffer; + fd_set fdread; + int sl=cnt->webcam.socket; + int sc; + /* the following string has an extra 16 chars at end for length */ + const char jpeghead[] = "--BoundaryString\r\n" + "Content-type: image/jpeg\r\n" + "Content-Length: "; + int headlength = sizeof(jpeghead) - 1; /* don't include terminator */ + char len[20]; /* will be used for sprintf, must be >= 16 */ + + /* timeout struct used to timeout the time we wait for a client + * and we do not wait at all + */ + timeout.tv_sec=0; + timeout.tv_usec=0; + FD_ZERO(&fdread); + FD_SET(cnt->webcam.socket, &fdread); + + /* If we have not reached the max number of allowed clients per + * thread we will check to see if new clients are waiting to connect. + * If this is the case we add the client as a new webcam struct and + * add this to the end of the chain of webcam structs that are linked + * to each other. + */ + if ((cnt->stream_count < DEF_MAXSTREAMS) && + (select(sl+1, &fdread, NULL, NULL, &timeout)>0)) { + sc=http_acceptsock(sl); + webcam_add_client(&cnt->webcam, sc); + cnt->stream_count++; + } + + /* call flush to send any previous partial-sends which are waiting */ + webcam_flush(&cnt->webcam, &cnt->stream_count, cnt->conf.webcam_limit); + + /* Check if any clients have available buffers */ + if (webcam_check_write(&cnt->webcam)) { + /* yes - create a new tmpbuffer for current image. + * Note that this should create a buffer which is *much* larger + * than necessary, but it is difficult to estimate the + * minimum size actually required. + */ + tmpbuffer = webcam_tmpbuffer(cnt->imgs.size); + + /* check if allocation went ok */ + if (tmpbuffer) { + int imgsize; + + /* We need a pointer that points to the picture buffer + * just after the mjpeg header. We create a working pointer wptr + * to be used in the call to put_picture_memory which we can change + * and leave tmpbuffer->ptr intact. + */ + unsigned char *wptr = tmpbuffer->ptr; + + /* For web protocol, our image needs to be preceded + * with a little HTTP, so we put that into the buffer + * first. + */ + memcpy(wptr, jpeghead, headlength); + + /* update our working pointer to point past header */ + wptr += headlength; + + /* create a jpeg image and place into tmpbuffer */ + tmpbuffer->size = put_picture_memory(cnt, wptr, cnt->imgs.size, image, + cnt->conf.webcam_quality); + + /* fill in the image length into the header */ + imgsize = sprintf(len, "%9ld\r\n\r\n", tmpbuffer->size); + memcpy(wptr - imgsize, len, imgsize); + + /* append a CRLF for good measure */ + memcpy(wptr + tmpbuffer->size, "\r\n", 2); + + /* now adjust tmpbuffer->size to reflect the + * header at the beginning and the extra CRLF + * at the end. + */ + tmpbuffer->size += headlength + 2; + + /* and finally put this buffer to all clients with + * no outstanding data from previous frames. + */ + webcam_add_write(&cnt->webcam, tmpbuffer, cnt->conf.webcam_maxrate); + } else { + motion_log(LOG_ERR, 1, "Error creating tmpbuffer"); + } + } + + /* Now we call flush again. This time (assuming some clients were + * ready for the new frame) the new data will be written out. + */ + webcam_flush(&cnt->webcam, &cnt->stream_count, cnt->conf.webcam_limit); + + return; +} diff --git a/webcam.h b/webcam.h new file mode 100644 index 0000000..db0d1d8 --- /dev/null +++ b/webcam.h @@ -0,0 +1,45 @@ +/* + * webcam.h + * + * Include file for webcam.c + * Copyright (C) 2002 Jeroen Vreeken (pe1rxq@amsat.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _INCLUDE_WEBCAM_H_ +#define _INCLUDE_WEBCAM_H_ + +struct webcam_buffer { + unsigned char *ptr; + int ref; + long size; +}; + +struct webcam { + int socket; + FILE *fwrite; + struct webcam_buffer *tmpbuffer; + long filepos; + int nr; + unsigned long int last; + struct webcam *prev; + struct webcam *next; +}; + +int webcam_init(struct context *); +void webcam_put(struct context *, unsigned char *); +void webcam_stop(struct context *); + +#endif /* _INCLUDE_WEBCAM_H_ */ diff --git a/webhttpd.c b/webhttpd.c new file mode 100644 index 0000000..2a51404 --- /dev/null +++ b/webhttpd.c @@ -0,0 +1,2143 @@ +/* + * webhttpd.c + * + * HTTP Control interface for motion. + * + * Specs : http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpAPI + * + * Copyright 2004-2005 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU Public License Version 2 + * See also the file 'COPYING'. + * + */ +#include "motion.h" +#include +#include +#include +#include +#include +#include "conf.h" +#include "webhttpd.h" +#include "netcam_wget.h" + +pthread_mutex_t httpd_mutex; + +int warningkill; // This is a dummy variable use to kill warnings when not checking sscanf and similar functions + +static const char* ini_template = + "\n" + "\n"; + +static const char *set_template = + "\n\n" + "\n"; + +static const char* end_template = + "\n" + "\n"; + +static const char* ok_response = + "HTTP/1.1 200 OK\r\n" + "Server: Motion-httpd/"VERSION"\r\n" + "Connection: close\r\n" + "Max-Age: 0\r\n" + "Expires: 0\r\n" + "Cache-Control: no-cache\r\n" + "Cache-Control: private\r\n" + "Pragma: no-cache\r\n" + "Content-type: text/html\r\n\r\n"; + +static const char* ok_response_raw = + "HTTP/1.1 200 OK\r\n" + "Server: Motion-httpd/"VERSION"\r\n" + "Connection: close\r\n" + "Max-Age: 0\r\n" + "Expires: 0\r\n" + "Cache-Control: no-cache\r\n" + "Cache-Control: private\r\n" + "Pragma: no-cache\r\n" + "Content-type: text/plain\r\n\r\n"; + + +static const char* bad_request_response = + "HTTP/1.0 400 Bad Request\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Bad Request

\n" + "

The server did not understand your request.

\n" + "\n" + "\n"; + +static const char* bad_request_response_raw = + "HTTP/1.0 400 Bad Request\r\n" + "Content-type: text/plain\r\n\r\n" + "Bad Request"; + +static const char* not_found_response_template = + "HTTP/1.0 404 Not Found\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Found

\n" + "

The requested URL was not found on the server.

\n" + "\n" + "\n"; + +static const char* not_found_response_template_raw = + "HTTP/1.0 404 Not Found\r\n" + "Content-type: text/plain\r\n\r\n" + "Not Found"; + +static const char* not_found_response_valid = + "HTTP/1.0 404 Not Valid\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Valid

\n" + "

The requested URL is not valid.

\n" + "\n" + "\n"; + +static const char* not_found_response_valid_raw = + "HTTP/1.0 404 Not Valid\r\n" + "Content-type: text/plain\r\n\r\n" + "The requested URL is not valid."; + +static const char* not_valid_syntax = + "HTTP/1.0 404 Not Valid Syntax\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Valid Syntax

\n" + "\n" + "\n"; + +static const char* not_valid_syntax_raw = + "HTTP/1.0 404 Not Valid Syntax\r\n" + "Content-type: text/plain\r\n\r\n" + "Not Valid Syntax\n"; + +static const char* not_track = + "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Tracking Not Enabled

\n"; + +static const char* not_track_raw = + "HTTP/1.0 200 OK\r\n" + "Content-type: text/plain\r\n\r\n" + "Tracking Not Enabled"; + +static const char* track_error = + "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Track Error

\n"; + +static const char* track_error_raw = + "HTTP/1.0 200 OK\r\n" + "Content-type: text/plain\r\n\r\n" + "Track Error"; + +static const char* error_value = + "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Value Error

\n"; + +static const char* error_value_raw = + "HTTP/1.0 200 OK\r\n" + "Content-type: text/plain\r\n\r\n" + "Value Error"; + +static const char* not_found_response_valid_command = + "HTTP/1.0 404 Not Valid Command\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Valid Command

\n" + "

The requested URL is not valid Command.

\n" + "\n" + "\n"; + +static const char* not_found_response_valid_command_raw = + "HTTP/1.0 404 Not Valid Command\r\n" + "Content-type: text/plain\n\n" + "Not Valid Command\n"; + +static const char* bad_method_response_template = + "HTTP/1.0 501 Method Not Implemented\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Method Not Implemented

\n" + "

The method is not implemented by this server.

\n" + "\n" + "\n"; + +static const char* bad_method_response_template_raw = + "HTTP/1.0 501 Method Not Implemented\r\n" + "Content-type: text/plain\r\n\r\n" + "Method Not Implemented\n"; + +static const char *request_auth_response_template= + "HTTP/1.0 401 Authorization Required\r\n" + "WWW-Authenticate: Basic realm=\"Motion Security Access\"\r\n"; + +static void send_template_ini_client(int client_socket, const char* template) +{ + ssize_t nwrite = 0; + nwrite = write(client_socket, ok_response, strlen (ok_response)); + nwrite += write(client_socket, template, strlen(template)); + if (nwrite != (ssize_t)(strlen(ok_response) + strlen(template))) + motion_log(LOG_ERR, 1, "httpd send_template_ini_client"); +} + +static void send_template_ini_client_raw(int client_socket) +{ + ssize_t nwrite = 0; + nwrite = write(client_socket, ok_response_raw, strlen (ok_response_raw)); + if (nwrite != (ssize_t)strlen(ok_response_raw)) + motion_log(LOG_ERR, 1, "httpd send_template_ini_client_raw"); +} + +static void send_template(int client_socket, char *res) +{ + ssize_t nwrite = 0; + nwrite = write(client_socket, res, strlen(res)); + if ( nwrite != (ssize_t)strlen(res)) + motion_log(LOG_ERR, 1, "httpd send_template failure write"); +} + +static void send_template_raw(int client_socket, char *res) +{ + ssize_t nwrite = 0; + nwrite = write(client_socket, res, strlen(res)); +} + +static void send_template_end_client(int client_socket) +{ + ssize_t nwrite = 0; + nwrite = write(client_socket, end_template, strlen(end_template)); +} + +static void response_client(int client_socket, const char* template, char *back) +{ + ssize_t nwrite = 0; + nwrite = write(client_socket, template, strlen(template)); + if (back != NULL) { + send_template(client_socket, back); + send_template_end_client(client_socket); + } +} + + +static int check_authentication(char *authentication, char *auth_base64, size_t size_auth, const char *conf_auth) +{ + int ret = 0; + char *userpass = NULL; + + authentication = (char *) mymalloc(BASE64_LENGTH(size_auth) + 1); + userpass = mymalloc(size_auth + 4); + /* base64_encode can read 3 bytes after the end of the string, initialize it */ + memset(userpass, 0, size_auth + 4); + strcpy(userpass, conf_auth); + base64_encode(userpass, authentication, size_auth); + free(userpass); + + if (!strcmp(authentication, auth_base64)) + ret=1; + + return ret; +} + + + +/* + This function decode the values from GET request following the http RFC. +*/ + +static int url_decode(char *urlencoded, int length) +{ + char *data=urlencoded; + char *urldecoded=urlencoded; + + while (length > 0) { + if (*data == '%') { + char c[3]; + int i; + data++; + length--; + c[0] = *data++; + length--; + c[1] = *data; + c[2] = 0; + warningkill = sscanf(c, "%x", &i); + if (i < 128) + *urldecoded++ = (char)i; + else { + *urldecoded++ = '%'; + *urldecoded++ = c[0]; + *urldecoded++ = c[1]; + } + } else if (*data=='+') { + *urldecoded++=' '; + + } + else { + *urldecoded++=*data; + } + data++; + length--; + } + *urldecoded = '\0'; + return 0; +} + + +/* + This function manages/parses the config action for motion ( set , get , write , list ). +*/ + +static int config(char *pointer, char *res, int length_uri, int thread, int client_socket, void *userdata) +{ + char question; + char command[256] = {'\0'}; + int i; + struct context **cnt = userdata; + + warningkill = sscanf (pointer, "%256[a-z]%c", command , &question); + if (!strcmp(command,"list")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if (length_uri == 0) { + const char *value = NULL; + char *retval = NULL; + /*call list*/ + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<- back", thread); + send_template(client_socket, res); + + for (i=0; config_params[i].param_name != NULL; i++) { + value = config_params[i].print(cnt, NULL, i, thread); + + if (value == NULL) { + retval=NULL; + + /* Only get the thread value for main thread */ + if (thread == 0) + config_params[i].print(cnt, &retval, i, thread); + + /* thread value*/ + + if (retval) { + + if (!strcmp(retval,"")) { + free(retval); + retval = strdup("No threads"); + } else { + char *temp=retval; + size_t retval_miss = 0; + size_t retval_len = strlen(retval); + int ind=0; + char thread_strings[1024]={'\0'}; + + while (retval_miss != retval_len) { + while (*temp != '\n') { + thread_strings[ind++] = *temp; + retval_miss++; + temp++; + } + temp++; + thread_strings[ind++] = '<'; + thread_strings[ind++] = 'b'; + thread_strings[ind++] = 'r'; + thread_strings[ind++] = '>'; + retval_miss++; + } + free(retval); + retval = NULL; + retval = strdup(thread_strings); + } + + sprintf(res, "
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, retval); + free(retval); + } else if (thread != 0) { + /* get the value from main thread for the rest of threads */ + value = config_params[i].print(cnt, NULL, i, 0); + + sprintf(res,"
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, + value ? value : "(not defined)"); + } else { + sprintf(res, "
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, + "(not defined)"); + } + + } else { + sprintf(res,"
  • %s = %s
  • \n",thread, + config_params[i].param_name, config_params[i].param_name, value); + } + send_template(client_socket, res); + } + + sprintf(res,"
    <- back",thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + for (i=0; config_params[i].param_name != NULL; i++) { + value=config_params[i].print(cnt, NULL, i, thread); + if (value == NULL) + value=config_params[i].print(cnt, NULL, i, 0); + sprintf(res,"%s = %s\n", config_params[i].param_name, value); + send_template_raw(client_socket, res); + } + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command,"set")) { + /* set?param_name=value */ + pointer = pointer + 3; + length_uri = length_uri - 3; + if ((length_uri != 0) && (question == '?')) { + pointer++; + length_uri--; + warningkill = sscanf(pointer,"%256[a-z_]%c", command, &question); + /*check command , question == '=' length_uri too*/ + if ((question == '=') && (command[0]!='\0')) { + length_uri = length_uri - strlen(command) - 1; + pointer = pointer + strlen(command) + 1; + /* check if command exists and type of command and not end of URI */ + i=0; + while (config_params[i].param_name != NULL) { + if (!strcasecmp(command, config_params[i].param_name)) + break; + i++; + } + + if (config_params[i].param_name) { + if (length_uri > 0) { + char Value[1024]={'\0'}; + warningkill = sscanf(pointer,"%1024s", Value); + length_uri = length_uri - strlen(Value); + if ( (length_uri == 0) && (strlen(Value) > 0) ) { + /* FIXME need to asure that is a valid value */ + url_decode(Value,strlen(Value)); + conf_cmdparse(cnt + thread, config_params[i].param_name, Value); + if (cnt[0]->conf.control_html_output) { + sprintf(res, + "
  • %s = %s
  • Done
    \n" + "<- back\n", + thread, config_params[i].param_name, + config_params[i].param_name, Value, thread); + + send_template_ini_client(client_socket, ini_template); + send_template(client_socket,res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, Value); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + } + } else { + char *type = NULL; + type = strdup(config_type(&config_params[i])); + + if (!strcmp(type,"string")) { + char value[1]={'\0'}; + conf_cmdparse(cnt+thread, config_params[i].param_name, value); + free(type); + type = strdup("(null)"); + } else if (!strcmp(type,"int")) { + free(type); + type = strdup("0"); + conf_cmdparse(cnt+thread, config_params[i].param_name, type); + } else if (!strcmp(type,"bool")) { + free(type); + type = strdup("off"); + conf_cmdparse(cnt+thread, config_params[i].param_name, type); + } else { + free(type); + type = strdup("unknown"); + } + + if (cnt[0]->conf.control_html_output) { + sprintf(res, + "
  • %s = %s
  • Done
    \n" + "<- back", + thread, config_params[i].param_name, + config_params[i].param_name, type, thread); + + send_template_ini_client(client_socket, ini_template); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "%s = %s\nDone\n", config_params[i].param_name,type); + send_template_raw(client_socket, res); + } + free(type); + + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /* Show param_name dialogue only for html output */ + if ( (cnt[0]->conf.control_html_output) && (command[0]!='\0') && + (((length_uri = length_uri - strlen(command)) == 0 )) ) { + i=0; + while (config_params[i].param_name != NULL) { + if (!strcasecmp(command, config_params[i].param_name)) + break; + i++; + } + /* param_name exists */ + if (config_params[i].param_name) { + send_template_ini_client(client_socket, ini_template); + if (!strcmp ("bool",config_type(&config_params[i])) ) + sprintf(res, "Thread %d \n" + "
    \n" + "%s \n" + "\n" + "
    \n" + "<- back\n", thread, + config_params[i].param_name, config_params[i].param_name, thread); + else + sprintf(res, "Thread %d \n" + "
    \n" + "%s \n" + "\n" + "
    \n" + "<- back\n", thread, + config_params[i].param_name, config_params[i].param_name, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, set_template); + sprintf(res, "Thread %d \n
    \n\n
    \n" + "
    \n" + "\n" + "\n" + "
    \n" + "<- back\n", thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"set needs param_name=value\n"); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command,"get")) { + /* get?query=param_name */ + pointer = pointer + 3; + length_uri = length_uri - 3; + + if ((length_uri > 7) && (question == '?')) { + /* 8 -> query=param_name FIXME minimun length param_name */ + pointer++; + length_uri--; + warningkill = sscanf(pointer,"%256[a-z]%c", command, &question); + + if ( (question == '=') && (!strcmp(command,"query")) ) { + pointer = pointer + 6; + length_uri = length_uri - 6; + warningkill = sscanf(pointer, "%256[a-z_]", command); + /*check if command exist, length_uri too*/ + length_uri = length_uri-strlen(command); + + if (length_uri == 0) { + const char *value = NULL; + i = 0; + while (config_params[i].param_name != NULL) { + if (!strcasecmp(command, config_params[i].param_name)) + break; + i++; + } + /* FIXME bool values or commented values maybe that should be + solved with config_type */ + + if (config_params[i].param_name) { + const char *type=NULL; + type = config_type(&config_params[i]); + if (!strcmp(type,"unknown")) { + /*error doesn't exists this param_name */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + return 1; + } else { + value = config_params[i].print(cnt, NULL, i, thread); + if (value == NULL) + value=config_params[i].print(cnt, NULL, i, 0); + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket,ini_template); + sprintf(res,"
  • %s = %s

  • \n" + "<- back\n", + config_params[i].param_name,value, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"%s = %s\nDone\n", config_params[i].param_name,value); + send_template_raw(client_socket, res); + } + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d
    \n" + "
    \n" + "\n" + "\n" + "
    \n" + "<- back\n", thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"get needs param_name\n"); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + } + } else if (!strcmp(command,"write")) { + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Are you sure? Yes
    \n" + "No\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + conf_print(cnt); + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d write\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + } else if (!strcmp(command, "writeyes")) { + pointer = pointer + 8; + length_uri = length_uri - 8; + if (length_uri==0) { + conf_print(cnt); + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d write done !
    \n" + "<- back\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d write\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) { + response_client(client_socket, not_found_response_valid_command, NULL); + } + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; +} + + +/* + This function manages/parses the actions for motion ( makemovie , snapshot , restart , quit ). +*/ + +static int action(char *pointer, char *res, int length_uri, int thread, int client_socket, void *userdata) +{ + /* parse action commands */ + char command[256] = {'\0'}; + struct context **cnt = userdata; + + warningkill = sscanf (pointer, "%256[a-z]" , command); + if (!strcmp(command,"makemovie")) { + pointer = pointer + 9; + length_uri = length_uri - 9; + if (length_uri == 0) { + /*call makemovie*/ + + if (thread == 0) { + int i = 0; + while (cnt[++i]) + cnt[i]->makemovie=1; + } else { + cnt[thread]->makemovie=1; + } + + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"makemovie for thread %d done
    \n" + "<- back\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"makemovie for thread %d\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket,not_found_response_valid_command,NULL); + else + response_client(client_socket,not_found_response_valid_command_raw,NULL); + } + } else if (!strcmp(command,"snapshot")) { + pointer = pointer + 8; + length_uri = length_uri - 8; + if (length_uri == 0) { + /*call snapshot*/ + + if (thread == 0) { + int i = 0; + while (cnt[++i]) + cnt[i]->snapshot=1; + } else { + cnt[thread]->snapshot=1; + } + + cnt[thread]->snapshot = 1; + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"snapshot for thread %d done
    \n" + "<- back\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"snapshot for thread %d\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "restart")) { + pointer = pointer + 7; + length_uri = length_uri - 7; + if (length_uri == 0) { + int i = 0; + do { + 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); + } 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 { + if (cnt[0]->conf.control_html_output) + response_client(client_socket,not_found_response_valid_command,NULL); + else + response_client(client_socket,not_found_response_valid_command_raw,NULL); + } + } else if (!strcmp(command,"quit")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if (length_uri == 0) { + int i = 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); + } 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 { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; +} + +/* + This function manages/parses the detection actions for motion ( status , start , pause ). +*/ + +static int detection(char *pointer, char *res, int length_uri, int thread, int client_socket, void *userdata) +{ + char command[256]={'\0'}; + struct context **cnt=userdata; + + warningkill = sscanf (pointer, "%256[a-z]" , command); + if (!strcmp(command,"status")) { + pointer = pointer + 6; + length_uri = length_uri - 6; + if (length_uri == 0) { + /*call status*/ + if (cnt[thread]->pause) + sprintf(res, "Thread %d Detection status PAUSE\n", thread); + else + sprintf(res, "Thread %d Detection status ACTIVE\n", thread); + + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + send_template(client_socket, res); + sprintf(res, "
    <- back\n", thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "start")) { + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + /*call start*/ + cnt[thread]->pause = 0; + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket,ini_template); + sprintf(res,"Thread %i Detection resumed
    \n" + "<- back\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %i Detection resumed\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command,"pause")){ + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri==0) { + /*call pause*/ + cnt[thread]->pause=1; + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %i Detection paused
    \n" + "<- back\n",thread,thread); + send_template(client_socket,res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %i Detection paused\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; +} + + +/* + This function manages/parses the track action for motion ( set , pan , tilt , auto ). +*/ + +static int track(char *pointer, char *res, int length_uri, int thread, int client_socket, void *userdata) +{ + char question; + char command[256] = {'\0'}; + struct context **cnt = userdata; + + warningkill = sscanf(pointer, "%256[a-z]%c", command, &question); + if (!strcmp(command, "set")) { + pointer=pointer+3;length_uri=length_uri-3; + /* FIXME need to check each value */ + /* Relative movement set?pan=0&tilt=0 | set?pan=0 | set?tilt=0*/ + /* Absolute movement set?x=0&y=0 | set?x=0 | set?y=0 */ + + if ((question == '?') && (length_uri > 2)) { + char panvalue[12] = {'\0'}, tiltvalue[12] = {'\0'}; + char x_value[12] = {'\0'}, y_value[12] = {'\0'}; + struct context *setcnt; + int pan = 0, tilt = 0, X = 0 , Y = 0; + + pointer++; + length_uri--; + /* set?pan=value&tilt=value */ + /* set?x=value&y=value */ + /* pan= or x= | tilt= or y= */ + + warningkill = sscanf (pointer, "%256[a-z]%c" , command, &question); + + if (( question != '=' ) || (command[0] == '\0')) { + /* no valid syntax */ + motion_log(LOG_WARNING, 0, "httpd debug race 1"); + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + + pointer++; + length_uri--; + + /* Check first parameter */ + + if (!strcmp(command, "pan")) { + pointer = pointer + 3; + length_uri = length_uri - 3; + pan = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]", panvalue))){ + pointer = pointer + strlen(panvalue); + length_uri = length_uri - strlen(panvalue); + } + } + else if (!strcmp(command, "tilt")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + tilt = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]", tiltvalue))){ + pointer = pointer + strlen(tiltvalue); + length_uri = length_uri - strlen(tiltvalue); + } + } + else if (!strcmp(command, "x")) { + pointer++; + length_uri--; + X = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]", x_value))){ + pointer = pointer + strlen(x_value); + length_uri = length_uri - strlen(x_value); + } + } + else if (!strcmp(command, "y")) { + pointer++; + length_uri--; + Y = 1; + if ((warningkill = sscanf (pointer, "%10[-0-9]" , y_value))){ + pointer = pointer + strlen(y_value); + length_uri = length_uri - strlen(y_value); + } + } else { + /* no valid syntax */ + motion_log(LOG_WARNING, 0, "httpd debug race 2"); + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + + + /* first value check for error */ + + + if ( !warningkill ) { + motion_log(LOG_WARNING, 0, "httpd debug race 3"); + /* error value */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + return 1; + } + + + + /* Only one parameter (pan= ,tilt= ,x= ,y= ) */ + if (length_uri == 0) { + if (pan) { + struct coord cent; + struct context *pancnt; + + /* move pan */ + + pancnt = cnt[thread]; + cent.width = pancnt->imgs.width; + cent.height = pancnt->imgs.height; + cent.y = 0; + cent.x = atoi(panvalue); + // Add the number of frame to skip for motion detection + cnt[thread]->moved = track_move(pancnt, pancnt->video_dev, ¢, &pancnt->imgs, 1); + if (cnt[thread]->moved) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track set relative pan=%s
    \n" + "<- back\n", panvalue, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track set relative pan=%s\nDone\n", panvalue); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res, "<- back\n", thread); + response_client(client_socket, track_error, res); + } + else + response_client(client_socket, track_error_raw, NULL); + } + } else if (tilt) { + struct coord cent; + struct context *tiltcnt; + + /* move tilt */ + + tiltcnt = cnt[thread]; + cent.width = tiltcnt->imgs.width; + cent.height = tiltcnt->imgs.height; + cent.x = 0; + cent.y = atoi(tiltvalue); + // Add the number of frame to skip for motion detection + cnt[thread]->moved=track_move(tiltcnt, tiltcnt->video_dev, ¢, &tiltcnt->imgs, 1); + if (cnt[thread]->moved){ + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track set relative tilt=%s
    \n" + "<- back\n", tiltvalue, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track set relative tilt=%s\nDone\n",tiltvalue); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n", thread); + response_client(client_socket, track_error, res); + } + else + response_client(client_socket, track_error_raw, NULL); + } + } else if (X){ + /* X */ + setcnt = cnt[thread]; + // 1000 is out of range for pwc + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), 1000); + if (cnt[thread]->moved) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track set absolute x=%s
    \n" + "<- back\n", x_value, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track set absolute x=%s\nDone\n", x_value); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n", thread); + response_client(client_socket, track_error, res); + } + else + response_client(client_socket, track_error_raw, NULL); + } + + } else { + /* Y */ + setcnt = cnt[thread]; + // 1000 is out of range for pwc + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 1000, atoi(y_value)); + if (cnt[thread]->moved) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track set absolute y=%s
    \n" + "<- back\n", y_value, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track set absolute y=%s\nDone\n", y_value); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back", thread); + response_client(client_socket, track_error, res); + } + else + response_client(client_socket, track_error_raw, NULL); + } + } + return 1; + } + + + /* Check Second parameter */ + + warningkill = sscanf (pointer, "%c%256[a-z]" ,&question, command); + if ( ( question != '&' ) || (command[0] == '\0') ){ + motion_log(LOG_WARNING, 0, "httpd debug race 4"); + if ( strstr(pointer,"&")){ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + } + /* no valid syntax */ + else{ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + } + return 1; + } + + pointer++; + length_uri--; + + if (!strcmp(command, "pan")){ + pointer = pointer + 3; + length_uri = length_uri - 3; + if ( (pan) || (!tilt) || (X) || (Y) ) { + motion_log(LOG_WARNING, 0, "httpd debug race 5"); + /* no valid syntax */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + pan=2; + warningkill = sscanf (pointer, "%c%10[-0-9]" ,&question, panvalue); + } + else if (!strcmp(command, "tilt")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if ( (tilt) || (!pan) || (X) || (Y) ) { + /* no valid syntax */ + motion_log(LOG_WARNING, 0, "httpd debug race 6"); + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + tilt = 2; + warningkill = sscanf (pointer, "%c%10[-0-9]" ,&question, tiltvalue); + } + else if (!strcmp(command, "x")) { + pointer++; + length_uri--; + if ( (X) || (!Y) || (pan) || (tilt) ) { + motion_log(LOG_WARNING, 0, "httpd debug race 7"); + + /* no valid syntax */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + X = 2; + warningkill = sscanf (pointer, "%c%10[-0-9]" ,&question, x_value); + } + else if (!strcmp(command, "y")) { + pointer++; + length_uri--; + if ( (Y) || (!X) || (pan) || (tilt) ){ + motion_log(LOG_WARNING, 0, "httpd debug race 8"); + /* no valid syntax */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + Y = 2; + warningkill = sscanf (pointer, "%c%10[-0-9]" ,&question, y_value); + } else { + motion_log(LOG_WARNING, 0, "httpd debug race 9"); + /* no valid syntax */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + + /* Second value check */ + + if ( ( warningkill < 2 ) && (question != '=') ) { + motion_log(LOG_WARNING, 0, "httpd debug race 10"); + /* no valid syntax */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + }else if (( question == '=') && ( warningkill == 1)){ + motion_log(LOG_WARNING, 0, "httpd debug race 11"); + if (cnt[0]->conf.control_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + return 1; + } + + + if (pan == 2){ + pointer = pointer + strlen(panvalue) + 1; + length_uri = length_uri - strlen(panvalue) - 1; + } + else if (tilt == 2){ + pointer = pointer + strlen(tiltvalue) + 1; + length_uri = length_uri - strlen(tiltvalue) - 1; + } + else if (X == 2){ + pointer = pointer + strlen(x_value) + 1; + length_uri = length_uri - strlen(x_value) - 1; + } + else{ + pointer = pointer + strlen(y_value) + 1; + length_uri = length_uri - strlen(y_value) - 1; + } + + + + if (length_uri != 0) { + motion_log(LOG_WARNING, 0, "httpd debug race 12"); + if (cnt[0]->conf.control_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + return 1; + } + + + /* track set absolute ( x , y )*/ + + if ( X && Y ) { + setcnt = cnt[thread]; + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), atoi(y_value)); + if (cnt[thread]->moved) { + if (cnt[0]->conf.control_html_output){ + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track set x=%s y=%s
    \n" + "<- back\n", x_value, y_value, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track set x=%s y=%s\nDone\n", x_value, y_value); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n", thread); + response_client(client_socket, track_error, res); + } + else + response_client(client_socket, track_error_raw, NULL); + } + /* track set relative ( pan , tilt )*/ + } else { + struct coord cent; + struct context *relativecnt; + + /* move pan */ + + relativecnt = cnt[thread]; + cent.width = relativecnt->imgs.width; + cent.height = relativecnt->imgs.height; + cent.y = 0; + cent.x = atoi(panvalue); + // Add the number of frame to skip for motion detection + cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); + if (cnt[thread]->moved){ + /* move tilt */ + relativecnt = cnt[thread]; + cent.width = relativecnt->imgs.width; + cent.height = relativecnt->imgs.height; + cent.x = 0; + cent.y = atoi(tiltvalue); + SLEEP(1,0); + cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); + if (cnt[thread]->moved){ + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track pan=%s tilt=%s
    \n" + "<- back\n", panvalue, tiltvalue, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track pan=%s tilt=%s\nDone\n", panvalue, tiltvalue); + send_template_raw(client_socket, res); + } + return 1; + } + else{ + /*error in track tilt*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n", thread); + response_client(client_socket, track_error, res); + }else response_client(client_socket, track_error_raw, NULL); + } + } + + /*error in track pan*/ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n", thread); + response_client(client_socket, track_error, res); + } else + response_client(client_socket, track_error_raw, NULL); + } + } else if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d
    \n" + "
    \n" + "Pan\n" + "Tilt\n" + "\n" + "
    \n" + "
    \n" + "X\n" + "Y\n" + "\n" + "
    \n" + "<- back\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"set needs a pan/tilt or x/y values\n"); + send_template_raw(client_socket, res); + } + } else { + /* error not valid command */ + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command,"status")) { + pointer = pointer+6; + length_uri = length_uri-6; + if (length_uri==0) { + if (cnt[0]->conf.control_html_output) { + if (cnt[thread]->track.active) + sprintf(res,"Thread %d
    track auto enabled
    \n",thread); + else + sprintf(res,"Thread %d
    track auto disabled
    \n",thread); + send_template_ini_client(client_socket, ini_template); + send_template(client_socket, res); + sprintf(res, "<- back\n",thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + if (cnt[thread]->track.active) + sprintf(res,"Thread %d\n track auto enabled\nDone\n",thread); + else + sprintf(res,"Thread %d\n track auto disabled\nDone\n",thread); + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + /* error not valid command */ + if (cnt[0]->conf.control_html_output) { + response_client(client_socket, not_found_response_valid_command, NULL); + } + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command,"auto")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if ((question == '?') && (length_uri > 0)) { + char query[256] = {'\0'}; + pointer++; + length_uri--; + /* value= */ + + warningkill = sscanf (pointer, "%256[a-z]%c",query,&question); + if ((question == '=') && (!strcmp(query,"value")) ) { + pointer = pointer + 6; + length_uri = length_uri - 6; + warningkill = sscanf (pointer, "%256[-0-9a-z]" , command); + if ((command!=NULL) && (strlen(command) > 0)) { + struct context *autocnt; + + /* auto value=0|1|status*/ + + if (!strcmp(command,"status")) { + if (cnt[0]->conf.control_html_output) { + if (cnt[thread]->track.active) + sprintf(res, "Thread %d
    track auto enabled\n", thread); + else + sprintf(res, "Thread %d
    track auto disabled\n", thread); + send_template_ini_client(client_socket, ini_template); + send_template(client_socket, res); + sprintf(res,"<- back", thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + if (cnt[thread]->track.active) + sprintf(res, "Thread %d\n track auto enabled\nDone\n", thread); + else + sprintf(res, "Thread %d\n track auto disabled\nDone\n", thread); + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + int active; + active = atoi(command); + if (active > -1 && active < 2) { + autocnt = cnt[thread]; + autocnt->track.active = atoi(command); + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"track auto %s
    <- back", + command,thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"track auto %s\nDone\n",command); + send_template_raw(client_socket, res); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + else if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d\n" + "
    \n" + "
    \n" + "<- back\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"auto needs a value 0,1 or status\n"); + send_template_raw(client_socket, res); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; +} + + + +/* + parses the action requested for motion ( config , action , detection , track ) and call + to action function if needed. +*/ + +static int handle_get(int client_socket, const char* url, void *userdata) +{ + struct context **cnt=userdata; + if (*url == '/' ){ + int i=0; + char *res=NULL; + res = malloc(2048); + + /* get the number of threads */ + while (cnt[++i]); + /* ROOT_URI -> GET / */ + if (! (strcmp (url, "/")) ) { + int y=0; + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket,ini_template); + sprintf(res,"Motion "VERSION" Running [%d] Threads
    \n" + "All
    \n", i); + send_template(client_socket, res); + for (y=1; yThread %d
    \n", y, y); + send_template(client_socket, res); + } + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Motion "VERSION" Running [%d] Threads\n0\n",i); + send_template_raw(client_socket, res); + for (y=1; y GET /2 */ + pointer++; + length_uri--; + warningkill = sscanf (pointer, "%i%c", &thread, &slash); + + if ((thread != -1) && (thread < i)) { + /* thread_number found */ + if (slash == '/') { /* slash found /2/ */ + pointer = pointer + 2; + length_uri = length_uri - 2; + } else { + pointer++; /* /2 found */ + length_uri--; + } + + if (length_uri!=0) { + warningkill = sscanf (pointer, "%256[a-z]%c" , command , &slash); + + /* config */ + if (!strcmp(command,"config")) { + pointer = pointer + 6; + length_uri = length_uri - 6; + if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d
    \n" + "list
    \n" + "write
    \n" + "set
    \n" + "get
    \n" + "<- back\n", + thread, thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d\nlist\nwrite\nset\nget\n", thread); + send_template_raw(client_socket, res); + } + } else if ((slash == '/') && (length_uri >= 4)) { + /*call config() */ + pointer++; + length_uri--; + config(pointer, res, length_uri, thread, client_socket, cnt); + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + /* action */ + else if (!strcmp(command,"action")) { + pointer = pointer + 6; + length_uri = length_uri - 6; + /* call action() */ + if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d
    \n" + "makemovie
    \n" + "snapshot
    \n" + "restart
    \n" + "quit
    \n" + "<- back\n", + thread,thread,thread,thread,thread,thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d\nmakemovie\nsnapshot\nrestart\nquit\n", thread); + send_template_raw(client_socket, res); + } + } else if ((slash == '/') && (length_uri > 4)) { + int ret = 1; + pointer++; + length_uri--; + ret = action(pointer, res, length_uri, thread, client_socket, cnt); + free(res); + return ret; + + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command,NULL); + else + response_client(client_socket, not_found_response_valid_command_raw,NULL); + } + } + /* detection */ + else if (!strcmp(command,"detection")) { + pointer = pointer + 9; + length_uri = length_uri - 9; + if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d
    \n" + "status
    \n" + "start
    \n" + "pause
    \n" + "<- back\n", + thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d\nstatus\nstart\npause\n", thread); + send_template_raw(client_socket, res); + } + } else if ((slash == '/') && (length_uri > 5)) { + pointer++; + length_uri--; + /* call detection() */ + detection(pointer, res, length_uri, thread, client_socket, cnt); + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + /* track */ + else if (!strcmp(command,"track")) { + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res,"Thread %d
    \n" + "track set pan/tilt
    \n" + "track auto
    \n" + "track status
    \n" + "<- back\n", + thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d\nset pan/tilt\nauto\nstatus\n", thread); + send_template_raw(client_socket, res); + } + } + else if ((slash == '/') && (length_uri >= 4)) { + pointer++; + length_uri--; + /* call track() */ + if (cnt[thread]->track.type) { + track(pointer, res, length_uri, thread, client_socket, cnt); + } else { + /* error track not enable */ + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n",thread); + response_client(client_socket, not_track,res); + } + else + response_client(client_socket, not_track_raw,NULL); + } + } else { + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n",thread); + response_client(client_socket, not_found_response_valid_command, res); + } + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n",thread); + response_client(client_socket, not_found_response_valid_command, res); + } + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /* /thread_number/ requested */ + if (cnt[0]->conf.control_html_output) { + send_template_ini_client(client_socket,ini_template); + sprintf(res,"Thread %d
    \n" + "config
    \n" + "action
    \n" + "detection
    \n" + "track
    \n" + "<- back\n", + thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"Thread %d\nconfig\naction\ndetection\ntrack\n", thread); + send_template_raw(client_socket, res); + } + } + } else { + if (cnt[0]->conf.control_html_output) { + sprintf(res,"<- back\n"); + response_client(client_socket, not_found_response_valid, res); + } + else + response_client(client_socket, not_found_response_valid_raw, NULL); + } + } + free(res); + } else { + if (cnt[0]->conf.control_html_output) + response_client(client_socket, not_found_response_template,NULL); + else + response_client(client_socket, not_found_response_template_raw,NULL); + } + + return 1; +} + + +/* + -TODO- + As usually web clients uses nonblocking connect/read + read_client should handle nonblocking sockets. +*/ + +static int read_client(int client_socket, void *userdata, char *auth) +{ + int alive = 1; + int ret = 1; + char buffer[1024] = {'\0'}; + int length = 1024; + struct context **cnt = userdata; + + /* lock the mutex */ + pthread_mutex_lock(&httpd_mutex); + + while (alive) + { + int nread = 0, readb = -1; + + nread = read (client_socket, buffer, length); + + if (nread <= 0) { + motion_log(LOG_ERR, 1, "httpd First read"); + pthread_mutex_unlock(&httpd_mutex); + return -1; + } + else { + char method[sizeof (buffer)]; + char url[sizeof (buffer)]; + char protocol[sizeof (buffer)]; + char *authentication=NULL; + + buffer[nread] = '\0'; + + warningkill = sscanf (buffer, "%s %s %s", method, url, protocol); + + while ((strstr (buffer, "\r\n\r\n") == NULL) && (readb!=0) && (nread < length)){ + readb = read (client_socket, buffer+nread, sizeof (buffer) - nread); + + if (readb == -1){ + nread = -1; + break; + } + + nread +=readb; + + if (nread > length) { + motion_log(LOG_ERR, 1, "httpd End buffer reached waiting for buffer ending"); + break; + } + buffer[nread] = '\0'; + } + + /* Make sure the last read didn't fail. If it did, there's a + problem with the connection, so give up. */ + if (nread == -1) { + motion_log(LOG_ERR, 1, "httpd READ"); + pthread_mutex_unlock(&httpd_mutex); + return -1; + } + alive = 0; + + /* Check Protocol */ + if (strcmp (protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) { + /* We don't understand this protocol. Report a bad response. */ + if (cnt[0]->conf.control_html_output) + warningkill = write (client_socket, bad_request_response, sizeof (bad_request_response)); + else + warningkill = write (client_socket, bad_request_response_raw, sizeof (bad_request_response_raw)); + + pthread_mutex_unlock(&httpd_mutex); + return -1; + } + else if (strcmp (method, "GET")) { + /* This server only implements the GET method. If client + uses other method, report the failure. */ + char response[1024]; + if (cnt[0]->conf.control_html_output) + snprintf (response, sizeof (response),bad_method_response_template, method); + else + snprintf (response, sizeof (response),bad_method_response_template_raw, method); + warningkill = write (client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return -1; + } else if ( auth != NULL) { + if ( (authentication = strstr(buffer,"Basic")) ) { + char *end_auth = NULL; + authentication = authentication + 6; + + if ( (end_auth = strstr(authentication,"\r\n")) ) { + authentication[end_auth - authentication] = '\0'; + } else { + char response[1024]; + snprintf (response, sizeof (response),request_auth_response_template, method); + warningkill = write (client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return -1; + } + + if ( !check_authentication(auth, authentication, + strlen(cnt[0]->conf.control_authentication), + cnt[0]->conf.control_authentication)) { + char response[1024]={'\0'}; + snprintf(response, sizeof (response), request_auth_response_template, method); + warningkill = write (client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return -1; + } else { + ret = handle_get (client_socket, url, cnt); + /* A valid auth request. Process it. */ + } + } else { + // Resquest Authorithation + char response[1024]={'\0'}; + snprintf (response, sizeof (response),request_auth_response_template, method); + warningkill = write (client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return -1; + } + } else { + ret=handle_get (client_socket, url, cnt); + /* A valid request. Process it. */ + } + } + } + pthread_mutex_unlock(&httpd_mutex); + + return ret; +} + + +/* + acceptnonblocking + + This function waits timeout seconds for listen socket. + Returns : + -1 if the timeout expires or on accept error. + curfd (client socket) on accept success. +*/ + +static int acceptnonblocking(int serverfd, int timeout) +{ + int curfd; + socklen_t namelen = sizeof(struct sockaddr_in); + struct sockaddr_in client; + struct timeval tm; + fd_set fds; + + tm.tv_sec = timeout; /* Timeout in seconds */ + tm.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(serverfd,&fds); + + if( select (serverfd + 1, &fds, NULL, NULL, &tm) > 0){ + if(FD_ISSET(serverfd, &fds)) { + if((curfd = accept(serverfd, (struct sockaddr*)&client, &namelen))>0) + return(curfd); + } + } + + return -1; +} + + +/* + Main function: Create the listening socket and waits client requests. +*/ + +void httpd_run(struct context **cnt) +{ + int sd, client_socket_fd; + int client_sent_quit_message = 1, val=1; + int closehttpd = 0; + struct sockaddr_in servAddr; + struct sigaction act; + char *authentication = NULL; + + /* Initialize the mutex */ + pthread_mutex_init(&httpd_mutex, NULL); + + + /* set signal handlers TO IGNORE */ + memset(&act,0,sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE,&act,NULL); + sigaction(SIGCHLD,&act,NULL); + + /* create socket */ + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd<0) { + motion_log(LOG_ERR, 1, "httpd socket"); + return; + } + + /* bind server port */ + servAddr.sin_family = AF_INET; + if (cnt[0]->conf.control_localhost) + servAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + else + servAddr.sin_addr.s_addr = htonl(INADDR_ANY); + servAddr.sin_port = htons(cnt[0]->conf.control_port); + + /* Reuse Address */ + + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof( int ) ) ; + + if (bind(sd, (struct sockaddr *) &servAddr, sizeof(servAddr))<0) { + motion_log(LOG_ERR, 1, "httpd bind()"); + close(sd); + return; + } + + if (listen(sd,5) == -1){ + motion_log(LOG_ERR, 1, "httpd listen()"); + close(sd); + return; + } + + motion_log(LOG_DEBUG, 0, "motion-httpd/"VERSION" running, accepting connections"); + motion_log(LOG_DEBUG, 0, "motion-httpd: waiting for data on port TCP %d", cnt[0]->conf.control_port); + + if (cnt[0]->conf.control_authentication != NULL ) { + char *userpass = NULL; + size_t auth_size = strlen(cnt[0]->conf.control_authentication); + + authentication = (char *) mymalloc(BASE64_LENGTH(auth_size) + 1); + userpass = mymalloc(auth_size + 4); + /* base64_encode can read 3 bytes after the end of the string, initialize it */ + memset(userpass, 0, auth_size + 4); + strcpy(userpass, cnt[0]->conf.control_authentication); + base64_encode(userpass, authentication, auth_size); + free(userpass); + } + + while ((client_sent_quit_message!=0) && (!closehttpd)) { + + client_socket_fd = acceptnonblocking(sd, 1); + + if (client_socket_fd<0) { + if (cnt[0]->finish){ + motion_log(-1, 1, "httpd - Finishing"); + closehttpd = 1; + } + } else { + /* Get the Client request */ + client_sent_quit_message = read_client (client_socket_fd, cnt, authentication); + motion_log(-1, 1, "httpd - Read from client"); + + /* Close Connection */ + if (client_socket_fd) + close(client_socket_fd); + } + + } + + if (authentication != NULL) free(authentication); + close(sd); + motion_log(LOG_DEBUG, 0, "httpd Closing"); + pthread_mutex_destroy(&httpd_mutex); +} + +void *motion_web_control(void *arg) +{ + struct context **cnt=arg; + httpd_run(cnt); + pthread_exit(NULL); +} diff --git a/webhttpd.h b/webhttpd.h new file mode 100644 index 0000000..a8fa23f --- /dev/null +++ b/webhttpd.h @@ -0,0 +1,19 @@ +/* + * webhttpd.h + * + * Include file for webhttpd.c + * + * Specs : http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpAPI + * + * Copyright 2004-2005 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU Public License Version 2 + * See also the file 'COPYING'. + * + */ +#ifndef _INCLUDE_WEBHTTPD_H_ +#define _INCLUDE_WEBHTTPD_H_ + +void * motion_web_control(void *arg); +void httpd_run(struct context **); + +#endif